混合开发是指 Flutter 与原生 Android、iOS 一起混合开发的 App。混合开发是为了充分利用 Flutter 在纯界面开发上的优势,以及避免Flutter对底层硬件强相关的开发支持的不足。这里以在Android原生项目中嵌入Flutter为例,iOS 的混合开发可以看 这里
创建 flutter 模块并配置
我们可以使用如下命令来创建 Flutter 模块,其中 flutter_module 是 Flutter 模块的名字。
lua
flutter create -t module flutter_module
创建完成后,我们需要在Android原生项目的 settings.gradle 文件中关联 Flutter 模块,如下所示:
kts 方式使用如下配置
ini
include(":app")
val filePath = settingsDir.toString() + "/flutter_module/.android/include_flutter.groovy"
apply(from = File(filePath))
groovy 配置方式如下:
php
include(":app")
setBinding(new Binding([gradle: this]))
def filePath = settingsDir.toString() + "/flutter_module/.android/include_flutter.groovy"
apply from: filePath
再到 app 目录下的 build.gradle 文件中添加 Flutter 模块,如下所示:
scss
dependencies {
// kts
implementation(project(":flutter"))
// groovy
implementation project(':flutter')
//...其他代码
}
启动 FlutterActivity
Flutter 提供了 FlutterActivity,让我们可以直接启动Flutter 界面。首先我们需要在 AndroidManifest.xml 文件中注册。代码示例如下:
ini
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
然后我们创建一个 FlutterEngine,FlutterEngine
是 Flutter 的核心,它用于加载执行 Dart 代码,并参与构建和渲染 UI 界面。创建代码如下:
kotlin
class MyApplication : Application() {
lateinit var flutterEngine: FlutterEngine
override fun onCreate() {
super.onCreate()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine(this)
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the FlutterEngine to be used by FlutterActivity.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
然后我们就可以在代码中启动 FlutterActivity 了,代码示例如下:
less
button.setOnClickListener {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this))
}
效果如下图所示:

FlutterFragment
处理 FlutterActivity
外,Flutter 还提供了 FlutterFragment
,方便我们把 Flutter 界面作为 Fragment 在 App 中使用。代码示例如下:
kotlin
class FlutterFragmentActivity : FragmentActivity() {
companion object {
private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
}
private var flutterFragment: FlutterFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_activity_layout)
val fragmentManager: FragmentManager = supportFragmentManager
// 尝试查找现有的 FlutterFragment,以防这不是第一次调用 onCreate() 方法。
flutterFragment = fragmentManager
.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
// 如果 FlutterFragment 不存在,则创建并附加一个新的 FlutterFragment。
if (flutterFragment == null) {
var newFlutterFragment = FlutterFragment.createDefault()
flutterFragment = newFlutterFragment
fragmentManager
.beginTransaction()
.add(
R.id.fragment_container,
newFlutterFragment,
TAG_FLUTTER_FRAGMENT
)
.commit()
}
}
override fun onPostResume() {
super.onPostResume()
// 调用 FlutterFragment 的 onPostResume() 方法
flutterFragment?.onPostResume()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// 调用 FlutterFragment 的 onNewIntent() 方法,并传入新的意图
flutterFragment?.onNewIntent(intent)
}
override fun onBackPressed() {
super.onBackPressed()
// 调用 FlutterFragment 的 onBackPressed() 方法
flutterFragment?.onBackPressed()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// 调用 FlutterFragment 的 onRequestPermissionsResult() 方法,传入请求码、权限数组和授权结果数组
flutterFragment?.onRequestPermissionsResult(
requestCode,
permissions,
grantResults
)
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
// 调用 FlutterFragment 的 onActivityResult() 方法,传入请求码、结果码和数据意图
flutterFragment?.onActivityResult(
requestCode,
resultCode,
data
)
}
override fun onUserLeaveHint() {
// 调用 FlutterFragment 的 onUserLeaveHint() 方法
flutterFragment?.onUserLeaveHint()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
// 调用 FlutterFragment 的 onTrimMemory() 方法,传入内存清理级别
flutterFragment?.onTrimMemory(level)
}
}
效果如下图所示:

