在本次課程的最后一個主題里,我來和你聊聊如何設計自己的 Flutter 混合開發框架。
所謂混合開發,是指在 App 的整體架構繼續使用原生技術棧的基礎上,將 Flutter 運行環境嵌入到原生 App 工程中:由原生開發人員為 Flutter 運行提供宿主容器及基礎能力支撐,而 Flutter 開發人員則負責應用層業務及 App 內大部分渲染工作。
這種開發模式的好處十分明顯。對于工程師而言,跨平臺的 Flutter 框架減少了對底層環境的依賴,使用完整的技術棧和工具鏈隔離了各個終端系統的差異,無論是 Android、iOS 甚至是前端工程師,都可以使用統一而標準化的能力進行業務開發,從而擴充了技能棧。而對于企業而言,這種方式不僅具備了原生 App 良好的用戶體驗,以及豐富的底層能力,還同時擁有了跨平臺技術開發低成本和多端體驗一致性的優勢,直接節省研發資源。
那么,在原生工程中引入 Flutter 混合開發能力,我們應該如何設計工程架構,原生開發與 Flutter 開發的工作模式又是怎樣的呢?
接下來,在今天的分享中,我會著重為你介紹這兩個主題設計思路和建設方向;而在下一次分享中,我則會通過一個實際的案例,與你詳細說明在業務落地中,我們需要重點考慮哪些技術細節,這樣你在為自己的原生工程中設計混合開發框架時也就有跡可循了。
## 混合開發架構
在[第 41 篇文章](https://time.geekbang.org/column/article/144121)中,我與你介紹了軟件功能分治的兩種手段,即組件化和平臺化,以及如何在滿足單向依賴原則的前提下,以分層的形式將軟件功能進行分類聚合的方法。這些設計思想,能夠讓我們在設計軟件系統架構時,降低整體工程的復雜性,提高 App 的可擴展性和可維護性。
與純 Flutter 工程能夠以自治的方式去分拆軟件功能、管理工程依賴不同,**Flutter 混合工程的功能分治**需要原生工程與 Flutter 工程一起配合完成,即:在 Flutter 模塊的視角看來,一部分與渲染相關的基礎能力完全由 Flutter 代碼實現,而另一部分涉及操作系統底層、業務通用能力部分,以及整體應用架構支撐,則需要借助于原生工程給予支持。
在第 41 篇文章中,我們通過四象限分析法,把純 Flutter 應用按照業務和 UI 分解成 4 類。同樣的,混合工程的功能單元也可以按照這個分治邏輯分為 4 個維度,即不具備業務屬性的原生基礎功能、不具備業務屬性的原生 UI 控件、不具備 UI 屬性的原生基礎業務功能和帶 UI 屬性的獨立業務模塊。
:-: 
圖 1 四象限分析法
從圖中可以看到,對于前 3 個維度(即原生 UI 控件、原生基礎功能、原生基礎業務功能)的定義,純 Flutter 工程與混合工程并無區別,只不過實現方式由 Flutter 變成了原生;對于第四個維度(即獨立業務模塊)的功能歸屬,考慮到業務模塊的最小單元是頁面,而 Flutter 的最終呈現形式也是獨立的頁面,因此我們把 Flutter 模塊也歸為此類,我們的工程可以像依賴原生業務模塊一樣直接依賴它,為用戶提供獨立的業務功能。
我們把這些組件及其依賴按照從上到下的方式進行劃分,就是一個完整的混合開發架構了。可以看到,原生工程和 Flutter 工程的邊界定義清晰,雙方都可以保持原有的分層管理依賴的開發模式不變。
:-: 
圖 2 Flutter 混合開發架構
需要注意的是,作為一個內嵌在原生工程的功能組件,Flutter 模塊的運行環境是由原生工程提供支持的,這也就意味著在渲染交互能力之外的部分基礎功能(比如網絡、存儲),以及和原生業務共享的業務通用能力(比如支付、賬號)需要原生工程配合完成,即原生工程以分層的形式提供上層調用接口,Flutter 模塊以插件的形式直接訪問原生代碼宿主對應功能實現。
因此,不僅不同歸屬定義的原生組件之前存在著分層依賴的關系,Flutter 模塊與原生組件之前也隱含著分層依賴的關系。比如,Flutter 模塊中處于基礎業務模塊的賬號插件,依賴位于原生基礎業務模塊中的賬號功能;Flutter 模塊中處于基礎業務模塊的網絡插件,依賴位于原生基礎功能的網絡引擎。
可以看到,在混合工程架構中,像原生工程依賴 Flutter 模塊、Flutter 模塊又依賴原生工程這樣跨技術棧的依賴管理行為,我們實際上是通過**將雙方抽象為彼此對應技術棧的依賴,從而實現分層管理**的:即將原生對 Flutter 的依賴抽象為依賴 Flutter 模塊所封裝的原生組件,而 Flutter 對原生的依賴則抽象為依賴插件所封裝的原生行為。
## Flutter 混合開發工作流
對于軟件開發而言,工程師的職責涉及從需求到上線的整個生命周期,包含需求階段 -> 方案階段 -> 開發階段 -> 發布階段 -> 線上運維階段。可以看出,這其實就是一種抽象的工作流程。
其中,**和工程化關聯最為緊密的是開發階段和發布階段**。我們將工作流中和工程開發相關的部分抽離,定義為開發工作流,根據生命周期中關鍵節點和高頻節點,可以將整個工作流劃分為如下七個階段,即初始化 -> 開發 / 調試 -> 構建 -> 測試 -> 發布 -> 集成 -> 原生工具鏈:
:-: 
圖 3 Flutter 混合開發工作流
前 6 個階段是 Flutter 的標準工作流,最后一個階段是原生開發的標準工作流。
可以看到,**在混合開發工作模式中,Flutter 的開發模式與原生開發模式之間有著清晰的分工邊界**:Flutter 模塊是原生工程的上游,其最終產物是原生工程依賴。從原生工程視角看,其開發模式與普通原生應用并無區別,因此這里就不再贅述了,我們**重點討論 Flutter 開發模式**。
對于 Flutter 標準工作流的 6 個階段而言,每個階段都會涉及業務或產品特性提出的特異性要求,技術方案的選型,各階段工作成本可用性、可靠性的衡量,以及監控相關基礎服務的接入和配置等。
每件事兒都是一個固定的步驟,而當開發規模隨著文檔、代碼、需求增加時,我們會發現重復的步驟越來越多。此時,**如果我們把這些步驟像抽象代碼一樣,抽象出一些相同操作,就可以大大提升開發效率。**
優秀的程序員會發掘工作中的問題,從中探索提高生產力的辦法,而**轉變思維模式就是一個不錯的起點**。以持續交付的指導思想來看待這些問題,我們希望整體方案能夠以可重復、可配置化的形式,來保障整個工作流的開發體驗、效率、穩定性和可靠性,而這些都離不開 Flutter 對命令行工具支持。
比如,對于測試階段的 Dart 代碼分析,我們可以使用 flutter analyze 命令對代碼中可能存在的語法或語義問題進行檢查;又比如,在發布期的 package 發布環節,我們可以使用 flutter packages pub publish --dry-run 命令對待發布的包進行發布前檢查,確認無誤后使用去掉 dry-run 參數的 publish 命令將包提交至 Pub 站點。
這些基本命令對各個開發節點的輸入、輸出以及執行過程進行了抽象,熟練掌握它們及對應的擴展參數用法,我們不僅可以在本地開發時打造一個易用便捷的工程開發環境,還可以將這些命令部署到云端,實現工程構建及部署的自動化。
我把這六個階段涉及的關鍵命令總結為了一張表格,你可以結合這張表格,體會落實在具體實現中的 Flutter 標準工作流。
:-: 表 1 Flutter 標準工作流命令

## 總結
對于 Flutter 混合開發而言,如何處理好原生與 Flutter 之間的關系,需要從工程架構與工作模式上定義清晰的分工邊界。
在架構層面,將 Flutter 模塊定義為原生工程的獨立業務層,以原生基礎業務層向 Flutter 模塊提供業務通用能力、原生基礎能力層向 Flutter 模塊提供基礎功能支持這樣的方式去分層管理依賴。
在工作模式層面,將作為原生工程上游的 Flutter 模塊開發,抽象為原生依賴產物的工程管理,并提煉出對應的工作流,以可重復、配置化的命令行方式對各個階段進行統一管理。
可以看到,在原生 App 工程中引入 Flutter 運行環境,由原生開發主做應用架構和基礎能力賦能、Flutter 開發主做應用層業務的混合開發協作方式,能夠綜合原生 App 與 Flutter 框架雙方的特點和優勢,不僅可以直接節省研發資源,也符合目前行業人才能力模型的發展趨勢。
## 思考題
除了工程依賴之外,我們還需要管理 Flutter SDK 自身的依賴。考慮到 Flutter SDK 升級非常頻繁,對于多人協作的團隊模式中,如何保證每個人使用的 Flutter SDK 版本完全一致呢?
- 前言
- 開篇詞
- 預習篇
- 01丨預習篇 · 從0開始搭建Flutter工程環境
- 02丨預習篇 · Dart語言概覽
- Flutter開發起步
- 03丨深入理解跨平臺方案的歷史發展邏輯
- 04丨Flutter區別于其他方案的關鍵技術是什么?
- 05丨從標準模板入手,體會Flutter代碼是如何運行在原生系統上的
- Dart語言基礎
- 06丨基礎語法與類型變量:Dart是如何表示信息的?
- 07丨函數、類與運算符:Dart是如何處理信息的?
- 08丨綜合案例:掌握Dart核心特性
- Flutter基礎
- 09丨Widget,構建Flutter界面的基石
- 10丨Widget中的State到底是什么?
- 11丨提到生命周期,我們是在說什么?
- 12丨經典控件(一):文本、圖片和按鈕在Flutter中怎么用?
- 13丨ListView在Flutter中是什么?
- 14 丨 經典布局:如何定義子控件在父容器中排版位置?
- 15 丨 組合與自繪,我該選用何種方式自定義Widget?
- 16 丨 從夜間模式說起,如何定制不同風格的App主題?
- 17丨依賴管理(一):圖片、配置和字體在Flutter中怎么用?
- 18丨依賴管理(二):第三方組件庫在Flutter中要如何管理?
- 19丨用戶交互事件該如何響應?
- 20丨關于跨組件傳遞數據,你只需要記住這三招
- 21丨路由與導航,Flutter是這樣實現頁面切換的
- Flutter進階
- 22丨如何構造炫酷的動畫效果?
- 23丨單線程模型怎么保證UI運行流暢?
- 24丨HTTP網絡編程與JSON解析
- 25丨本地存儲與數據庫的使用和優化
- 26丨如何在Dart層兼容Android-iOS平臺特定實現?(一)
- 27丨如何在Dart層兼容Android-iOS平臺特定實現?(二)
- 28丨如何在原生應用中混編Flutter工程?
- 29丨混合開發,該用何種方案管理導航棧?
- 30丨為什么需要做狀態管理,怎么做?
- 31丨如何實現原生推送能力?
- 32丨適配國際化,除了多語言我們還需要注意什么
- 33丨如何適配不同分辨率的手機屏幕?
- 34丨如何理解Flutter的編譯模式?
- 35丨HotReload是怎么做到的?
- 36丨如何通過工具鏈優化開發調試效率?
- 37丨如何檢測并優化FlutterApp的整體性能表現?
- 38丨如何通過自動化測試提高交付質量?
- Flutter綜合應用
- 39丨線上出現問題,該如何做好異常捕獲與信息采集?
- 40丨衡量FlutterApp線上質量,我們需要關注這三個指標
- 41丨組件化和平臺化,該如何組織合理穩定的Flutter工程結構?
- 42丨如何構建高效的FlutterApp打包發布環境?
- 43丨如何構建自己的Flutter混合開發框架(一)?
- 44丨如何構建自己的Flutter混合開發框架(二)?
- 結束語
- 結束語丨勿畏難,勿輕略