Flutter 与原生混合开发
特性将 Flutter 集成到原生应用 (Add-to-App)在 Flutter 中嵌入原生视图 (Platform Views)主导方原生 App是主导,Flutter 是一个或多个页面/模块。是主导,原生视图是嵌入的组件。适用场景渐进式重构、为现有大型应用添加新功能。复用已有的复杂原生组件(地图、WebView、专业SDK)。核心技术管理、、平台通道UiKitView、平台通道主要挑战引擎生
Flutter 与原生混合开发是一个非常重要且实用的主题。它允许团队在不完全重写现有应用的情况下,逐步引入 Flutter,或者在 Flutter 应用中利用强大的原生生态。
Flutter 混合开发主要有两种模式:
-
将 Flutter 集成到现有原生应用 (Add-to-App):这是最常见的混合模式。你有一个成熟的 Android 或 iOS 应用,希望用 Flutter 来开发新的功能模块、页面,甚至是重构部分旧页面。
-
在 Flutter 应用中嵌入原生视图 (Platform Views):你的应用主体是 Flutter,但需要嵌入一个无法或很难用 Flutter 实现的原生组件,例如 Google Maps、特定的广告 SDK 视图、或者一个复杂的原生图表库。
下面我们详细探讨这两种模式。
模式一:将 Flutter 集成到现有原生应用 (Add-to-App)
这是 Flutter 官方重点支持的方案。原生应用作为“宿主”,Flutter 模块作为“客人”被嵌入其中。
核心概念:FlutterEngine
FlutterEngine 是 Flutter 运行的核心。它包含了 Dart VM、你的 Dart 代码、以及 Flutter 的所有运行时服务。管理 FlutterEngine 的生命周期是混合开发性能的关键。
-
冷启动 (Cold Start):每次进入 Flutter 页面时都创建一个新的
FlutterEngine。这会导致明显的延迟和白屏,因为引擎需要初始化、加载 Dart 代码并渲染第一帧。应尽量避免。 -
热启动 (Warm Start):提前创建并“预热”一个
FlutterEngine实例,并将其缓存起来。当需要展示 Flutter 页面时,直接使用这个缓存的引擎。这能极大提升 Flutter 页面的打开速度,带来近乎原生的体验。
实现流程
1. 创建 Flutter Module
首先,你需要创建一个特殊的 Flutter 项目,它不是一个完整的 App,而是一个可以被原生项目依赖的模块。
flutter create --template module my_flutter_module
这个命令会创建一个 my_flutter_module 目录,其中包含了 Flutter 代码,以及一个隐藏的 .android 和 .ios 目录,它们包含了将 Flutter 模块打包成原生库(AAR/Framework)的脚本。
2. 原生项目集成
在 Android 中 (Kotlin/Java):
-
依赖 Flutter 模块: 在原生项目的
settings.gradle中,添加对 Flutter 模块的引用。Gradle 会自动处理编译和打包。 -
预热 FlutterEngine: 在你的
Application类中,创建并缓存一个FlutterEngine实例。
```kotlin
class MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
// 实例化 FlutterEngine
flutterEngine = FlutterEngine(this)
// 预热引擎,执行 Dart 代码的 main() 函数
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// 缓存引擎以供复用
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
```
-
启动 Flutter 页面: 从你的原生
Activity中,使用缓存的引擎启动FlutterActivity。
```kotlin
// 在原生 Button 的点击事件中
button.setOnClickListener {
// 使用 withCachedEngine 来获取预热好的引擎
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
)
}
```
在 iOS 中 (Swift):
-
依赖 Flutter 模块: 使用 CocoaPods 将 Flutter 模块集成到你的 iOS 项目中。在
Podfile中添加相关脚本。 -
预热 FlutterEngine: 在你的
AppDelegate.swift中,创建并缓存一个FlutterEngine。
```swift
import Flutter
import FlutterPluginRegistrant
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var flutterEngine = FlutterEngine(name: "my_engine_id")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 预热引擎
flutterEngine.run()
// 注册插件
GeneratedPluginRegistrant.register(with: self.flutterEngine)
return true
}
}
```
-
展示 Flutter 页面: 从你的原生
ViewController中,创建一个FlutterViewController并展示它。
```swift
// 在原生 Button 的点击事件中
@IBAction func showFlutter() {
// 获取 AppDelegate 中的引擎实例
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let flutterEngine = appDelegate.flutterEngine
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
// 可以通过 present 或 push 的方式展示
present(flutterViewController, animated: true, completion: nil)
}
```
3. 双向通信
原生和 Flutter 模块之间需要频繁通信,例如传递初始化数据(用户 Token、页面参数)、同步状态、回传结果等。这完全依赖于我们之前讨论的平台通道 (Platform Channels)。
-
MethodChannel是最常用的方式。例如,原生部分可以在启动 Flutter 页面前,通过MethodChannel告诉 Flutter 当前的用户信息。Flutter 页面关闭前,可以通过MethodChannel将操作结果返回给原生。 -
Pigeon在混合开发中尤其推荐,因为它可以保证原生和 Flutter 两端数据模型的类型安全,减少因手写通道代码而出错的概率。
模式二:在 Flutter 应用中嵌入原生视图 (Platform Views)
当你的 App 主体是 Flutter,但需要嵌入一个原生视图时,使用此模式。
核心概念:AndroidView 和 UiKitView
Flutter 提供了这两个 Widget,它们可以在 Flutter 的 Widget 树中开辟一块“区域”,专门用来渲染一个原生的 View/UIView。
实现原理
-
Flutter 端: 在你的 Widget 树中放置
AndroidView或UiKitView。你需要提供一个唯一的viewType字符串,用于标识你想创建哪种原生视图。 -
原生端:
-
你需要创建一个工厂类 (PlatformViewFactory)。
-
这个工厂类注册到 Flutter 插件系统中,并与 Flutter 端的
viewType关联起来。 -
当 Flutter 渲染到
AndroidView/UiKitView时,它会通知原生端,原生端通过工厂类创建一个对应的原生 View 实例。 -
Flutter 会将这个原生 View 合成到最终的屏幕画面中。
-
示例:在 Flutter 中嵌入一个原生的 TextView (Android)
1. Flutter 端 (Dart):
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
Widget build(BuildContext context) {
const String viewType = '<platform-view-type>';
final Map<String, dynamic> creationParams = <String, dynamic>{};
if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
return Text('$defaultTargetPlatform is not yet supported by this plugin.');
}
2. Android 端 (Kotlin):
首先,创建一个 PlatformViewFactory:
// NativeTextViewFactory.kt
import android.content.Context
import android.graphics.Color
import android.view.View
import android.widget.TextView
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class NativeTextViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NativeTextView(context, viewId, creationParams)
}
}
internal class NativeTextView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val textView: TextView
override fun getView(): View {
return textView
}
override fun dispose() {}
init {
textView = TextView(context)
textView.textSize = 24f
textView.setBackgroundColor(Color.rgb(200, 200, 200))
textView.text = "Rendered from Android native code"
}
}
然后,在你的插件注册类中注册这个工厂:
// YourPlugin.kt (implements FlutterPlugin)
override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
binding
.platformViewRegistry
.registerViewFactory("<platform-view-type>", NativeTextViewFactory())
}
性能和注意事项
-
性能开销: Platform Views 会带来一定的性能开销,因为它们打破了 Flutter 的统一渲染模式,需要进行额外的视图合成。在列表或动画中大量使用可能会导致掉帧。
-
触摸事件: 触摸事件的传递需要从 Flutter 转发到原生视图,可能会有轻微延迟。
-
Android 上的合成模式:
-
Hybrid Composition (混合合成): 默认模式,性能更好,但要求 Android API 19+。
-
Virtual Display (虚拟显示): 旧模式,兼容性更好,但性能较差,且对触摸事件和可访问性支持有限。
-
总结对比
| 特性 | 将 Flutter 集成到原生应用 (Add-to-App) | 在 Flutter 中嵌入原生视图 (Platform Views) |
|---|---|---|
| 主导方 | 原生 App 是主导,Flutter 是一个或多个页面/模块。 | Flutter App 是主导,原生视图是嵌入的组件。 |
| 适用场景 | 渐进式重构、为现有大型应用添加新功能。 | 复用已有的复杂原生组件(地图、WebView、专业SDK)。 |
| 核心技术 | FlutterEngine 管理、FlutterActivity/FlutterViewController、平台通道 |
AndroidView/UiKitView、PlatformViewFactory、平台通道 |
| 主要挑战 | 引擎生命周期管理、路由和数据同步、统一导航体验。 | 性能开销、手势冲突、键盘处理。 |
选择哪种混合开发模式,完全取决于你的项目现状和业务需求。Flutter 提供了强大而灵活的工具来应对这两种场景。
更多推荐



所有评论(0)