實(shí)現(xiàn) Flutter 模塊嵌入 iOS 原生項(xiàng)目的步驟:
1、在項(xiàng)目根目錄用命令創(chuàng)建 Flutter 模塊:
例如項(xiàng)目目錄這樣子
project/
├── ios_project.xcodeproj
├── ios_project.xcworkspace
├── ios_project/
│ ├── AppDelegate.swift
│ └── ...
├── Podfile
├── Podfile.lock
├── flutter_module/
│ ├── lib/
│ ├── .iOS/
│ └── ...
進(jìn)入到目錄
cd project/
flutter create -t module flutter_module
2、將 Flutter 模塊集成進(jìn) iOS 原生工程
2.1找到pod文件,將Flutter引擎添加到podfile
flutter_application_path = './flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
install_all_flutter_pods(flutter_application_path)
示例:
flutter_application_path = './flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'ios_project' do
use_frameworks!
install_all_flutter_pods(flutter_application_path)
# 其他你的依賴(lài)...
end
post_install do |installer|
flutter_post_install(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings["IPHONEOS_DEPLOYMENT_TARGET"] = "12.0"
end
end
end
2.2安裝 flutter項(xiàng)目依賴(lài)、pod安裝flutter引擎
cd flutter_module # 在 Flutter 模塊目錄下
flutter pub get
cd .. # 回到 iOS 項(xiàng)目目錄
pod install
2.3 iOS項(xiàng)目中添加依賴(lài)及初始化引擎
flutter 官方提供了幾種方式
2.3.1 提前加載引擎預(yù)加載。
import UIKit
import Flutter
import FlutterPluginRegistrant
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var flutterEngine = FlutterEngine(name: "my flutter engine")
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: ViewController())
window?.makeKeyAndVisible()
flutterEngine.run();
// Connects plugins with iOS platform code to this app.
GeneratedPluginRegistrant.register(with: self.flutterEngine);
return true
}
}
import UIKit
import Flutter
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Make a button to call the showFlutter function when pressed.
let button = UIButton(type:UIButton.ButtonType.custom)
button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside)
button.setTitle("Show Flutter!", for: UIControl.State.normal)
button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
}
@objc func showFlutter() {
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterViewController =
FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
}
}
}
這種情況適用于提前加載,適用于大量頁(yè)面,全局引擎,性能好。
用這種方式關(guān)閉flutter頁(yè)面時(shí)要注意 引擎持有的vc 置nil 的問(wèn)題。不然下次打不開(kāi)了。
可參考下面這種方法:
ps: flutter中可用這個(gè)調(diào)用原生 關(guān)閉 下次就無(wú)法打開(kāi)了
SystemNavigator.pop(animated: true);
當(dāng) Flutter 頁(yè)面通過(guò) SystemNavigator.pop() 關(guān)閉時(shí):
Flutter 會(huì)直接調(diào)用宿主平臺(tái)的 pop 機(jī)制(iOS 上會(huì)觸發(fā) dismiss)。
但此時(shí) UIKit 并不會(huì) 將lutterEngine 持有 的VC 置為 nil
如果你用的 FlutterEngine 是單例的,不會(huì)自動(dòng) detach,下一次就打不開(kāi)頁(yè)面了。
可以這樣*/
//
// File.swift
// ios_flutter
//
// Created by liuhao on 2025/5/16.
//
import Foundation
import Flutter
class SafeFlutterViewController: FlutterViewController {
private var methodChannel: FlutterMethodChannel?
override func viewDidLoad() {
super.viewDidLoad()
methodChannel = FlutterMethodChannel(name: "com.example.flutter/close",
binaryMessenger: engine.binaryMessenger)
methodChannel?.setMethodCallHandler { [weak self] call, result in
if call.method == "popFromFlutter" {
self?.dismiss(animated: true) {
self?.detachEngineIfNeeded()
}
result(nil)
}
}
}
deinit {
detachEngineIfNeeded()
}
private func detachEngineIfNeeded() {
if engine.viewController === self {
engine.viewController = nil
print("? FlutterEngine detached.")
}
}
}
flutter代碼
import 'package:flutter/services.dart';
class NativeNavigator {
static const _channel = MethodChannel('com.example.flutter/close');
static Future<void> close() async {
await _channel.invokeMethod('popFromFlutter');
}
}
//封裝一個(gè)專(zhuān)門(mén)用于關(guān)閉的交互
2.3.2 還可以 每次 新的 engine 。
// Existing code omitted.
func showFlutter() {
let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
}
// flutter中可用這個(gè)調(diào)用原生 關(guān)閉
// SystemNavigator.pop(animated: true);
作為前一個(gè)示例的替代,您可以讓其FlutterViewController隱式創(chuàng)建自己的示例,F(xiàn)lutterEngine而無(wú)需提前預(yù)熱。
通常不建議這樣做,因?yàn)閯?chuàng)建FlutterEngine按需加載可能會(huì)在呈現(xiàn)和渲染第一幀之間引入明顯的延遲FlutterViewController。但是,如果 Flutter 屏幕很少顯示,或者沒(méi)有好的啟發(fā)式方法來(lái)確定何時(shí)啟動(dòng) Dart VM,并且 Flutter 不需要在視圖控制器之間持久化狀態(tài),那么按需加載可能會(huì)很有用。
要讓FlutterViewController不存在的現(xiàn)在FlutterEngine,省略構(gòu)造FlutterEngine,并創(chuàng)建FlutterViewController沒(méi)有引擎引用的。
3、其他
如遇到錯(cuò)誤這個(gè)錯(cuò)誤

可嘗試這樣

3、將 Flutter 模塊集成進(jìn) Android 原生工程
工程配置
創(chuàng)建module
flutter create -t module --org com.example flutter_module
JAVA版本要求
Flutter 需要使用 JAVA 11 的特性
在嘗試將 Flutter 模塊項(xiàng)目集成到宿主 Android 應(yīng)用之前,請(qǐng)先確保宿主 Android 應(yīng)用的
build.gradle.kts 文件的 android { } 塊中聲明了以下源兼容性。
build.gradle
android {
// ...
compileOptions {
sourceCompatibility = 11 // The minimum value
targetCompatibility = 11 // The minimum value
}
}
settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
val storageUrl: String = System.getenv("FLUTTER_STORAGE_BASE_URL") ?: "https://storage.googleapis.com"
repositories {
google()
mavenCentral()
maven("$storageUrl/download.flutter.io")
}
}
rootProject.name = "android_study"
include(":app")
// 這段引用 flutter 的 include_flutter.groovy
val flutterInclude = File(rootDir.parentFile, "flutter_module/.android/include_flutter.groovy")
apply(from = flutterInclude)
app/build.gradle
dependencies {
implementation(project(":flutter"))
}
3.2 開(kāi)始集成
FlutterActivity
再 AndroidManifest.xml 中注冊(cè) activity
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/Theme.Android_study"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
//導(dǎo)入頭文件
import io.flutter.embedding.android.FlutterActivity;
//按鈕點(diǎn)擊打開(kāi) FlutterActivity
val btnOpenFlutter = view.findViewById<Button>(R.id.button_open_flutter)
btnOpenFlutter.setOnClickListener {
// 這里使用Fragment的context啟動(dòng)FlutterActivity
val context = requireContext()
val intent = FlutterActivity.createDefaultIntent(requireContext())
startActivity(intent)
}
其他方式請(qǐng)?jiān)斠?jiàn)文檔
https://docs.flutter.cn/add-to-app/android/add-flutter-screen