混合开发:Flutter 嵌入原生应用(Add-to-App)
hardflutterhybridadd-to-appflutter-engineflutter-fragmentflutter-view-controllerplatform-view
为什么需要混合开发?
大型已有原生项目(Android/iOS)通常无法完全重写为 Flutter,但又想部分使用 Flutter 加速开发。Add-to-App 方案允许在原生应用中嵌入一个或多个 Flutter 页面。
架构概览
原生 App(宿主)
├── FlutterEngine(Flutter 运行时)
│ ├── DartVM
│ ├── Flutter Rendering
│ └── Platform Channels
├── FlutterActivity / FlutterFragment(Android)
│ └── 展示 Flutter UI
└── FlutterViewController(iOS)
└── 展示 Flutter UI
关键点:一个 FlutterEngine 对应一个 Dart Isolate,多个 View 可以共享同一 Engine(节约内存),但一个 View 同一时间只能连接一个 Engine。
Android 集成
1. 依赖配置
// settings.gradle.kts
include(":flutter_module")
project(":flutter_module").projectDir = File("../my_flutter")
// app/build.gradle
dependencies {
implementation project(":flutter_module")
}
2. 预热 Engine(关键!)
Engine 启动耗时约 100-200ms,提前预热可避免首次打开 Flutter 页面白屏:
// Application.kt
class MyApplication : Application() {
// 全局缓存 Engine
lateinit var flutterEngine: FlutterEngine
override fun onCreate() {
super.onCreate()
FlutterEngineCache.getInstance().also { cache ->
flutterEngine = FlutterEngine(this).apply {
dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// 可指定初始路由
// navigationChannel.setInitialRoute("/home")
}
cache.put("my_engine", flutterEngine)
}
}
}
3. 启动 Flutter 页面
// 方式一:FlutterActivity(独立 Activity)
val intent = FlutterActivity
.withCachedEngine("my_engine")
.build(this)
startActivity(intent)
// 方式二:FlutterFragment(嵌入到原生布局)
val fragment = FlutterFragment
.withCachedEngine("my_engine")
.build()
supportFragmentManager
.beginTransaction()
.replace(R.id.flutter_container, fragment)
.commit()
iOS 集成
Podfile 配置
# ios/Podfile
flutter_application_path = '../my_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'MyApp' do
install_all_flutter_pods(flutter_application_path)
end
展示 Flutter 页面
// 预热 Engine
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var flutterEngine = FlutterEngine(name: "my_flutter_engine")
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
flutterEngine.run()
GeneratedPluginRegistrant.register(with: flutterEngine)
return true
}
}
// 显示 FlutterViewController
func showFlutterPage() {
let flutterVC = FlutterViewController(
engine: (UIApplication.shared.delegate as! AppDelegate).flutterEngine,
nibName: nil,
bundle: nil
)
present(flutterVC, animated: true)
}
PlatformView —— 在 Flutter 中嵌入原生视图
反向场景:Flutter 页面中需要使用原生视图(如 GoogleMaps、WebView):
// 使用 WebView(webview_flutter 插件)
WebViewWidget(
controller: WebViewController()
..loadRequest(Uri.parse('https://example.com')),
)
PlatformView 实现方式(Android):
- Virtual Display(旧):原生视图渲染到虚拟屏幕,性能差
- Hybrid Composition(推荐):原生 View 叠加在 Flutter Canvas 上,手势处理更自然
// Android 端注册
class WebViewFactory(context: Context) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, id: Int, args: Any?) =
WebPlatformView(context, id, args as? Map<*, *>)
}
// FlutterPlugin 注册
class MyPlugin : FlutterPlugin {
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
binding.platformViewRegistry.registerViewFactory(
"my_web_view",
WebViewFactory(binding.applicationContext)
)
}
}
多 Flutter Engine 注意事项
✅ Engine 预热:1-2 个,按需缓存
❌ 每次导航都创建新 Engine(内存 x 倍数增加)
内存参考:
- 1 个 FlutterEngine ≈ 10-15MB
- 共享 Engine:多个页面公用,节省内存
核心考点
Q:Add-to-App 中 Engine 预热为什么重要?
FlutterEngine 启动时需要初始化 DartVM、加载 Dart 代码,耗时 100-200ms。若不预热,用户点击按钮后会有明显白屏。最佳实践是在 Application.onCreate 时创建并缓存 Engine。
Q:PlatformView 有哪些实现模式,各有什么优缺点?
- Virtual Display:原生视图渲染在虚拟屏幕上,以纹理方式合成到 Flutter Layer,可能存在交互延迟和键盘弹出问题。
- Hybrid Composition:将原生 View 叠加在 Flutter Layer 之上,Android Q+ 使用,手势和键盘处理更自然,但需要额外的 View 层合成开销。Flutter 3.0 后 Android 默认使用 Hybrid Composition。