目前大多數公司都有自己開發多年的專案,不可能直接用
Flutter
從頭開發一套,那樣不實現,除非是小專案,因此只能是在原有的基礎上用Flutter
來開發新業務或重構舊業務,而這裡就需要用到Flutter
的混合開發
一、建立Flutter模組
使用混合開發就不能像之前一樣直接上來就建立一個 Flutter
專案,而是要使用 Flutter模板
# flutter_module_lxf 可以隨便你命名
flutter create --template module flutter_module_lxf
# --template 可以替換為 -t
# flutter create -t module flutter_module_lxf
複製程式碼
建立出來的 Flutter
模組依然是可以像之前建立的Flutter專案
一樣開啟和執行的。
目錄下有也有 ios
和 android
目錄,只不過前面加了個點 ,成了點目錄。
二、iOS
整合
通過
Cocoapods
,將Flutter
模組編譯成一個庫,再到原生專案中進行引入和使用即可
在 Podfile
中新增兩行配置
# 指定我們剛剛建立的 Flutter 模組的路徑
flutter_application_path = '../flutter_module_lxf'
# 拼接指令碼檔案的路徑: .ios/Flutter/podhelper.rb
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
複製程式碼
在每個需要引用 Flutter
的 Target
下,都需要新增一行配置
install_all_flutter_pods(flutter_application_path)
複製程式碼
新增後如下所示:
flutter_application_path = '../flutter_module_lxf'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
use_frameworks!
target 'LXFFlutterHybridDemo' do
install_all_flutter_pods(flutter_application_path)
end
複製程式碼
新增完成後,執行一次 pod install
使用
兩個步驟
- 獲取 Flutter引擎
FlutterEngine
- 通過
FlutterEngine
建立FlutterViewController
基本使用
AppDelegate
類中宣告一個 FlutterEngine
變數,在 didFinishLaunchingWithOptions
方法中啟動 Flutter引擎
// AppDelegate.swift
import Flutter
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
// 建立 Flutter引擎
lazy var flutterEngine = FlutterEngine(name: "lxf")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 啟動 Flutter引擎
flutterEngine.run()
return true
}
...
}
複製程式碼
ViewController
中新增一個按鈕,點選彈出 Flutter模組
// ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIButton(type: .custom)
btn.frame = CGRect(x: 100, y: 200, width: 200, height: 44)
btn.backgroundColor = .black
btn.addTarget(self, action: #selector(showFlutterVc), for: .touchUpInside)
btn.setTitle("彈出Flutter模組", for: .normal)
self.view.addSubview(btn)
}
@objc func showFlutterVc() {
// 建立FlutterViewController
let flutterVc = FlutterViewController(engine: fetchFlutterEngine(), nibName: nil, bundle: nil)
self.present(flutterVc, animated: true, completion: nil)
}
func fetchFlutterEngine() -> FlutterEngine {
return (UIApplication.shared.delegate as! AppDelegate).flutterEngine
}
複製程式碼
如果遇到報 Command PhaseScriptExecution failed with a nonzero exit code
錯誤,如下圖所示:
請先用 Android Studio
或 VSCode
開啟 Flutter模組
專案並執行到iOS裝置上,讓其幫我們對iOS專案進行一些初始化配置。成功執行後就可以關閉 Flutter模組
專案的執行了,接著再用 Xcode
開啟原生專案執行即可。
修改初始路由
官方文件裡面提到,修改初始路由,需要在 Flutter引擎
在 run
之前,通過 invokeMethod
呼叫 setInitialRoute
方法進行設定,程式碼如下
// 修改初始路由
flutterEngine.navigationChannel.invokeMethod("setInitialRoute", arguments: "/other")
// 啟動 Flutter引擎
flutterEngine.run()
複製程式碼
但是,我發現這樣寫並沒有起任何作用,在 Flutter
的官方 issue
上也有人提到這個問題: 【setInitialRoute is broken for iOS add-to-app #59895】,目前只能官方進行修復和調整 API
臨時可以使用如下方式實現:
let flutterVc = FlutterViewController(project: FlutterDartProject(), nibName: nil, bundle: nil)
flutterVc.setInitialRoute("/other")
self.present(flutterVc, animated: true, completion: nil)
複製程式碼
雖然這麼寫可以實現這個功能,但是會有明顯的類似卡頓的現象,因為使用這種方式去建立 FlutterViewController
之前,會隱式建立和啟動一個 FlutterEngine
,而我們彈出 FlutterViewController
時 FlutterEngine
還沒載入完畢,所以我們會看到先彈出了一個透明的介面,再顯示 /other
路由對應的介面檢視。
使用 FlutterAppDelegate
使用 FlutterAppDelegate
這個不是必要的操作,但是如果你想讓 Flutter模組
也能使用原生的功能的話,建議使用
原生功能
- 處理
openURL
的回撥- 列表檢視在點選狀態列後滾到頂部
class AppDelegate: FlutterAppDelegate
複製程式碼
更具體的使用,請閱讀 官方文件
三、Android
修改安卓專案 根目錄下的 settings.gradle
檔案
// settings.gradle
include ':app' // assumed existing content
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
// 這裡的 flutter_module_lxf 請修改為你自己建立的Flutter模板目錄名稱
'flutter_module_lxf/.android/include_flutter.groovy' // new
))
複製程式碼
修改安卓專案 app
目錄下的 build.gradle
檔案
// app/build.gradle
dependencies {
...
// 配置flutter依賴
implementation project(':flutter')
}
複製程式碼
如果在編譯的時候遇到如下錯誤
Default interface methods are only supported starting with Android N (--min-api 24): void androidx.lifecycle.DefaultLifecycleObserver.onCreate(androidx.lifecycle.LifecycleOwner)
複製程式碼
請確認是否指定了使用 Java 8
進行編譯 【官方文件 - Java 8 requirement】
修改安卓專案 app
目錄下的 build.gradle
檔案
// app/build.gradle
android {
...
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
...
}
複製程式碼
修改 app/src/main/AndroidManifest.xml
檔案
// app/src/main/AndroidManifest.xml
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/AppTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
複製程式碼
新增一個按鈕,點選彈出 Flutter模組
<!-- activity_main.xml -->
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="彈出Flutter模組"
android:background="#000000"
android:textColor="#ffffff"
android:gravity="center"
android:onClick="btnClick"
/>
複製程式碼
// MainActivity.java
public void btnClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(this)
);
}
複製程式碼
四、除錯與熱過載
由於當前我們是使用原生開發工具(如:Xcode)來執行專案,每次修改我們的
Flutter模組
的程式碼,也就需要重新執行才能看到效果,不像之前按下Cmd + s
就能進行熱過載。這樣Flutter模組
的開發效率極其低下,那有沒有辦法可以讓我們像之前開發Flutter
專案時那樣進行熱過載
呢?答案是有的
Flutter
官方提供了 flutter attach
,以輔助我們開發,到終端下執行
flutter attach
複製程式碼
如果當前有多個裝置,會提示我們需要指定 attach
哪個裝置
按要求加上指定引數即可
flutter attach -d FE305309-9E79-418D-BA3F-7EFECF2980BC
複製程式碼
如圖,這樣就關聯上了,你在 dart
檔案裡面對介面進行任何修改後,按 r
進行熱過載,按 R
進行熱啟動。
如果你使用的是 Android Studio
,可以直接選擇對應的裝置後,點選右邊的 Flutter Attach
按鈕,執行成功後就可以跟之前一樣按 Cmd + s
進行熱過載了。
五、資料
-
GitHub
-
官方文件
add-to-app | add-to-app/ios | add-to-app/android | Debugging & hot reload