手把手 Flutter 混合开发 -- 基于 FlutterBoost 实现

目录

1.FlutterBoost 简介

2.FlutterBoost 集成 ( 版本 1.17.1 )

2.1 Flutter 端集成

2.2 Android 端集成

3.FlutterBoost 使用

3.1 FlutterBoost 初始化

3.2 Native 传参启动 Flutter 页面,并接收返回结果

3.3 Flutter 传参启动 Native / Flutter 页面,并接收返回结果

4.最后


本文未提供 iOS 端集成与使用示例

        当一个成熟的产品/项目,想要开始 Flutter 开发,同时又不想从零开始全面使用 Flutter 开发,最后选择保留原有 Native 项目代码和功能,在新业务或变动上使用 Flutter 进行开发,这种以 既有原生,又有 Flutter 的开发模式就称之为 Flutter 混合开发模式。该开发模式的好处体现在,不用全面推翻 APP 项目的原有积累, Native 端就可以无感接入 Flutter ,开始 Flutter 开发,拥抱 Flutter 的特性特点。

1.FlutterBoost 简介

        FlutterBoost 是阿里系闲鱼技术团队开源的 Flutter 插件,它可以轻松为现有原生应用程序提供 Flutter 混合集成方案。其理念是将 Flutter 像 WebView 那样来使用。FlutterBoost 帮开发者处理 Native 与 Flutter 页面的映射和跳转,开发者只需关心页面的名字和参数即可 ( 通常可以是 URL ) 。

FlutterBoost 的优点

  • 官方的集成方案有诸多弊病,eg:日志不能输出到原生端、存在内存泄漏的问题、资源冗余……
  • FlutterBoost 的通道的封装使得 Native 调用 Flutter 、Flutter 调用 Native 的开发更加简便
  • FlutterBoost 对于页面生命周期的管理,也梳理的比较整齐
  • FlutterBoost 为阿里出品,已在闲鱼生产环境中使用,正稳定为亿级用户提供服务

2.FlutterBoost 集成 ( 版本 1.17.1 )

2.1 Flutter 端集成

        通过 IDE 创建 Flutter Module ,命名为 flutter_module ( 本示例使用的是 Android Studio ,也可以通过别的 IDE 或命令台方式创建 Flutter Module ) 

2.1-1 通过 Android Studio 创建 Flutter Module
2.1-2 通过 Android Studio 创建 Flutter Module

        使用 IDE 打开上面步骤创建的 Flutter Module,在根目录 pubspec.yaml 文件中的 dependencies : 下添加 FlutterBoost 依赖  ( 点击跳转查看源码 )

#闲鱼 flutter_boost
flutter_boost:
  git:
    url: 'https://github.com/alibaba/flutter_boost.git'
    ref: '1.17.1'
2.1-3 添加 FlutterBoost 依赖

        通过命令 flutter build apk 来检查是否完成 FlutterBoost 的依赖,成功示意图如下

2.1-4 测试是否集成成功

2.2 Android 端集成

        新建 Android 项目或在原有 Android 项目(本示例中的 Android 项目名为 AndroidDemo )的根目录中的 settings.gradle 内引用上一个步骤创建的 Flutter Module ,代码如下 ( PS :文件名与路径请视实际情况调整 ) ( 点击跳转查看源码 )

//引用 Flutter 模块
setBinding(new Binding([gradle:this]))
evaluate(new File('../flutter_module/.android/include_flutter.groovy'))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
2.2-1 Androud 端引用 Flutter Module

        本示例中对应的文件路径/层级关系如下图

2.2-2 文件路径/层级关系

        添加完成后编译项目,sync now,完成后得到如下项目结构,项目中多出了 flutter、flutter_boost 和 flutter_module

2.2-3 编译后的项目结构

        接着完成 Android 端 FlutterBoost 的依赖,在 app 下的 build.gradle 中的 dependencies 下添加 FlutterBoost 依赖并同步 ( 点击跳转查看源码 )

