原生 Android Activity 允许您启动完全由 Android 平台运行的、全屏的用户界面。您将只在这些视图中编写 Kotlin 代码(尽管它们可能会与您的 Dart 代码传递和接收消息),并且您将能够访问所有原生的 Android 功能。

添加此功能需要对您的 Flutter 应用及其内部生成的 Android 应用进行多项更改。在 Flutter 侧,您需要创建一个新的平台方法通道并调用其 invokeMethod 方法。在 Android 侧,您需要注册一个匹配的原生 MethodChannel 来接收来自 Dart 的信号,然后启动一个新的 Activity。请记住,所有 Flutter 应用(在 Android 上运行时)都存在于一个完全被 Flutter 应用占用的 Android Activity 中。因此,正如您将在代码示例中看到的,原生 MethodChannel 回调的职责是启动第二个 Activity。

并非所有 Android Activity 都使用 Jetpack Compose,但本教程假设您希望使用 Compose。

在 Dart 侧

#

在 Dart 侧,创建一个方法通道,并从特定的用户交互(例如点击按钮)中调用它。

dart
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),
        ),
      ),
    );
  }
}

有 3 个重要值必须在您的 Dart 和 Kotlin 代码中保持一致

  1. 通道名称(在此示例中,值为 "com.example.flutter_android_activity")。
  2. 方法名称(在此示例中,值为 "launchActivity")。
  3. Dart 传递的数据结构与 Kotlin 期望接收的数据结构。在此示例中,数据是一个包含单个 "message" 键的映射。

在 Android 侧

#

您必须对生成的 Android 应用中的 4 个文件进行更改,以便它能够启动新的 Compose Activity。

第一个需要修改的文件是 android/app/build.gradle

  1. 将以下内容添加到现有的 android 块中

    android/app/build.gradle
    groovy
    android {
      // 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 期间收到错误,并且这些错误告诉您机器上安装了哪些版本时,才需要这样做。

  2. 接下来,在文件的底部,在根级别添加以下块

    android/app/build.gradle
    groovy
    dependencies {
        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

  3. 在文件顶部添加以下 buildscript 块

    android/build.gradle
    groovy
    buildscript {
        dependencies {
            // Replace with the latest version.
            classpath 'com.android.tools.build:gradle:8.1.1'
        }
        repositories {
            google()
            mavenCentral()
        }
    }

    第三个需要修改的文件是 android/app/src/main/AndroidManifest.xml

  4. 在根 application 块中,添加以下 <activity> 声明

    android/app/src/main/AndroidManifest.xml
    xml
    <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 代码。

  5. 在文件顶部添加必要的 import

    MainActivity.kt
    kotlin
    package 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
  6. 通过添加 CHANNEL 字段和 configureFlutterEngine 方法来修改生成的 MainActivity

    MainActivity.kt
    kotlin
    class 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 -> {}
                    }
            }
        }
    }
  7. 在文件底部添加第二个 Activity,您在之前对 AndroidManifest.xml 的更改中引用了它

    MainActivity.kt
    kotlin
    class 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 功能的一种简单方法。