布景

flutter依托Skia的深度定制,给咱们供给了很多关于烘托的支撑。能够完成跨渠道运用层的烘托的一致性。可是咱们在实践的运用开发中,除了根本的UI展现,还有更多的功用逻辑需求运用体系的底层的才能,比方:推送、拍照等功用。由于flutter只接管运用烘托层,因而这些体系的底层才能无法在flutter结构内供给支撑。另一方面flutter问世的是时刻相对较短,一些比较成熟的处理计划在flutter中还没有相关的完成计划,所以和原生交互才能是至关重要的。

运用开发

依据布景部分的考虑,咱们清晰了与原生交互能处理flutter调用原生才能的需求场景,那底层才能 + 运用层烘托,就能够搞定一个App的所有场景吗?在下结论之前,咱们先依照四象限剖析法,把才能和烘托分解成四个维度,剖析构建一个相对完整的 App 需求什么

flutter与native的交互详解

如图中所示,开发一个 App 需求掩盖的知识点其实是非常多的,flutter和原生交互只能搞定运用层烘托、运用层才能和底层才能,关于那些触及到底层烘托,比方浏览器、相机、地图,以及原生自界说视图的场景,咱们重新在flutter 上重新开发一套显然不太现实。在这种情况下,运用混合视图的场景需求就清楚明了。咱们能够在 flutter 的 Widget 树中提前预留一块空白区域,在 flutter 的画板中(即 FlutterView 与 FlutterViewController)嵌入一个与空白区域彻底匹配的原生视图,就能够完成想要的视觉效果了。可是,采用这种计划极其不优雅,由于嵌入的原生视图并不在 flutter 的烘托层级中,需求一起在 flutter 侧与原生侧做很多的适配工作,才能完成正常的用户交互体会。那么针对这些问题,咱们接下来看看flutter给开发者都供给了哪些支撑。

相关支撑

  • Method Channel 为了处理调用原生体系底层才能以及相关代码库复用问题,flutter供给了一个轻量级的处理计划,即逻辑层的办法通道(Method Channel)机制。依据办法通道,咱们能够将原生代码所具有的才能,以接口形式暴露给 Dart,然后完成 Dart 代码与原生代码的交互,就像调用了一个一般的 Dart API 相同。

  • Platform View flutter 供给了一个渠道视图(Platform View)的概念。它供给了一种办法,答应开发者在 flutter 里面嵌入原生体系(Android 和 iOS)的视图,并加入到 flutter 的烘托树中,完成与 flutter 一致的交互体会。这样一来,经过渠道视图,咱们就能够将一个原生控件包装成 flutter 控件,嵌入到 flutter 页面中,就像运用一个一般的 Widget 相同。

下面咱们详细探究一下flutter的这两个处理计划,两个计划的完成原理,计划是否如上所述能处理咱们实践开发中的问题。

Method Channel 办法通道

flutter 作为一个跨渠道结构,供给了一套标准化的处理计划,为开发者屏蔽了操作体系的差异。但,flutter 究竟不是操作体系,因而在某些特定场景下(比方推送、蓝牙、摄像头硬件调用时),也需求具有直接拜访体系底层原生代码的才能。为此,flutter 供给了一套灵敏而轻量级的机制来完成 Dart 和原生代码之间的通讯,即办法调用的音讯传递机制,而办法通道则是用来传递通讯音讯的信道。 一次典型的办法调用进程相似网络调用,由作为客户端的 flutter,经过办法通道向作为服务端的原生代码宿主发送办法调用恳求,原生代码宿主在监听到办法调用的音讯后,调用渠道相关的 API 来处理 flutter 建议的恳求,最终将处理完毕的成果经过办法通道回发至 flutter。调用进程如下图所示:

flutter与native的交互详解

从上图中能够看到,办法调用恳求的处理和呼应,在 Android 中是经过 FlutterView,而在 iOS 中则是经过 FlutterViewController 进行注册的。FlutterView 与 FlutterViewController 为 Flutter 运用供给了一个画板,使得构建于 Skia 之上的 Flutter 经过绘制即可完成整个运用所需的视觉效果。因而,它们不仅是 flutter 运用的容器,一起也是 flutter 运用的入口,天然也是注册办法调用恳求最合适的当地。接下来,我经过一个例子来演示怎么运用办法通道完成与原生代码的交互。

办法通道运用示例

在实践业务中,提示用户跳转到运用商场(iOS 为 App Store、Android 则为各类手机运用商场)去评分是一个高频需求,考虑到 flutter 并未供给这样的接口,而跳转办法在 Android 和 iOS 上各不相同,因而咱们需求分别在 Android 和 iOS 上完成这样的功用,并暴露给 Dart 相关的接口。咱们先来看看作为客户端的 flutter,怎样完成一次办法调用恳求。

flutter 怎么完成一次办法调用恳求?

首要,咱们需求确认一个仅有的字符串标识符,确认一个命名通道;在这个通道之上,flutter 经过指定办法名“fcredirect://personal_friends_takeover”来建议一次办法调用恳求。能够看到,这和咱们平时调用一个 Dart 目标的办法彻底相同。由于办法调用进程是异步的,所以咱们需求运用非阻塞(或许注册回调)来等待原生代码给予呼应。 下面展现一下时刻一个简单的分享功用的flutter和原生交互的部分代码