2.2-4 Android 端依赖 FlutterBoost

        以上操作就完成了Android 端 Flutter Module 和 FlutterBoost 的引用和依赖,因为 iOS 端的引用需要在 macOS 上操作,故在此不做演示 。

3.FlutterBoost 使用

        只介绍 Android 端和 Flutter 端的相互调用,因当前操作系统为 Windows 而非 macOS 所以未包含 iOS 端。

3. 效果演示

主要讲解如下功能点的实现:

  • FlutterBoost 的初始化
  • 基于 FlutterBoost 实现 Native 启动 Flutter 页面,携带请求参数并接收返回结果
  • 基于 FlutterBoost 实现 Flutter 启动 Native 页面,携带请求参数并接收返回结果
  • 基于 FlutterBoost 实现 Flutter 启动 Flutter 页面,携带请求参数并接收返回结果

3.1 FlutterBoost 初始化

3.1.1 Android 端初始化

        1.在 AndroidManifest.xml 中添加配置 ( 点击跳转查看源码 )

<!-- FlutterBoost 配置 -->
<meta-data
    android:name="flutterEmbedding"
    android:value="2" />
3.1.1-1 Android 端添加 FlutterBoost 配置

        2.自定义 Application ,并在 onCreate 方法中初始化 FlutterBoost ( 点击跳转查看源码 )  

3.1.1-2 Android 端初始化 FlutterBoost

3.1.2 Flutter 端初始化

        1.在 main.dart 中 AppWidgetStateinitState () 方法中注册 FlutterBoost ,并在 build 方法中初始化 FlutterBoost ( 点击跳转查看源码 ) 

3.1.2-1 Flutter 端注册 FlutterBoost 并初始化

3.2 Native 传参启动 Flutter 页面,并接收返回结果

        启动 Flutter 页面需要一个 Activity 作为载体,该载体应为 BoostFlutterActivity 或其子类,所以有两种方式启动 Flutter 页面。

1.单纯通过 BoostFlutterActivity 启动 Flutter 页面

       1.1 在 AndroidManifest.xml 注册 BoostFlutterActivity ( 点击跳转查看源码 )

3.2.1-1 注册 BoostFlutterActivity

       1.2 根据设定的 路由地址( 该地址应与 Flutter Module 中设定的保持一致 ) 跳转启动 Flutter 页面,这里封装成方法 openPageByUrl ( 点击跳转查看源码 )

                    //跳转 Flutter 页面
                    val intent =
                        BoostFlutterActivity.withNewEngine().url(url)
                            .params(requestParams ?: mutableMapOf())
                            .backgroundMode(BoostFlutterActivity.BackgroundMode.opaque)
                            .build(context)
                    if (context is Activity) {
                        context.startActivityForResult(intent, requestCode)
                    } else {
                        context.startActivity(intent)
                    }
3.2.1-2 启动 Flutter 页面

       1.3 在启动 openPageByUrl 方法的 Activity 上重写 onActivityResult 方法接收回调

3.2.1-3 注册回调监听

2.通过自定义 Activity 继承 BoostFlutterActivity 为载体启动 Flutter 页面

       2.1 自定义 FlutterPageActivity 继承 BoostFlutterActivity 并重写 getContainerUrlParams ()getContainerUrl ()onActivityResult 方法 ( 点击跳转查看源码 )

3.2.2-1 自定义 BoostFlutterActivity

       2.2 启动 FlutterPageActivity ( 别忘了在 AndroidManifest.xml 中注册该 Activity )

3.2.2-2 启动自定义 BoostFlutterActivity

3.Flutter 端进行相应配置

       3.1 在 main.adrtFlutterBoost.singleton.registerPageBuilders 方法中注册 路由地址 ( 该地址应与 Native 层的保持一致 ) 与其 对应的 Widget 并接收 请求数据 params ( 点击跳转查看源码 )

