Flutter基建 - 利用插件和原生Android、iOS交互

本文基于Flutter 3.19.3版本进行开发

Flutter 3.19.3 • channel stable • github.com/flutter/flu...

Framework • revision ba39319843 (8 days ago) • 2024-03-07 15:22:21 -0600

Engine • revision 2e4ba9c6fb

Tools • Dart 3.3.1 • DevTools 2.31.1

本篇为Flutter基建的第十三篇文章💪🏻💪🏻💪🏻,本篇文章主要介绍在Flutter中如何通过插件的形式进行Android和iOS原生的交互操作,具体会分为两部分,其中一部分Flutter使用MethodChannel从原生端中获取信息,另一部分则是原生端通过EventChannel向Flutter端主动发送消息,下面让我们一起进入文章感受下吧~

Flutter基建系列文章

使用MethodChannel获取消息

MethodChannel主要作用就是Flutter层向原生层面获取消息,原生层属于一个被动的状态,当收到Flutter层传递获取的具体方法时,可以根据具体的方法和参数向Flutter层传递对应的消息。下面我们介绍MethodChannel的用法过程中,以获取Android和iOS系统版本为例,来直观的感受下MethodChannel的具体逻辑。

Flutter层定义MethodChannel

首先我们在Flutter层面定义好MethodChannel对象,它需要接收一个name参数,这个参数还是挺重要的,Flutter层和原生层就是通过name来匹配对应的通道。

dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  final methodChannel = const MethodChannel("version_channel");

  String _version = "";

  @override
  void initState() {
    super.initState();
    getVersion();
  }

  Future<void> getVersion() async {
    String version = await methodChannel.invokeMethod("getVersion");
    setState(() {
      _version = version;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Version Info: $_version',
            ),
          ],
        ),
      ),
    );
  }
}

上述代码在第二行我们进行了MethodChannel对象的定义,然后在initState()方法中调用了一个getVersion()方法,它内部就是通过MethodChannel来连接原生层,通过对应的方法名来获取需要的信息,这里需要注意的一点就是methodChannel.invokeMethod()方法是一个异步函数,大家在调用的时候稍微注意一下,加上await等待即可。

methodChannel.invokeMethod()方法除了传入方法名以外,还可以通过可选参数arguments来传入指定的参数,然后在原生层获取即可。

Flutter层定义好管道和调用的方法之后,我们就可以在Android或者iOS原生层进行方法的具体实现了,接着我们以Android端为例(因为手头没有ios设备.....)

Android端实现消息传递

无论是Android端还是iOS端,也还是以MethodChannel为主,它可以帮助我们处理管道对应的方法,然后进行消息传递。

kotlin 复制代码
class MainActivity : FlutterActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        flutterEngine?.dartExecutor?.let {
            MethodChannel(it, "version_channel").setMethodCallHandler { call, result ->
                if (call.method == "getVersion") {
                    result.success("Android ${Build.VERSION.RELEASE}")
                }
            }
        }
    }
}

整体上来看,Android端的代码还是比较简单的,通过创建一个MethodChannel对象,内部需要传入BinaryMessenger和name,这里的BinaryMessenger可以通过flutterEngine获取到,然后name参数必须和Flutter层保持一致,不然会提示找不到对应的MethodChannel。

定义好MethodChannel对象之后,直接调用它的setMethodCallHandler()方法即可,在回调中可以通过call获取通过对应的方法名和参数等信息,具体如下:

回调的result可以帮助我们将对应的消息传递给Flutter层,可使用result.success()来传递正常的消息,也可以通过result.error()来通知Flutter层消息传递失败。

这里注意一下, 在写完Android端代码之后,记得保存一下,不然运行会不生效,不知道小伙伴会不会出现同样的问题,欢迎沟通交流~

最后我们来运行看下效果:

使用EventChannel获取消息

介绍完MethodChannel用法之后,我们再来看下EventChannel的具体使用逻辑,它和MethodChannel最大的区别就是原生层在其中属于一个主动的角色,它可以主动向Flutter传递一些需要的消息,当Flutter开始接收时,原生层就可以根据通道名来向此通道持续的发送消息,下面我们以定时器为例,实现一个Android端向Flutter层发送计时的一个小例子。

