Flutter 混合开发实战:与原生 Android/iOS 工程的无缝集成
在跨平台开发的实际场景中,纯 Flutter 应用往往无法满足所有业务需求 —— 例如调用平台专属的硬件能力、复用已有原生代码资产、接入仅支持原生的第三方 SDK。此时,Flutter 混合开发就成为了最优解。它允许开发者在原生 Android(Kotlin/Java)或 iOS(Swift/Objective-C)工程中嵌入 Flutter 模块,实现 “原生 + 跨平台” 的协同开发模式。
本文将从混合开发架构设计、原生与 Flutter 通信、页面路由管理、资源与权限共享四个核心维度,结合实战案例讲解 Flutter 与原生工程的无缝集成方案,帮助开发者高效落地混合开发项目。
一、混合开发核心架构与适用场景
1. 两种核心集成架构
Flutter 混合开发有两种主流架构模式,开发者可根据项目需求选择:
| 架构模式 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 原生工程嵌入 Flutter 模块 | 以原生工程为主工程,Flutter 作为模块被引入 | 可充分复用原生代码和 SDK,原生能力不受限 | Flutter 页面需依赖原生工程编译运行 | 已有成熟原生应用,需新增跨平台页面 |
| Flutter 工程接入原生插件 | 以 Flutter 工程为主工程,通过自定义插件封装原生能力 | 跨平台代码占比高,开发效率高 | 原生能力需封装为插件,有一定开发成本 | 新应用开发,需少量调用原生能力 |
本文重点讲解原生工程嵌入 Flutter 模块的方案,这也是企业级项目中最常用的混合开发模式。
2. 混合开发适用场景
- 复用原生代码资产:已有原生应用的业务逻辑(如支付、登录)无需重构,直接供 Flutter 调用;
- 调用平台专属能力:如 Android 的指纹识别、iOS 的 ARKit、蓝牙设备通信等 Flutter 官方插件未覆盖的功能;
- 接入原生第三方 SDK:如某些硬件厂商提供的 SDK 仅支持原生开发,需通过混合开发集成;
- 渐进式迁移:将原生应用的页面逐步替换为 Flutter 页面,降低重构风险。
二、实战准备:环境配置与工程创建
1. 前置环境
- Flutter 3.20+、Android Studio Hedgehog+、Xcode 15+;
- 原生工程:Android(Kotlin)或 iOS(Swift)工程;
- 开启 Flutter 混合开发支持:执行
flutter config --enable-android-embedding-v2和flutter config --enable-ios-embedding-v2。
2. 创建 Flutter 模块
在原生工程同级目录下,创建 Flutter 模块:
# 创建Flutter模块(注意:不要使用flutter create app,避免创建全量工程)
flutter create -t module flutter_module
创建完成后,得到一个标准的 Flutter 模块工程,核心目录结构如下:
flutter_module/
├── android/ # Android平台配置
├── ios/ # iOS平台配置
├── lib/ # Flutter业务代码
├── pubspec.yaml # 依赖配置
└── .android/ # 隐藏的Android构建目录
三、步骤 1:Android 原生工程集成 Flutter 模块
1. 配置 settings.gradle
在原生 Android 工程的settings.gradle中,添加 Flutter 模块的依赖:
// 引入Flutter模块
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
2. 配置 app/build.gradle
在原生工程的app/build.gradle中,添加 Flutter 依赖:
dependencies {
// 其他原生依赖
implementation project(':flutter')
}
3. 原生页面嵌入 Flutter 页面
在 Android 原生工程中,通过FlutterActivity或FlutterFragment嵌入 Flutter 页面。
方式 1:通过 FlutterActivity 启动独立 Flutter 页面
// 原生Kotlin代码:MainActivity.kt
import io.flutter.embedding.android.FlutterActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 点击按钮跳转到Flutter页面
findViewById<Button>(R.id.btn_flutter).setOnClickListener {
val intent = FlutterActivity
.withNewEngine()
.initialRoute("/home") // 指定Flutter初始路由
.build(this)
startActivity(intent)
}
}
}
方式 2:通过 FlutterFragment 嵌入 Flutter 页面(与原生页面共存)
// 原生Kotlin代码:FlutterContainerActivity.kt
import io.flutter.embedding.android.FlutterFragment
class FlutterContainerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter_container)
// 创建FlutterFragment
val flutterFragment = FlutterFragment.withNewEngine()
.initialRoute("/profile")
.build()
// 将FlutterFragment添加到原生布局的FrameLayout中
supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, flutterFragment)
.commit()
}
}
对应的原生布局文件activity_flutter_container.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="原生头部导航栏"
android:gravity="center"/>
<FrameLayout
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
四、步骤 2:iOS 原生工程集成 Flutter 模块
1. 通过 CocoaPods 集成 Flutter 模块
在 iOS 原生工程的Podfile中添加 Flutter 模块依赖:
# 指定Flutter模块路径
flutter_application_path = '../flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'NativeiOSApp' do
# 引入Flutter相关Pods
install_all_flutter_pods(flutter_application_path)
# 其他原生依赖
end
执行pod install,完成 Flutter 模块的集成。
2. 原生页面嵌入 Flutter 页面
方式 1:通过 FlutterViewController 启动 Flutter 页面
// 原生Swift代码:ViewController.swift
import UIKit
import Flutter
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 点击按钮跳转到Flutter页面
let btn = UIButton(type: .system)
btn.setTitle("跳转到Flutter页面", for: .normal)
btn.addTarget(self, action: #selector(openFlutterPage), for: .touchUpInside)
btn.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
view.addSubview(btn)
}
@objc func openFlutterPage() {
// 创建Flutter引擎
let flutterEngine = FlutterEngine(name: "my_flutter_engine")
flutterEngine.run(withEntrypoint: nil, initialRoute: "/home")
// 创建FlutterViewController
let flutterVC = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterVC, animated: true)
}
}
方式 2:将 Flutter 视图嵌入原生 View(与原生 UI 共存)
// 原生Swift代码:FlutterContainerVC.swift
import UIKit
import Flutter
class FlutterContainerVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 原生头部导航栏
let navBar = UILabel(frame: CGRect(x: 0, y: 80, width: view.bounds.width, height: 50))
navBar.text = "原生头部导航栏"
navBar.textAlignment = .center
view.addSubview(navBar)
// 创建Flutter引擎
let flutterEngine = FlutterEngine(name: "my_flutter_engine")
flutterEngine.run(withEntrypoint: nil, initialRoute: "/profile")
// 创建Flutter视图
let flutterView = FlutterView(frame: CGRect(x: 0, y: 130, width: view.bounds.width, height: view.bounds.height - 130))
let flutterVC = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
flutterVC.view = flutterView
addChild(flutterVC)
view.addSubview(flutterView)
}
}
五、核心能力:原生与 Flutter 的双向通信
混合开发的关键是原生与 Flutter 的双向数据交互,Flutter 提供了MethodChannel(方法调用)、EventChannel(事件流)、BasicMessageChannel(消息传递)三种通信方式,其中MethodChannel是最常用的。
1. 通信原理
- MethodChannel:基于异步消息传递机制,支持原生与 Flutter 互相调用方法并返回结果;
- 通信标识:两端必须使用相同的
Channel Name(如com.example.flutter/native),否则无法建立通信; - 数据类型:支持基础数据类型(int、String、bool)和集合类型(List、Map),复杂对象需转换为 Map 传递。
2. 实战:Flutter 调用原生能力(以获取设备信息为例)
步骤 1:原生端实现方法(Android)
// 原生Kotlin代码:在FlutterActivity中配置MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.flutter/device_info"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 注册MethodChannel
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"getDeviceModel" -> {
// 获取设备型号
val deviceModel = android.os.Build.MODEL
result.success(deviceModel)
}
else -> {
result.notImplemented()
}
}
}
}
}
步骤 2:原生端实现方法(iOS)
// 原生Swift代码:在FlutterViewController中配置MethodChannel
class ViewController: FlutterViewController {
let CHANNEL = "com.example.flutter/device_info"
override func viewDidLoad() {
super.viewDidLoad()
// 注册MethodChannel
let methodChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: flutterEngine!.binaryMessenger)
methodChannel.setMethodCallHandler { [weak self] call, result in
guard let self = self else { return }
if call.method == "getDeviceModel" {
// 获取设备型号
let deviceModel = UIDevice.current.modelName
result(deviceModel)
} else {
result(FlutterMethodNotImplemented)
}
}
}
}
// 扩展:获取iOS设备具体型号
extension UIDevice {
var modelName: String {
// 实现设备型号获取逻辑,此处省略
return "iPhone 15 Pro"
}
}
步骤 3:Flutter 端调用原生方法
// Flutter Dart代码
import 'package:flutter/services.dart';
class DeviceInfoService {
static const MethodChannel _channel = MethodChannel('com.example.flutter/device_info');
// 调用原生方法获取设备型号
static Future<String> getDeviceModel() async {
try {
final String model = await _channel.invokeMethod('getDeviceModel');
return model;
} on PlatformException catch (e) {
return "获取设备信息失败:${e.message}";
}
}
}
// 在Flutter页面中使用
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('设备信息')),
body: Center(
child: FutureBuilder<String>(
future: DeviceInfoService.getDeviceModel(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('设备型号:${snapshot.data}');
} else {
return const CircularProgressIndicator();
}
},
),
),
);
}
}
3. 实战:原生调用 Flutter 方法
原生调用 Flutter 方法的流程与上述相反,核心是 Flutter 端注册 MethodChannel 并监听方法调用。
// Flutter Dart代码:注册MethodChannel,监听原生调用
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static const MethodChannel _channel = MethodChannel('com.example.flutter/native_call');
String _message = "等待原生调用";
@override
void initState() {
super.initState();
// 监听原生方法调用
_channel.setMethodCallHandler((call) async {
if (call.method == "showMessage") {
String msg = call.arguments as String;
setState(() {
_message = msg;
});
return "Flutter已收到消息";
} else {
throw MissingPluginException();
}
});
}
@override
Widget build(BuildContext context) {
return Center(child: Text(_message));
}
}
六、进阶优化:混合开发核心问题解决方案
1. 路由管理:原生与 Flutter 页面跳转
在混合开发中,页面跳转分为三种场景:
- 原生→Flutter:通过
FlutterActivity/FlutterViewController,指定initialRoute; - Flutter→原生:通过 MethodChannel 调用原生方法,由原生端处理跳转;
- Flutter→Flutter:使用 Flutter 自带的
Navigator管理。
示例:Flutter 跳转到原生页面
// Flutter端通过MethodChannel触发跳转
static Future<void> openNativeSettingPage() async {
await _channel.invokeMethod('openSettingPage');
}
// Android原生端处理跳转
when (call.method) {
"openSettingPage" -> {
val intent = Intent(this, SettingActivity::class.java)
startActivity(intent)
result.success(null)
}
}
2. 资源共享:原生与 Flutter 资源互通
- 图片资源:Flutter 可通过
AssetImage访问自身assets目录资源,原生资源需通过 MethodChannel 传递路径; - 字符串资源:建议使用统一的远程配置,避免两端重复维护;
- 主题样式:通过 MethodChannel 同步原生主题(如深色模式)到 Flutter 端。
3. 权限管理:统一权限申请流程
- 原生权限:由原生端统一申请,Flutter 通过 MethodChannel 查询权限状态;
- 权限回调:原生端申请权限后,通过 EventChannel 将结果同步到 Flutter 端。
4. 性能优化:避免 Flutter 引擎重复初始化
- 引擎复用:在原生工程中创建全局 Flutter 引擎,避免每次跳转 Flutter 页面都初始化引擎;
- 懒加载引擎:在应用启动后延迟初始化 Flutter 引擎,减少启动耗时。
七、总结
Flutter 混合开发的核心是 **“原生为主,Flutter 为辅”,通过合理的架构设计和通信机制,实现原生与 Flutter 的无缝协同。在实际项目中,开发者需要重点关注工程集成、双向通信、路由管理 ** 三个核心环节,同时结合项目特点选择合适的集成方案。
混合开发既保留了原生应用的性能和平台能力,又发挥了 Flutter 跨平台开发的效率优势,是企业级应用实现 “渐进式跨平台迁移” 的最佳路径。
欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)