首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Flutter 如何混编原生功能

1. 前言

依托于与 Skia 的深度定制及优化,Flutter 给我们提供了很多关于渲染的控制和支持,能够实现绝对的跨平台应用层渲染一致性。但对于一个应用而言,除了应用层视觉显示和对应的交互逻辑处理之外,有时还需要原生操作系统(Android、iOS)提供的底层能力支持。比如,我们前面提到的数据持久化,以及推送、摄像头硬件调用等。

由于 Flutter 只接管了应用渲染层,因此这些系统底层能力是无法在 Flutter 框架内提供支持的;而另一方面,Flutter 还是一个相对年轻的生态,因此原生开发中一些相对成熟的 Java、C++ 或 Objective-C 代码库,比如图片处理、音视频编解码等,可能在 Flutter 中还没有相关实现。

Flutter 项目中添加原生功能主要可以从两个方面考虑

Flutter 和原生平台的通信

Flutter 页面中嵌入原生页面

2. Flutter 和原生平台的通信

了解决调用原生系统底层能力以及相关代码库复用问题,Flutter 为开发者提供了一个轻量级的解决方案,即逻辑层的方法通道(Method Channel)机制。基于方法通道,我们可以将原生代码所拥有的能力,以接口形式暴露给 Dart,从而实现 Dart 代码与原生代码的交互,就像调用了一个普通的 Dart API 一样。

当在Flutter中调用原生方法时,调用信息通过平台通道传递到原生,原生收到调用信息后方可执行指定的操作,如需返回数据,则原生会将数据再通过平台通道传递给Flutter。值得注意的是消息传递是异步的,这确保了用户界面在消息传递时不会被挂起。

▐2.1平台通信的 3 中方式

Flutter 与 Native 端通信有如下3个方法:

MethodChannel:Flutter 与 Native 端相互调用,调用后可以返回结果,可以 Native 端主动调用,也可以 Flutter 主动调用,属于双向通信。此方式为最常用的方式, Native 端调用需要在主线程中执行。

BasicMessageChannel:用于使用指定的编解码器对消息进行编码和解码,属于双向通信,可以 Native 端主动调用,也可以Flutter主动调用。

EventChannel:用于数据流(event streams)的通信, Native 端主动发送数据

▐2.2Android、iOS 和 Dart 平台间的常见数据类型转换

平台通道使用标准消息编/解码器对消息进行编解码,它可以高效的对消息进行二进制序列化与反序列化。由于 Dart 与原生平台之间数据类型有所差异,下面我们列出数据类型之间的映射关系。

当在发送和接收值时,这些值在消息中的序列化和反序列化会自动进行。

▐2.3如何获取平台信息

Flutter 中提供了一个全局变量defaultTargetPlatform 来获取当前应用的平台信息,defaultTargetPlatform定义在platform.dart中,它的类型是TargetPlatform,这是一个枚举类,定义如下:

可以看到目前 Flutter 只支持这三个平台。我们可以通过如下代码判断平台

▐2.3使用示例

加入我们Flutter要向原生传递一个字典{"flutter":"我是flutter"},原生向 Flutter 传递一个数组[1,2,3]

2.3.1 Flutter如何实现一次方法调用请求

首先,我们需要确定一个唯一的字符串标识符,来构造一个命名通道;然后,在这个通道之上,Flutter 通过指定方法名flutter_postData来发起一次方法调用请求。

可以看到,这和我们平时调用一个 Dart 对象的方法完全一样。因为方法调用过程是异步的,所以我们需要使用非阻塞(或者注册回调)来等待原生代码给予响应。

2.3.2 iOS端的方法调用响应如何实现

首先打开Xcode中Flutter应用程序的iOS部分:

在 iOS 平台,方法调用的处理和响应是在 Flutter 应用的入口,也就是在 Applegate 中的 rootViewController(即 FlutterViewController)里实现的,因此我们需要打开 Flutter 的 iOS 宿主 App,找到 AppDelegate.m 文件,并添加相关逻辑。

点击按钮打印

2.3.3 android 端的方法调用响应如何实现

首先在 Android Studio 中打开您的 Flutter 应用的 Android 部分:

在 Android 平台,方法调用的处理和响应是在 Flutter 应用的入口,也就是在 MainActivity 中的 FlutterView 里实现的,因此我们需要打开 Flutter 的 Android 宿主 App,找到 MainActivity.java 文件,并在其中添加相关的逻辑。