Flutter层定义EventChannel

直接上完整代码,后面我们跟着代码一起了解下。

dart 复制代码
class _MyHomePageState extends State<MyHomePage> {
  final eventChannel = const EventChannel("count_channel");
  StreamSubscription? _streamSubscription;

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Count: $count'),
            TextButton(
              onPressed: () {
                registerEventChannel();
              },
              child: const Text("注册EventChannel")),
            TextButton(
              onPressed: () {
                unRegisterEventChannel();
              },
              child: const Text("解注册EventChannel")),
          ],
        ),
      ),
    );
  }

  void registerEventChannel() {
    _streamSubscription = eventChannel.receiveBroadcastStream().listen((event) {
      setState(() {
        count = event;
      });
    });
  }

  void unRegisterEventChannel() {
    if (_streamSubscription != null) {
      _streamSubscription!.cancel();
      _streamSubscription = null;
    }
  }

  @override
  void dispose() {
    super.dispose();
    unRegisterEventChannel();
  }
}

我们先从第二行看,在这里定义好了一个EventChannel对象,然后传入name参数,此name参数和MethodChannel作用一直,原生端要与其保持一致;

然后在下面第三行定义了一个StreamSubscription对象,它是EventChannel执行listen监听后产生的对象,可取消和暂停监听操作;

然后在Widget中分别定义Text用于显示原生端传递过来的信息,两个TextButton分别用于执行注册监听和取消监听;

在第34行的registerEventChannel()方法中,调用EventChannel.receiveBroadcastStream().listen()方法进行通过的监听,listen()方法中一共有四个参数:

  • onData()用于处理通道中传递过来的数据,这里我们直接将传递过来的count显示在界面上;
  • onError()用于处理通道中发生异常时场景;
  • onDone()方法则是表示通道被取消监听或者原生端传递了done事件;
  • cancelOnError这个参数如果被赋值true之后,那么通道在发生异常时则会自动取消监听。

接着在第42行unRegisterEventChannel()方法中我们进行了通道的取消监听操作。

接下来我们再来看看Android端是如何持续发送数据的:

Android端持续发送消息

kotlin 复制代码
class MainActivity : FlutterActivity() {
    var count = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        flutterEngine?.dartExecutor?.let {
            EventChannel(it, "count_channel").setStreamHandler(object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    thread {
                        while (count < 20) {
                            runOnUiThread { events?.success(count++) }
                            Thread.sleep(1000L)
                        }
                    }
                }

                override fun onCancel(arguments: Any?) {
                    Log.d("taonce", "EventChannel onCancel")
                    count = 0
                }
            })
        }
    }
}

其实在EventChannel的使用场景下,Android端的代码就稍微要简单一点了,只需要通过EventChannel对象的setStreamHandler()方法进行数据的传递即可,此方法需要传递一个EventChannel.StreamHandler回调,回调内部包含了onListen()onCancel()方法,传递需要可以在onListen()方法中处理,onCancel()方法则是表示Flutter端进行了取消操作。

这里不得不提一下,在原生端发送数据时线程问题,这里需要在主线程中进行 events.success() 操作,否则就会产生Crash问题~

最后我们来运行下程序看看具体效果:

当我们在第11s时点击解注册按钮之后,Android端就会收到onCancel回调。

写在最后

本篇文章主要介绍了Flutter中MethodChannel和EventChannel的基本使用,在Flutter的日常开发中,这两个知识点在使用频率和重要性上都是等级比较高的,希望通过文章给阅读的小伙伴们带来一点帮助,后续会循序渐进逐步接触Flutter更多的知识。

我是Taonce,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢😆😆~

相关推荐
Justinc.16 分钟前
CSS3新增边框属性(五)
前端·css·css3
Mr Lee_26 分钟前
android 配置鼠标右键快捷对apk进行反编译
android
neter.asia32 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
~甲壳虫33 分钟前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
嚣张农民39 分钟前
JavaScript中Promise分别有哪些函数?
前端·javascript·面试
光影少年1 小时前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_1 小时前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu10830189111 小时前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾1 小时前
前端基础-html-注册界面
前端·算法·html
Dragon Wu1 小时前
前端 Canvas 绘画 总结
前端