Flutter - 混合開發

LinXunFeng發表於2020-07-12

目前大多數公司都有自己開發多年的專案,不可能直接用 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專案 一樣開啟和執行的。

目錄下有也有 iosandroid 目錄,只不過前面加了個點 ,成了點目錄。

Flutter - 混合開發

二、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')
複製程式碼

在每個需要引用 FlutterTarget 下,都需要新增一行配置

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 - 混合開發

使用

兩個步驟

  1. 獲取 Flutter引擎 FlutterEngine
  2. 通過 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
}
複製程式碼

Flutter - 混合開發

如果遇到報 Command PhaseScriptExecution failed with a nonzero exit code 錯誤,如下圖所示:

Flutter - 混合開發

請先用 Android StudioVSCode 開啟 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,而我們彈出 FlutterViewControllerFlutterEngine 還沒載入完畢,所以我們會看到先彈出了一個透明的介面,再顯示 /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
))  
複製程式碼

Flutter - 混合開發

修改安卓專案 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
  }
  ...
}
複製程式碼

Flutter - 混合開發

修改 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 - 混合開發

新增一個按鈕,點選彈出 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 - 混合開發

按要求加上指定引數即可

flutter attach -d FE305309-9E79-418D-BA3F-7EFECF2980BC
複製程式碼

Flutter - 混合開發

如圖,這樣就關聯上了,你在 dart 檔案裡面對介面進行任何修改後,按 r 進行熱過載,按 R 進行熱啟動。

如果你使用的是 Android Studio,可以直接選擇對應的裝置後,點選右邊的 Flutter Attach 按鈕,執行成功後就可以跟之前一樣按 Cmd + s 進行熱過載了。

Flutter - 混合開發

五、資料

Flutter - 混合開發

相關文章