現有iOS、Android項目集成Flutter

準備工作

首先,我們分別用 Xcode 與 Android Studio 快速建立一個只有首頁的基本工程,工程名分別為 iOSDemo 與 AndroidDemo.

這時,Android 工程就已經準備好了;而對于 iOS 工程來說,由于基本工程并不支持以組件化的方式管理項目,因此我們還需要多做一步,將其改造成使用 CocoaPods 管理的工程,也就是要在 iOSDemo 根目錄下創建一個只有基本信息的 Podfile 文件:

target 'iOSDemo' do
  use_frameworks!

  target 'iOSDemoTests' do
    inherit! :search_paths
  end

  target 'iOSDemoUITests' do
  end
end

然后,在命令行輸入 pod install 后,會自動生成一個 iOSDemo.xcworkspace 文件,這時我們就完成了 iOS 工程改造。

Flutter 混編方案介紹

如果你想要在已有的原生 App 里嵌入一些 Flutter 頁面,有兩個辦法:

  • 將原生工程作為 Flutter 工程的子工程,由 Flutter 統一管理。這種模式,就是統一管理模式。
  • 將 Flutter 工程作為原生工程共用的子模塊,維持原有的原生工程管理方式不變。這種模式,就是三端分離模式。
flutter.png

由于 Flutter 早期提供的混編方式能力及相關資料有限,國內較早使用 Flutter 混合開發的團隊大多使用的是統一管理模式。

但是,隨著功能迭代的深入,這種方案的弊端也隨之顯露,不僅三端(Android、iOS、Flutter)代碼耦合嚴重,相關工具鏈耗時也隨之大幅增長,導致開發效率降低。

所以,后續使用 Flutter 混合開發的團隊陸續按照三端代碼分離的模式來進行依賴治理,實現了 Flutter 工程的輕量級接入。

輕量級接入,三端代碼分離模式把 Flutter 模塊作為原生工程的子模塊,還可以快速實現 Flutter 功能的解除依賴,降低原生工程的改造成本。而 Flutter 工程通過 Android Studio 進行管理,無需打開原生工程,可直接進行 Dart 代碼和原生代碼的開發調試。

三端工程分離模式的關鍵是抽離 Flutter 工程,將不同平臺的構建產物依照標準組件化的形式進行管理,即 Android 使用 aar、iOS 使用 pod。也就是可以像引用其他第三方原生組件庫那樣快速接入 Flutter 。

集成 Flutter

原生工程對 Flutter 的依賴主要分為兩部分:

  • Flutter 庫和引擎,也就是 Flutter 的 Framework 庫和引擎庫。
  • Flutter 工程,也就是我們自己實現的 Flutter 模塊功能,主要包括 Flutter 工程 lib 目錄下的 Dart 代碼實現的這部分功能。

創建同級目錄的Flutter module

Flutter create -t module flutter_demo

然后我們分別對 flutter_demo 進行集成

iOS 模塊集成

先說一下官方集成Flutter方式 wiki,里面用的兩個腳本 podhelper.rb 和xcode_backend.sh 分別在Podfile和Build Phases里面

flutter_application_path = '../flutter_demo/'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

解釋下這兩個腳本的作用:

1.podhelper.rb

  • 通過Pod引入Flutter.framework(Flutter引擎),以及Flutter的插件注冊表FlutterPluginRegistrant(創建的工程中GeneratedPluginRegistrant.{h,m}文件中)
  • 引入.flutter-plugins的依賴
  • 導入Generated.xcconfig配置,這里面放的是Flutter要用的環境變量(如:xcode_backend.sh開頭就要用的$FLUTTER_ROOT)

2.xcode_backend.sh

這個腳本原本是放在Build Phases,在Debug和Release模式下分別會產出對應環境的Flutter產物(Flutter有Debug,Profile,Release三個模式)。并且里面還有一句代碼是:

RunCommand cp -r -- "${app_framework}" "${derived_dir}"
  • 在Debug和Release下構建出對應的Flutter產物,App.framework,flutter_assets(App.framework就是我們在lib下面寫的Dart代碼,flutter_assets就是我們資源)把上面的產物拷貝到App包里

在 iOS 平臺,原生工程對 Flutter 的依賴分別是:

  • Flutter 庫和引擎,即 Flutter.framework;
  • Flutter 工程的產物,即 App.framework。

iOS 平臺的對 Flutter 模塊依賴,實際上就是通過打包命令生成這兩個產物,我們將它們封裝成一個 pod 供原生工程引用,這里我們解除對Flutter工程的依賴,podhelper.rb,xcode_backend.sh腳本我們不使用。

