本文基于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基建系列文章
- Flutter基建 – Dart基础类型
- Flutter基建 – Dart方法和类
- Flutter基建 – 文本组件
- Flutter基建 – 按钮全解析
- Flutter基建 – 布局组件全面解析
- Flutter基建 – 离不开的列表组件
- Flutter基建 – 形影不离的State**Widget
- Flutter基建 – 异步加载UI
- Flutter基建 – 12种隐式动画小组件全解析
- Flutter基建 – Hero动画有多英雄
- Flutter基建 – 状态管理State
- Flutter基建 – ChangeNotifier详解
- Flutter基建 – 利用插件和原生Android、iOS交互
使用MethodChannel获取消息
MethodChannel主要作用就是Flutter层向原生层面获取消息,原生层属于一个被动的状态,当收到Flutter层传递获取的具体方法时,可以根据具体的方法和参数向Flutter层传递对应的消息。下面我们介绍MethodChannel的用法过程中,以获取Android和iOS系统版本为例,来直观的感受下MethodChannel的具体逻辑。
Flutter层定义MethodChannel
首先我们在Flutter层面定义好MethodChannel对象,它需要接收一个name参数,这个参数还是挺重要的,Flutter层和原生层就是通过name来匹配对应的通道。
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为主,它可以帮助我们处理管道对应的方法,然后进行消息传递。
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
直接上完整代码,后面我们跟着代码一起了解下。
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端持续发送消息
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,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢😆😆~
原文链接:https://juejin.cn/post/7350950545697357861 作者:Taonce