将flutter打成aar包嵌入到安卓
Flutter Module 打包成 AAR 并集成到 Android 项目
一、创建 Flutter Module
如果你 还没有 Flutter Module
flutter create -t module flutter_module
二、构建 Flutter AAR
执行 AAR 构建命令
flutter build aar
构建产物位置
flutter_module/build/host/outputs/repo/
构建控制台输出
在下面的配置中会用到
1. Open <host>\app\build.gradle
2. Ensure you have the repositories configured, otherwise add them:
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url 'E:\lumosproject\module\lumos\build\host\outputs\repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
3. Make the host app depend on the Flutter module:
dependencies {
debugImplementation 'com.example.lumos:flutter_debug:1.0'
profileImplementation 'com.example.lumos:flutter_profile:1.0'
releaseImplementation 'com.example.lumos:flutter_release:1.0'
}
4. Add the `profile` build type:
android {
buildTypes {
profile {
initWith debug
}
}
}
三、Android 宿主项目集成 AAR
1. 修改MyApp/app/build.gradle
你目前现有的 Android 项目可能支持 mips 或 x86 之类的架构,然而,Flutter 当前仅支持 为 x86_64,armeabi-v7a 和 arm64-v8a 构建预编(AOT)的库。
可以考虑使用 abiFilters 这个 Android Gradle 插件 API 来指定 APK 中支持的架构,从而避免 libflutter.so 无法生成而导致应用运行时崩溃,具体操作如下:
Groovy 版
android {
defaultConfig {
ndk {
// Filter for architectures supported by Flutter
abiFilters "armeabi-v7a", "arm64-v8a", "x86_64"
}
}
}
Kotlin DSL
android {
defaultConfig {
ndk {
// Filter for architectures supported by Flutter
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64")
}
}
}
2. 配置 settings.gradle(.kts)
在国内,需要使用镜像站点代替 storage.googleapis.com。有关镜像的详细信息,参见 在中国网络环境下使用 Flutter 页面。
Groovy 版
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.flutter-io.cn"
repositories {
google()
mavenCentral()
maven {
url 'E:/lumosproject/module/lumos/build/host/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
}
Kotlin DSL
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven("https://storage.googleapis.com/download.flutter.io")
}
}
3. 添加依赖(app/build.gradle)
这里添加的依赖都是build aar 控制台输出的内容
groovy版
dependencies {
debugImplementation 'com.example.untitled:flutter_debug:1.0'
profileImplementation 'com.example.untitled:flutter_profile:1.0'
releaseImplementation 'com.example.untitled:flutter_release:1.0'
}
Kotlin DSL
dependencies {
debugImplementation("com.example.flutter_module:flutter_debug:1.0")
releaseImplementation("com.example.flutter_module:flutter_release:1.0")
add("profileImplementation", "com.example.flutter_module:flutter_profile:1.0")
}
4. 添加profile build type(app/build.gradle)
在buildTypes中添加
groovy版
profile {
initWith debug
}
Kotlin DSL
create("profile") { initWith(getByName("debug")) }
四、Android 启动 Flutter 页面
1.在 AndroidManifest.xml 中添加 FlutterActivity
Flutter 提供了 FlutterActivity,用于在 Android 应用内部展示一个 Flutter 的交互界面。和其他的 Activity 一样,FlutterActivity 必须在项目的 AndroidManifest.xml 文件中注册。将下边的 XML 代码添加到你的 AndroidManifest.xml 文件中的 application 标签内
<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"
/>
2. 加载 FlutterActivity
确保使用如下的语句导入:
import io.flutter.embedding.android.FlutterActivity;
MyButton(onClick = {
startActivity(
FlutterActivity.createDefaultIntent(this)
)
})
@Composable
fun MyButton(onClick: () -> Unit) {
Button(onClick = onClick) {
Text("Launch Flutter!")
}
}
上述的例子假定了你的 Dart 代码入口是调用 main(),并且你的 Flutter 初始路由是 '/'。 Dart 代码入口不能通过 Intent 改变,但是初始路由可以通过 Intent 来修改。下面的例子讲解了如何打开一个自定义 Flutter 初始路由的 FlutterActivity。
MyButton(onClick = {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("/my_route")
.build(this)
)
})
@Composable
fun MyButton(onClick: () -> Unit) {
Button(onClick = onClick) {
Text("Launch Flutter!")
}
}
3. 使用缓存 Engine(推荐)
每一个 FlutterActivity 默认会创建它自己的 FlutterEngine。每一个 FlutterEngine 会有一个明显的预热时间。这意味着加载一个标准的 FlutterActivity 时,在你的 Flutter 交互页面可见之前会有一个短暂的延迟。想要最小化这个延迟时间,你可以在抵达你的 FlutterActivity 之前,初始化一个 FlutterEngine,然后使用这个已经预热好的 FlutterEngine。
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)
}
}
使用
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
)
}
为缓存的 FlutterEngine 设置初始路由
class MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine(this)
// Configure an initial route.
flutterEngine.navigationChannel.setInitialRoute("your/route/here");
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
通过设置路由的方式可以配置缓存引擎在执行 Dart 入口点之前使用自定义初始路由
五、Flutter 与 Android 通信
MethodChannel方式
Flutter
添加到合适的地方
static const methodChannel = MethodChannel('com.bluetoothCharacteristic');
methodChannel.setMethodCallHandler(_handleMethod);
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case 'bluetoothCharacteristic':
// 设置设备写入特征
_device.setWriteCharacteristic(
call.arguments as BluetoothCharacteristic,
);
break;
default:
throw PlatformException(code: 'Unrecognized Method');
}
}
Android
private lateinit var methodChannel: MethodChannel
methodChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.bluetoothCharacteristic"
)
使用
Button(
onClick = {
val intent = FlutterActivity
.withCachedEngine("my_engine_id")
.build(activity)
methodChannel.invokeMethod(
"bluetoothCharacteristic",
"00002a37-0000-1000-8000-00805f9b34fb"
);
activity.startActivity(intent)
},
modifier = Modifier.padding(16.dp)
) {
Text("跳转到flutter页面")
}
MethodChannel 中的 com.bluetoothCharacteristic 和methodChannel.invokeMethod中的bluetoothCharacterstic必须和 Flutter 中保持一致,否则接收不到数据。
EventChannel方式
EventChannel的使用方式大致和MethodChannel相同,我这里就把主要代码复制下来了。
class MainActivity : ComponentActivity() {
private lateinit var flutterEngine: FlutterEngine
private lateinit var eventChannel: EventChannel
private var eventDataSink: EventChannel.EventSink? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// 创建 FlutterEngine
flutterEngine = FlutterEngine(this)
//设置初始路由
flutterEngine.navigationChannel.setInitialRoute("/settings")
// 启动 Flutter 引擎
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// 设置 EventChannel 用于实时数据传输
eventChannel = EventChannel(flutterEngine.dartExecutor, "com.example.flutter_aar_demo/event_channel")
eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventDataSink = events
startRealTimeDataTransmission()
}
override fun onCancel(arguments: Any?) {
eventDataSink = null
}
})
// 缓存 FlutterEngine
FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine)
}
我这里的安卓使用的是groovy的创建方式Kotlin DSL的方式是我直接从官网复制的代码可能有不正确的地方具体请查看将 Flutter module 集成到 Android 项目。