準備工作
首先,我們分別用 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 早期提供的混編方式能力及相關資料有限,國內較早使用 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 工程的接入就完了。
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 就展示出來了.
IOS,Android 集成這里是手動拷貝pod, aar,多人維護很麻煩,可以本地測試用debug包,提交用release包,或者把aar/pod以版本的方式發布到私有Maven/Cocoapod,以組件名+版本的方式在工程中引用,不直接管理打包構建產物。
對于三端工程分離模式最主要的則是抽離 Flutter 工程,將不同平臺的構建產物依照標準組件化的形式進行管理,即:針對 Android 平臺打包構建生成 aar,通過 build.gradle 進行依賴管理;針對 iOS 平臺打包構建生成 framework,將其封裝成獨立的 pod,并通過 podfile 進行依賴管理。
至此原生集成Flutter就分享完了,附上demo,歡迎大家留言探討。