3.2.3-1 Flutter 端注册路由地址与其对应的页面

       3.2 Widget 中调用 FlutterBoost 提供的 closeCurrent 方法关闭当前 Flutter 页面并返回数据给上一个页面 ( Flutter / Native )  

3.2.3-2 关闭当前 Flutter 页面并返回数据

3.3 Flutter 传参启动 Native / Flutter 页面,并接收返回结果

当 Flutter 端通过 FlutterBoost 启动新的页面时,无论要启动的目标页面属于 Native 页面还是 Flutter 页面,其都只是触发 Native 端 ( 以 Android 端为例 ) INativeRouter 接口的 openContainer 回调,然后 Native 端在该回调方法中接收请求参数 urlParams ,并根据 url 控制是否启动及启动哪一个新页面。 

3.3.1 Flutter 端传参启动新页面

1.Flutter 端启动新页面 ( 点击跳转查看源码 ) 

                  //启动新页面带请求参数和回调监听
                  Map<String, dynamic> requestParams = Map();
                  requestParams[HasRequestParamsNative.EXTRA_KEY_NATIVE] = "Hello native";
                  FlutterBoost.singleton
                      .open(Test.PAGE_NATIVE_HAS_RESULT,
                          urlParams: requestParams)
                      .then((Map<dynamic, dynamic> value) {
                    //TODO 回调监听 value 则是新页面返回的数据
                    
                  });

 2.Android 端在 FlutterBoost 初始化的时候,注册路由跳转监听,并在回调方法中判断启动新页面 ( 点击跳转查看源码 ) 

        //路由跳转监听
        val router = INativeRouter { context, url, urlParams, requestCode, exts ->
            /**
             * 当 Flutter 中使用 FlutterBoost 启动新页面 (Flutter/Native) 时,触发该回调
             * 如果需要启动 Native 页面,则通过 context.startActivity 启动指定页面
             * 如果需要启动 Flutter 页面,则通过 FlutterBoost 启动指定页面
             */
            //TODO 判断启动新页面
            //PageRouter.openPageByUrl(context, url, urlParams, requestCode)
        }

        val platform = FlutterBoost.ConfigBuilder(this, router)
            .isDebug(true)
            .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
            .renderMode(FlutterView.RenderMode.texture)
            .lifecycleListener(boostLifecycleListener)
            .build()
        FlutterBoost.instance().init(platform)

3.3.2 Android 端返回数据给上一个页面 ( 点击跳转查看源码 ) 

        /**
         * 关闭当前页面并返回数据至 Flutter 页面
         * @param resultData 返回数据
         */
        fun finishAndResult(activity: Activity, resultData: MutableMap<String, Any>? = null) {
            val intent = Intent()
            val bundle = Bundle()
            bundle.putSerializable(
                IFlutterViewContainer.RESULT_KEY,
                (resultData ?: mutableMapOf()) as Serializable
            )
            intent.putExtras(bundle)
            activity.setResult(0, intent)
            activity.finish()
        }

3.3.3 Flutter 端返回数据给上一个页面 ( 点击跳转查看源码 ) 

                  // FlutterBoost 关闭当前页面并回调数据给上一个页面
                  var resultMap = Map<String, dynamic>();
                  resultMap[HasResult.EXTRA_KEY_NAME] = "张先生";
                  resultMap[HasResult.EXTRA_KEY_AGE] = 26;
                  FlutterBoost.singleton.closeCurrent(result: resultMap);

4.最后

        因为发现现阶段 FlutterBoost 的集成与使用文档较少或较旧,所以本文主要讲原生项目如何借助 FlutterBoost 集成 Flutter 开始混合开发,基于 FlutterBoost 的 Flutter 与 Native 的基础交互也已详细交代清楚。

        针对那些还对 Flutter 开发持观望态度的开发人员来说,建议可以借着 FlutterBoost 的简洁便利,试着把项目从原生项目开始尝试 Flutter 开发,同时让自己从原生开发人员开始兼职 Flutter 开发人员,拥抱和体验 " 新 " 技术。