从 Flutter 应用程序启动 Jetpack Compose Activity
了解如何在 Flutter 应用中启动原生 Android Activity。
原生 Android Activity 允许你启动由 Android 平台完全运行的全屏界面。在这些视图中,你只需编写 Kotlin 代码(尽管它们可以与 Dart 代码进行消息传递),并且可以访问 Android 原生的全部功能。
添加此功能需要对 Flutter 应用及其内部生成的 Android 应用进行多项更改。在 Flutter 端,你需要创建一个新的平台 MethodChannel 并调用其 invokeMethod 方法。在 Android 端,你需要注册一个匹配的原生 MethodChannel 来接收来自 Dart 的信号,然后启动一个新的 Activity。请记住,所有的 Flutter 应用(在 Android 上运行时)都存在于一个完全被 Flutter 应用占用的 Android Activity 中。因此,正如你在代码示例中看到的那样,原生 MethodChannel 回调的作用是启动第二个 Activity。
并非所有的 Android Activity 都使用 Jetpack Compose,但本教程假设你想使用 Compose。
在 Dart 侧
#在 Dart 端,创建一个 Method Channel,并在特定的用户交互(例如点击按钮)时调用它。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// SECTION 1: START COPYING HERE
const platformMethodChannel = MethodChannel(
// Note: You can change this string value, but it must match
// the `CHANNEL` attribute in the next step.
'com.example.flutter_android_activity',
);
// SECTION 1: END COPYING HERE
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
// SECTION 2: START COPYING HERE
void _launchAndroidActivity() {
platformMethodChannel.invokeMethod(
// Note: You can change this value, but it must match
// the `call.method` value in the next section.
'launchActivity',
// Note: You can pass any primitive data types you like.
// To pass complex types, use package:pigeon to generate
// matching Dart and Kotlin classes that share serialization logic.
{'message': 'Hello from Flutter'},
);
}
// SECTION 2: END COPYING HERE
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: const Center(child: Text('Hello World!')),
floatingActionButton: FloatingActionButton(
// SECTION 3: Call `_launchAndroidActivity` somewhere.
onPressed: _launchAndroidActivity,
// SECTION 3: End
tooltip: 'Launch Android activity',
child: const Icon(Icons.launch),
),
),
);
}
}
Dart 代码和 Kotlin 代码之间有 3 个必须匹配的重要值
- 通道名称(在本示例中,值为
"com.example.flutter_android_activity")。 - 方法名称(在本示例中,值为
"launchActivity")。 - Dart 传递的数据结构与 Kotlin 预期接收的数据结构。在本例中,数据是一个包含单个
"message"键的 Map。
在 Android 端
#你需要修改生成的 Android 应用中的 4 个文件,以便为启动新的 Compose Activity 做好准备。
第一个需要修改的文件是 android/app/build.gradle。
-
将以下内容添加到现有的
android块中android/app/build.gradle.ktskotlinandroid { // Begin adding here buildFeatures { compose = true } composeOptions { // https://developer.android.com.cn/jetpack/androidx/releases/compose-kotlin kotlinCompilerExtensionVersion = "1.4.8" } // End adding here }android/app/build.gradlegroovyandroid { // Begin adding here buildFeatures { compose true } composeOptions { // https://developer.android.com.cn/jetpack/androidx/releases/compose-kotlin kotlinCompilerExtensionVersion = "1.4.8" } // End adding here }访问代码片段中的 developer.android.com 链接,并根据需要调整
kotlinCompilerExtensionVersion。只有在flutter run过程中出现错误且错误信息提示你机器上安装的版本时,才需要执行此操作。 -
接下来,在文件底部、根级别添加以下块
android/app/build.gradle.ktskotlindependencies { implementation("androidx.core:core-ktx:1.10.1") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.activity:activity-compose") implementation(platform("androidx.compose:compose-bom:2024.06.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material:material") implementation("androidx.compose.material3:material3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation(platform("androidx.compose:compose-bom:2024.06.00")) androidTestImplementation("androidx.compose.ui:ui-test-junit4") androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") }android/app/build.gradlegroovydependencies { implementation "androidx.core:core-ktx:1.10.1" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1" implementation "androidx.activity:activity-compose" implementation platform("androidx.compose:compose-bom:2024.06.00") implementation "androidx.compose.ui:ui" implementation "androidx.compose.ui:ui-graphics" implementation "androidx.compose.ui:ui-tooling-preview" implementation "androidx.compose.material:material" implementation "androidx.compose.material3:material3" testImplementation "junit:junit:4.13.2" androidTestImplementation "androidx.test.ext:junit:1.1.5" androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" androidTestImplementation platform("androidx.compose:compose-bom:2023.08.00") androidTestImplementation "androidx.compose.ui:ui-test-junit4" debugImplementation "androidx.compose.ui:ui-tooling" debugImplementation "androidx.compose.ui:ui-test-manifest" }第二个需要修改的文件是
android/build.gradle。 -
将以下 buildscript 块添加到文件顶部
android/build.gradle.ktskotlinbuildscript { dependencies { // Replace with the latest version. classpath("com.android.tools.build:gradle:8.1.1") } repositories { google() mavenCentral() } }android/build.gradlegroovybuildscript { dependencies { // Replace with the latest version. classpath 'com.android.tools.build:gradle:8.1.1' } repositories { google() mavenCentral() } }第三个需要修改的文件是
android/app/src/main/AndroidManifest.xml。 -
在根 application 块中,添加以下
<activity>声明android/app/src/main/AndroidManifest.xmlxml<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:label="flutter_android_activity" android:name="${applicationName}" android:icon="@mipmap/ic_launcher"> // START COPYING HERE <activity android:name=".SecondActivity" android:exported="true" android:theme="@style/LaunchTheme"></activity> // END COPYING HERE <activity android:name=".MainActivity" …></activity> … </manifest>第四个也是最后一个需要修改的代码位于
android/app/src/main/kotlin/com/example/flutter_android_activity/MainActivity.kt。在这里,你将为你想要实现的 Android 功能编写 Kotlin 代码。 -
在文件顶部添加必要的导入
MainActivity.ktkotlinpackage com.example.flutter_android_activity import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.ui.Modifier import androidx.core.app.ActivityCompat import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugins.GeneratedPluginRegistrant -
通过添加
CHANNEL字段和configureFlutterEngine方法来修改生成的MainActivity类MainActivity.ktkotlinclass MainActivity: FlutterActivity() { // This value must match the `MethodChannel` name in your Dart code. private val CHANNEL = "com.example.flutter_android_activity" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call: MethodCall, result: MethodChannel.Result -> when (call.method) { // Note: This must match the first parameter passed to // `platformMethodChannel.invokeMethod` in your Dart code. "launchActivity" -> { try { // Takes an object, in this case a String. val message = call.arguments val intent = Intent(this@MainActivity, SecondActivity::class.java) intent.putExtra("message", message.toString()) startActivity(intent) } catch (e: Exception){} result.success(true) } else -> {} } } } } -
在文件底部添加第二个
Activity,即你在之前修改AndroidManifest.xml时引用的那个MainActivity.ktkotlinclass SecondActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Column { Text(text = "Second Activity") // Note: This must match the shape of the data passed from your Dart code. Text("" + getIntent()?.getExtras()?.getString("message")) Button(onClick = { finish() }) { Text("Exit") } } } } } }
这些步骤展示了如何从 Flutter 应用启动原生 Android Activity,这有时是连接特定 Android 功能的简单方法。