混合开发模式(Add-to-App)
2025-05-07 本文已影响0人
猿人
实现 Flutter 模块嵌入 iOS 原生项目的步骤:
1、在项目根目录用命令创建 Flutter 模块:
例如项目目录这样子
project/
├── ios_project.xcodeproj
├── ios_project.xcworkspace
├── ios_project/
│ ├── AppDelegate.swift
│ └── ...
├── Podfile
├── Podfile.lock
├── flutter_module/
│ ├── lib/
│ ├── .iOS/
│ └── ...
进入到目录
cd project/
flutter create -t module flutter_module
2、将 Flutter 模块集成进 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)
# 其他你的依赖...
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项目依赖、pod安装flutter引擎
cd flutter_module # 在 Flutter 模块目录下
flutter pub get
cd .. # 回到 iOS 项目目录
pod install
2.3 iOS项目中添加依赖及初始化引擎
flutter 官方提供了几种方式
2.3.1 提前加载引擎预加载。
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)
}
}
}
这种情况适用于提前加载,适用于大量页面,全局引擎,性能好。
用这种方式关闭flutter页面时要注意 引擎持有的vc 置nil 的问题。不然下次打不开了。
可参考下面这种方法:
ps: flutter中可用这个调用原生 关闭 下次就无法打开了
SystemNavigator.pop(animated: true);
当 Flutter 页面通过 SystemNavigator.pop() 关闭时:
Flutter 会直接调用宿主平台的 pop 机制(iOS 上会触发 dismiss)。
但此时 UIKit 并不会 将lutterEngine 持有 的VC 置为 nil
如果你用的 FlutterEngine 是单例的,不会自动 detach,下一次就打不开页面了。
可以这样*/
//
// 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');
}
}
//封装一个专门用于关闭的交互
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中可用这个调用原生 关闭
// SystemNavigator.pop(animated: true);
作为前一个示例的替代,您可以让其FlutterViewController隐式创建自己的示例,FlutterEngine而无需提前预热。
通常不建议这样做,因为创建FlutterEngine按需加载可能会在呈现和渲染第一帧之间引入明显的延迟FlutterViewController。但是,如果 Flutter 屏幕很少显示,或者没有好的启发式方法来确定何时启动 Dart VM,并且 Flutter 不需要在视图控制器之间持久化状态,那么按需加载可能会很有用。
要让FlutterViewController不存在的现在FlutterEngine,省略构造FlutterEngine,并创建FlutterViewController没有引擎引用的。
3、其他
如遇到错误这个错误
截屏2025-05-08 15.57.24.png
可尝试这样
image.png