手把手 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閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容