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

本文未提供 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.dartAppWidgetStateinitState () 方法中注册 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 开发人员,拥抱和体验 " 新 " 技术。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容