Flutter中显示原生系统的视图控件
Flutter、Golang、Python、编译原理、算法、Chrome原理学习系列文章抢先看请关注【码农帮派】:
Flutter接管了应用渲染层,方法通道可以获得原生底层能力,对于需要底层渲染的视图,比如浏览器、相机、地图以及一些原生自定义视图,我们自己在Flutter上再实现一遍,需要花费大量的精力。
为了复用原生系统已有的视图,我们可以采用混合视图的方式,我们在Flutter的Widget树中提前预留一块空白区域,在Flutter画板(即FlutterView/FlutterViewController)中嵌入一个与空白区域完全匹配的原生视图。但这种方案,由于嵌入的视图不在Flutter的渲染层级中,需要Flutter和原生宿主App上做大量的适配工作。
为了解决在Flutter上复用原生视图的问题,Flutter提供了平台视图(Platform View)。通过平台视图,我们可以将原生控件包装成Flutter控件,从而加入到Flutter的渲染树中,获得与Flutter控件一致的用户体验。
方法通道解决的是Flutter与原生系统之间的逻辑通信问题,平台视图解决的是Flutter与原生系统之间的视图复用问题。
平台视图
为了解决Flutter层复用原生控件的问题,Flutter提供了平台视图(Platform View)让我们可以通过简单的Dart接口,将Android/iOS平台的原生视图插入到Flutter的渲染树中,实现Flutter视图和原生视图的混用。
一次完整的平台视图调用的流程(平台视图的使用过程和方法通道使用过程大致相似,也和一次网络请求类似):
-
首先,Flutter通过向原生视图的Flutter封装类(Android上是AndroidView、iOS上是UIKitView)传入视图标志符,用于发起创建原生视图的请求;
-
然后,原生系统接收到请求,创建原生视图,交给平台视图工厂类(PlatformViewFactory)实现;
-
最后,原生系统将视图标识符和平台视图工厂类进行关联,让Flutter发起的原生视图创建请求可以直接找到对应的视图创建工厂。
Flutter实现平台视图调用接口
在Flutter中,针对不同的平台使用对应的视图封装类(Android使用AndroidView、iOS使用UIKitView),发送创建对应平台的原生视图,并传入视图的唯一标识符,用来建立和原生视图的关联:
上面的是Flutter端调用原生系统控件的代码,可以看出,Flutter端使用原生系统控件与普通的Flutter控件无明显差异,只是需要根据当前的平台使用AndroidView和UIKitView进行原生控件的调用。
Flutter端的代码实现了,剩下的就是Android、iOS端的控件实现了。在Flutter引用的原生宿主入口处,我们需要注册一个监听接口,用来监听来自Flutter的平台视图创建请求,当接收到请求之后,按照viewType(AndroidView/UIKitView中传递的viewType参数),来决定调用哪个视图工厂类,被调用的视图工厂类在其create方法中返回一个原生视图的封装类类,在该原生视图封装类中生成原生视图。Flutter在获得这个原生视图之后,已经是在Flutter层面的控件,就可以像普通控件一样加入到Flutter渲染树中。
Android端实现平台视图
首先是视图工厂类SampleViewFactory.java,在视图工厂类的create方法中返回一个原生视图封装类:
之后是原生视图封装类SimpleViewController.java,在这个类中我们创建并返回对应的原生视图:
在上面代码的第8行,可以初始化生成你需要的Android原生视图,比如地图控件等。
最后,我们需要绑定监听来自Flutter端的调用平台视图的请求,和方法通道一样,我们仍然是在原生宿主的Flutter入口处(MainActivity.java)进行注册:
这样Android端就可以监听到来自Flutter端的平台视图调用请求,并通过视图工厂/视图封装类生成原生视图,并将其返回给Flutter端,以Flutter控件的方式加入到Flutter层的渲染树进行渲染。
iOS端实现平台视图
首先,是平台视图封装类SampleViewController.m:
然后是平台视图工厂类SampleViewFactory.m:
最后需要在原生宿主的Flutter入口处进行接口注册(AppDelegate.m):
【注意】
在iOS平台上,Flutter内嵌UIKitView还处于技术预览的状态,因此我们若要在iOS平台上使用平台视图,还需要在Info.plist文件中开启:
<dic>
...
<key>io.flutter.embedded_views_preview</key>
<true/>
</dic>
之后我们就可以在Flutter中使用平台视图了:
Scaffold(
backgroundColor: Colors.yellow,
body: Container(
width: 200,
height: 200,
child: SampleView(),
),
);
代码实现效果:
上面的代码实现了在Flutter端,通过平台视图显示原生系统的视图控件,如何实现Flutter和平台视图的交互呢,这就要使用方法通道。
我们知道Android/iOS的视图控件状态的改变是命令式的,所以我们可以在原生视图封装类中将修改视图实例的接口,以方法通道的方式暴露给Flutter,从而让Flutter可以修改原生视图的状态。
下面的例子中在Flutter端创建了一个原生视图控制器,通过方法通道,实现与平台视图的交互:
然后声明Flutter端的StatefulWidget控件:
Android端的代码实现:
iOS端代码的实现:
之后我们就可以在Flutter中实现平台视图的通信了:
class DefaultState extends State<DefaultPage> {
NativeViewController _controller;
@override
void initState() {
// 初始化原生 View 控制器
_controller = NativeViewController();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// ...
// 内嵌原生 View
body: Container(
width: 200,
height:200,
child: SampleView(controller: _controller)
),
// 设置点击行为:改变视图颜色
floatingActionButton: FloatingActionButton(onPressed: () {
_controller.changeBackgroundColor();
}),
);
}
}
上一篇: Python 12306抢火车票脚本
推荐阅读