// 声明MethodChannel
const methodChannel = const MethodChannel('flutter_native');
_iOSPushToFriend() async {
   await methodChannel.invokeMethod('redirect://personal_friends_takeover');
   // 需求留意的是,办法调用恳求有可能会失利,实践开发中咱们需求把建议办法调用恳求的句子用 try-catch 包装起来。
}

在 iOS 渠道,办法调用的处理和呼应是在 flutter 运用的入口,即 FlutterViewController里完成的,咱们能够在宿主App中做如下的完成:

func flutterMethodChannel(viewController: FlutterViewController) {
    let channelName = "flutter_native"
    let methodChannel = FlutterMethodChannel.init(name: channelName, binaryMessenger: viewController.binaryMessenger)
    methodChannel.setMethodCallHandler {(call: FlutterMethodCall, result: @escaping FlutterResult) in
        if (call.method == "redirect://personal_friends_takeover") {
            print("进行交互")
        } else {
            print("nothing")
        }
    }
}

需求留意的是,触及到跨体系数据交互,flutter 会运用 StandardMessageCodec 对通道中传输的信息进行相似 JSON 的二进制序列化,以标准化数据传输行为。这样在咱们发送或许接收数据时,这些数据就会依据各自体系预订的规矩主动进行序列化和反序列化。关于 Android、iOS 和 Dart 渠道间的常见数据类型转化,总结成了下面一张表格,帮助你理解与回忆。你只要记住,像 null、布尔、整型、字符串、数组和字典这些根本类型,是能够在各个渠道之间以渠道界说的规矩去混用的,就能够了。

flutter与native的交互详解

Platform View(渠道视图)

如果说办法通道处理的是原生才能逻辑复用问题,那么渠道视图处理的便是原生视图复用问题。flutter 供给了一种轻量级的办法,让咱们能够创立原生(Android 和 iOS)的视图,经过一些简单的 Dart 层接口封装之后,就能够将它刺进 Widget 树中,完成原生视图与 flutter 视图的混用。一次典型的渠道视图运用进程与办法通道相似:

  • 首要,由作为客户端的 flutter,经过向原生视图的 flutter 封装类(在 iOS 和 Android 渠道分别是 UIKitView 和 AndroidView)传入视图标识符,用于建议原生视图的创立恳求;
  • 然后,原生代码侧将对应原生视图的创立交给渠道视图工厂(PlatformViewFactory)完成;
  • 最终,在原生代码侧将视图标识符与渠道视图工厂进行相关注册,让 flutter 建议的视图创立恳求能够直接找到对应的视图创立工厂。 至此,咱们就能够像运用 Widget 那样,运用原生视图了。整个流程,如下图所示:

flutter与native的交互详解

flutter 怎么完成原生视图的接口调用?

在下面的代码中,咱们在 SampleView 的内部,分别运用了原生 Android、iOS 视图的封装类 AndroidView 和 UIkitView,并传入了一个仅有标识符,用于和原生视图树立相关:

class SampleView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //运用Android渠道的AndroidView,传入仅有标识符sampleView
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(viewType: 'sampleView');
    } else {
      //运用iOS渠道的UIKitView,传入仅有标识符sampleView
      return UiKitView(viewType: 'sampleView');
    }
  }
}

能够看到,渠道视图在 flutter 侧的运用办法比较简单,与一般 Widget 并无明显差异。 渠道视图处理了原生烘托才能的复用问题,使得 flutter 能够经过轻量级的代码封装,把原生视图组装成一个 flutter 控件。flutter 供给了渠道视图工厂和视图标识符两个概念,因而 Dart 层建议的视图创立恳求能够经过标识符直接找到对应的视图创立工厂,然后完成原生视图与 flutter 视图的交融复用。关于需求在运行期动态调用原生视图接口的需求,咱们能够在原生视图的封装类中注册办法通道,完成准确控制原生视图展现的效果。

总结

办法通道

办法通道处理了逻辑层的原生才能复用问题,使得 flutter 能够经过轻量级的异步办法调用,完成与原生代码的交互。一次典型的调用进程由 flutter 建议办法调用恳求开端,恳求经由仅有标识符指定的办法通道抵达原生代码宿主,而原生代码宿主则经过注册对应办法完成、呼应并处理调用恳求,最终将执行成果经过音讯通道,回传至 flutter。

渠道视图

由于 flutter 与原生烘托办法彻底不同,因而转化不同的烘托数据会有较大的性能开销。如果在一个界面上一起实例化多个原生控件,就会对性能造成非常大的影响,所以咱们要防止在运用 flutter 控件也能完成的情况下去运用内嵌渠道视图。由于这样做,一方面需求分别在 Android 和 iOS 端写很多的适配桥接代码,违反了跨渠道技术的本意,也增加了后续的保护成本;另一方面究竟除去地图、WebView、相机等触及底层计划的特殊情况外,大部分原生代码能够完成的 UI 效果,彻底能够用 flutter 完成。