更多关于 FlutterFragment 的用法可以看 Flutter 中文文档
嵌入 Flutter 界面
我们还可以通过 FlutterView
来将 Flutter 界面嵌入某个 View 中。代码示例如下:
kotlin
class FlutterViewActivity : FragmentActivity() {
private var flutterEngine: FlutterEngine? = FlutterEngineCache.getInstance().get("my_engine_id")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_flutter_view)
val flutterView = FlutterView(this)
val lp = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
val flContainer = findViewById<FrameLayout>(R.id.container)
flContainer.addView(flutterView, lp)
flutterEngine?.let {
flutterView.attachToFlutterEngine(it)
}
}
override fun onResume() {
super.onResume()
flutterEngine?.lifecycleChannel?.appIsResumed()
}
override fun onPause() {
super.onPause()
flutterEngine?.lifecycleChannel?.appIsInactive()
}
override fun onStop() {
super.onStop()
flutterEngine?.lifecycleChannel?.appIsPaused()
}
}
效果如下图所示:

数据传输
Flutter 提供了 MethodChannel 来与 Android 进行数据传输。下面我们来看看如何使用这个类来实现双方的通信。
在 Android 中的代码如下所示:
kotlin
private var flutterEngine: FlutterEngine? = FlutterEngineCache.getInstance().get("my_engine_id")
// MethodChannel 是一个双向通信通道,用于在 Flutter 和 Android 之间传递消息
private val methodChannel by lazy {
val engine = flutterEngine ?: return@lazy null
MethodChannel(engine.dartExecutor.binaryMessenger, CHANNEL)
}
private fun listenFlutterEvent() {
// 处理来自 Flutter 的方法调用
methodChannel?.setMethodCallHandler { call, result ->
when (call.method) {
// flutter 传输数据过来
"getFlutterVersion" -> {
Log.d(TAG, "getFlutterVersion ${call.argument<String>("flutterVersion")}")
}
// flutter 调用 Android 的方法
"getPlatformVersion" -> {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else -> result.notImplemented()
}
}
}
其中 setMethodCallHandler 方法用于设置处理来自 Flutter 的方法调用回调。在回调中,我们可以使用 call.argument
方法获取从 Flutter 中传入的值。也可以使用 result.success
设置返回值给 Flutter。
Flutter 端的代码示例如下:
csharp
static const String CHANNEL = "com.example.flutterandroidproject/channel";
void getPlatformVersion() async {
// 通过 MethodChannel 获取 platform 的版本信息
String platformVersion = await MethodChannel(CHANNEL).invokeMethod('getPlatformVersion');
setState(() {
_platformVersion = platformVersion;
});
}
void getFlutterVersion() async {
// 通过 MethodChannel 传递给 Android 当前 flutter 版本信息
await MethodChannel(CHANNEL).invokeMethod('getFlutterVersion', {"flutterVersion": "1.0.0"});
}
其中 invokeMethod 方法,用于从 Flutter 调用 Android 方法。可以看到我们可以通过获取 invokeMethod 的返回值来获取 Android 端返回的数据。
Gradle异常处理
Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by plugin 'dev.flutter.flutter-gradle-plugin'

如果发生了如图所示的构建问题,需要将根目录下的 setting.gradle.kts 中的 repositoriesMode
改成 RepositoriesMode.PREFER_SETTINGS
,代码示例如下:
bash
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
...
}
}
Could not find io.flutter:flutter_embedding_debug:1.0.0-18b71d647a292a980abb405ac7d16fe1f0b20434.

如果遇到这个错误,我们需要在项目的 setting.gradle.kts 中增加如下代码:
ini
pluginManagement {
repositories {
maven { url = uri("https://storage.googleapis.com/download.flutter.io") }
...
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
maven { url = uri("https://storage.googleapis.com/download.flutter.io") }
...
}
}