目前大多數公司都有本身開發多年的項目,不可能直接用
Flutter
從頭開發一套,那樣不實現,除非是小項目,所以只能是在原有的基礎上用Flutter
來開發新業務或重構舊業務,而這裏就須要用到Flutter
的混合開發
java
使用混合開發就不能像以前同樣直接上來就建立一個 Flutter
項目,而是要使用 Flutter模板
android
# flutter_module_lxf 能夠隨便你命名
flutter create --template module flutter_module_lxf
# --template 能夠替換爲 -t
# flutter create -t module flutter_module_lxf
複製代碼
建立出來的 Flutter
模塊依然是能夠像以前建立的Flutter項目
同樣打開和運行的。ios
目錄下有也有 ios
和 android
目錄,只不過前面加了個點 ,成了點目錄。git
經過
Cocoapods
,將Flutter
模塊編譯成一個庫,再到原生項目中進行引入和使用便可github
在 Podfile
中添加兩行配置shell
# 指定咱們剛剛建立的 Flutter 模塊的路徑
flutter_application_path = '../flutter_module_lxf'
# 拼接腳本文件的路徑: .ios/Flutter/podhelper.rb
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
複製代碼
在每一個須要引用 Flutter
的 Target
下,都須要添加一行配置swift
install_all_flutter_pods(flutter_application_path)
複製代碼
添加後以下所示:api
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
ruby
兩個步驟bash
- 獲取 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
這個不是必要的操做,可是若是你想讓 Flutter模塊
也能使用原生的功能的話,建議使用
原生功能
- 處理
openURL
的回調- 列表視圖在點擊狀態欄後滾到頂部
class AppDelegate: FlutterAppDelegate 複製代碼
更具體的使用,請閱讀 官方文檔
修改安卓項目 根目錄下的 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