Flutter - 混合開發

目前大多數公司都有本身開發多年的項目,不可能直接用 Flutter 從頭開發一套,那樣不實現,除非是小項目,所以只能是在原有的基礎上用 Flutter 來開發新業務或重構舊業務,而這裏就須要用到 Flutter混合開發java

1、建立Flutter模塊

使用混合開發就不能像以前同樣直接上來就建立一個 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

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

2、iOS

集成

經過 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')
複製代碼

在每一個須要引用 FlutterTarget 下,都須要添加一行配置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 installruby

使用

兩個步驟bash

  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
}
複製代碼

若是遇到報 Command PhaseScriptExecution failed with a nonzero exit code 錯誤,以下圖所示:

請先用 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 複製代碼

更具體的使用,請閱讀 官方文檔

3、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)
    );
}
複製代碼

4、調試與熱重載

因爲當前咱們是使用原生開發工具(如: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 進行熱重載了。

5、資料

相關文章
相關標籤/搜索