接下来,在 onCreate 里创建 MethodChannel 并设置一个 MethodCallHandler。确保使用和 Flutter 客户端中使用的通道名称相同的名称。

2.3.4 总结

Flutter 发起方法调用请求开始,请求经由唯一标识符指定的方法通道到达原生代码宿主,而原生代码宿主则通过注册对应方法实现、响应并处理调用请求,最后将执行结果通过消息通道,回传至 Flutter。

需要注意的是,方法通道是非线程安全的。这意味着原生代码与 Flutter 之间所有接口调用必须发生在主线程。Flutter 是单线程模型,因此自然可以确保方法调用请求是发生在主线程(Isolate)的;而原生代码在处理方法调用请求时,如果涉及到异步或非主线程切换,需要确保回调过程是在原生系统的 UI 线程(也就是 Android 和 iOS 的主线程)中执行的,否则应用可能会出现奇怪的 Bug,甚至是 Crash。

3. Flutter视图中嵌套原生视图

我们来分析一下构建一个复杂 App 都需要什么?我们先按照四象限分析法,把能力和渲染分解成四个维度,分析构建一个复杂 App 都需要什么。

经过分析,我们终于发现,原来构建一个 App 需要覆盖那么多的知识点,通过 Flutter 和方法通道只能搞定应用层渲染、应用层能力和底层能力,对于那些涉及到底层渲染,比如浏览器、相机、地图,以及原生自定义视图的场景,自己在 Flutter 上重新开发一套显然不太现实。

在这种情况下,使用混合视图看起来是一个不错的选择。我们可以在 Flutter 的 Widget 树中提前预留一块空白区域,在 Flutter 的画板中(即 FlutterView 与 FlutterViewController)嵌入一个与空白区域完全匹配的原生视图,就可以实现想要的视觉效果了。

但是,采用这种方案极其不优雅,因为嵌入的原生视图并不在 Flutter 的渲染层级中,需要同时在 Flutter 侧与原生侧做大量的适配工作,才能实现正常的用户交互体验。

幸运的是,Flutter 提供了一个平台视图(Platform View)的概念。它提供了一种方法,允许开发者在 Flutter 里面嵌入原生系统(Android 和 iOS)的视图,并加入到 Flutter 的渲染树中,实现与 Flutter 一致的交互体验。

这样一来,通过平台视图,我们就可以将一个原生控件包装成 Flutter 控件,嵌入到 Flutter 页面中,就像使用一个普通的 Widget 一样

使用方法

首先,由作为客户端的 Flutter,通过向原生视图的 Flutter 封装类(在 iOS 和 Android 平台分别是 UIKitView 和 AndroidView)传入视图标识符,用于发起原生视图的创建请求;

然后,原生代码侧将对应原生视图的创建交给平台视图工厂(PlatformViewFactory)实现;

最后,在原生代码侧将视图标识符与平台视图工厂进行关联注册,让 Flutter 发起的视图创建请求可以直接找到对应的视图创建工厂。

▐3.1Flutter 如何实现原生视图的接口调用

▐3.2嵌入原生 View-iOS

1、创建 FlutterPlatformView

2、注册工厂类MyFlutterViewFactory

3、在 AppDelegate 中注册

▐3.3嵌入原生View-Android

1、在 App 项目的 java/ 包名 目录下创建嵌入 Flutter 中的 Android View,此 View 继承 PlatformView

2、注册工厂类MyFlutterViewFactory

3、在 App 中 MainActivity 中注册

▐3.3总结

由于 Flutter 与原生渲染方式完全不同,因此转换不同的渲染数据会有较大的性能开销。如果在一个界面上同时实例化多个原生控件,就会对性能造成非常大的影响,所以我们要避免在使用 Flutter 控件也能实现的情况下去使用内嵌平台视图。

因为这样做,一方面需要分别在 Android 和 iOS 端写大量的适配桥接代码,违背了跨平台技术的本意,也增加了后续的维护成本;另一方面毕竟除去地图、WebView、相机等涉及底层方案的特殊情况外,大部分原生代码能够实现的 UI 效果,完全可以用 Flutter 实现。

如果觉得不错,素质三连、或者点个「赞」「在看」都是对笔者莫大的支持,谢谢各位大佬啦~

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210305A0DAYF00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券