欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flutter中显示原生系统的视图控件

程序员文章站 2022-06-01 17:46:45
...

Flutter、Golang、Python、编译原理、算法、Chrome原理学习系列文章抢先看请关注【码农帮派】

Flutter中显示原生系统的视图控件

 

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通过向原生视图的Flutter封装类(Android上是AndroidView、iOS上是UIKitView)传入视图标志符,用于发起创建原生视图的请求;

  • 然后,原生系统接收到请求,创建原生视图,交给平台视图工厂类(PlatformViewFactory)实现;

  • 最后,原生系统将视图标识符和平台视图工厂类进行关联,让Flutter发起的原生视图创建请求可以直接找到对应的视图创建工厂。

 

Flutter实现平台视图调用接口

在Flutter中,针对不同的平台使用对应的视图封装类(Android使用AndroidView、iOS使用UIKitView),发送创建对应平台的原生视图,并传入视图的唯一标识符,用来建立和原生视图的关联:

Flutter中显示原生系统的视图控件

上面的是Flutter端调用原生系统控件的代码,可以看出,Flutter端使用原生系统控件与普通的Flutter控件无明显差异,只是需要根据当前的平台使用AndroidView和UIKitView进行原生控件的调用。

 

Flutter端的代码实现了,剩下的就是Android、iOS端的控件实现了。在Flutter引用的原生宿主入口处,我们需要注册一个监听接口,用来监听来自Flutter的平台视图创建请求,当接收到请求之后,按照viewType(AndroidView/UIKitView中传递的viewType参数),来决定调用哪个视图工厂类,被调用的视图工厂类在其create方法中返回一个原生视图的封装类类,在该原生视图封装类中生成原生视图。Flutter在获得这个原生视图之后,已经是在Flutter层面的控件,就可以像普通控件一样加入到Flutter渲染树中。

 

Android端实现平台视图

首先是视图工厂类SampleViewFactory.java,在视图工厂类的create方法中返回一个原生视图封装类:

Flutter中显示原生系统的视图控件

之后是原生视图封装类SimpleViewController.java,在这个类中我们创建并返回对应的原生视图:

Flutter中显示原生系统的视图控件

在上面代码的第8行,可以初始化生成你需要的Android原生视图,比如地图控件等。

 

最后,我们需要绑定监听来自Flutter端的调用平台视图的请求,和方法通道一样,我们仍然是在原生宿主的Flutter入口处(MainActivity.java)进行注册:

Flutter中显示原生系统的视图控件

这样Android端就可以监听到来自Flutter端的平台视图调用请求,并通过视图工厂/视图封装类生成原生视图,并将其返回给Flutter端,以Flutter控件的方式加入到Flutter层的渲染树进行渲染。

 

iOS端实现平台视图

首先,是平台视图封装类SampleViewController.m:

Flutter中显示原生系统的视图控件

然后是平台视图工厂类SampleViewFactory.m:

Flutter中显示原生系统的视图控件

最后需要在原生宿主的Flutter入口处进行接口注册(AppDelegate.m):

Flutter中显示原生系统的视图控件

【注意】

在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端,通过平台视图显示原生系统的视图控件,如何实现Flutter和平台视图的交互呢,这就要使用方法通道。

 

我们知道Android/iOS的视图控件状态的改变是命令式的,所以我们可以在原生视图封装类中将修改视图实例的接口,以方法通道的方式暴露给Flutter,从而让Flutter可以修改原生视图的状态。

 

下面的例子中在Flutter端创建了一个原生视图控制器,通过方法通道,实现与平台视图的交互:

Flutter中显示原生系统的视图控件

然后声明Flutter端的StatefulWidget控件:

Flutter中显示原生系统的视图控件

 

Android端的代码实现:

Flutter中显示原生系统的视图控件

 

iOS端代码的实现:

Flutter中显示原生系统的视图控件

之后我们就可以在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();
           }),
      );
    }
}