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,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢😆😆~

相关推荐
腾讯TNTWeb前端团队5 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪8 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪8 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom10 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom10 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom10 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom10 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试