Flutter build ios --debug 

這條命令的作用是編譯 Flutter 工程生成兩個產物:Flutter.framework 和 App.framework。如果需要release,把 debug 換成 release 就可以構建 release 產物.

接下來,我們讓iOSDemo依賴Flutter的編譯的這兩個產物,這里使用cocoapods進行依賴:

我們在/flutter_demo/.ios/Flutter目錄創建FlutterEngine.podspec文件(路徑可以修改成想要的位置)

pod spec create FlutterEngine

編輯FlutterEngine.podspec 文件依賴 Flutter.framework 和 App.framework

Pod::Spec.new do |spec|

  spec.name         = "FlutterEngine"
  spec.version      = "0.0.1"
  spec.summary      = "A short description of FlutterEngine."
  spec.description  = <<-DESC
    A short description of FlutterEngine.
                   DESC
  spec.homepage         = 'https://github.com/xx/FlutterEngine'
  spec.license          = { :type => 'MIT', :file => 'LICENSE' }
    spec.author             = { "tema.tian" => "temagsoft@163.com" }
    spec.source       = { :git => "", :tag => "#{spec.version}" }
  spec.ios.deployment_target = '8.0'
  spec.ios.vendored_frameworks = "App.framework", "engine/Flutter.framework"

end

pod lib lint 一下 Flutter組件模塊就做好了,我們再修改一下iOSDemo的 Podfile,把它集成進去

target 'iOSDemo' do
  use_frameworks!
  
  pod 'FlutterEngine', :path => '../flutter_demo/.ios/Flutter/'
  
  target 'iOSDemoTests' do
    inherit! :search_paths
  end

  target 'iOSDemoUITests' do
  end

end

pod install 一下,Flutter 模塊就集成進 iOS 原生工程中了。

我們在修改一下iOSDemo AppDelegate 把window的rootViewController 設置為 FlutterViewController

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    window = UIWindow(frame: UIScreen.main.bounds)
    let flutterVC = FlutterViewController()
    flutterVC.setInitialRoute("defaultRoute")
    window?.rootViewController = flutterVC
    window?.makeKeyAndVisible()
    return true
  }

點擊xcode運行,最后點擊運行,官方的 Flutter Widget 也展示出來了。至此,iOS 工程的接入就完了。

ios_demo_s.png

Android 模塊集成

Android 原生工程對 Flutter 的依賴主要分為兩部分,對應到 Android 平臺,這兩部分分別是:

  • Flutter 庫和引擎,也就是 icudtl.dat、libFlutter.so,還有一些 class 文件。這些文件都封裝在 Flutter.jar 中。
  • Flutter 工程產物,主要包括應用程序數據段 isolate_snapshot_data、應用程序指令段 isolate_snapshot_instr、虛擬機數據段 vm_snapshot_data、虛擬機指令段 vm_snapshot_instr、資源文件 Flutter_assets。

我們對 Android 的 Flutter 依賴進行抽取,首先我們再flutter_demo根目錄,執行arr打包命令

Flutter build apk --debug

如果需要release,把 debug 換成 release 就可以構建 release 產物.

打包構建的flutter-debug.arr 位于 /.android/Flutter/build/outputs/aar/目錄下,我們把它拷貝到AndroidDemo的App/libs目錄下,然后在build.gradle中對他添加依賴:

 implementation(name: 'flutter-debug', ext: 'aar')

sync同步一下,然后我們修改AndroidDemo MainActivity的代碼,將setContentView的加載View換成FlutterView

View FlutterView = Flutter.createView(this, getLifecycle(), "defaultRoute");
setContentView(FlutterView);

最后點擊運行, Flutter Widget 就展示出來了.

android_demo_s.png

IOS,Android 集成這里是手動拷貝pod, aar,多人維護很麻煩,可以本地測試用debug包,提交用release包,或者把aar/pod以版本的方式發布到私有Maven/Cocoapod,以組件名+版本的方式在工程中引用,不直接管理打包構建產物。

對于三端工程分離模式最主要的則是抽離 Flutter 工程,將不同平臺的構建產物依照標準組件化的形式進行管理,即:針對 Android 平臺打包構建生成 aar,通過 build.gradle 進行依賴管理;針對 iOS 平臺打包構建生成 framework,將其封裝成獨立的 pod,并通過 podfile 進行依賴管理。

至此原生集成Flutter就分享完了,附上demo,歡迎大家留言探討。

參考文獻

flutter-wiki

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容