原生 Android Activity 允許您啟動完全由 Android 平臺執行的全屏 UI。您只需在這些檢視中編寫 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),
        ),
      ),
    );
  }
}

您的 Dart 和 Kotlin 程式碼中有 3 個重要值必須匹配

  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/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. 在檔案頂部新增必要的匯入

    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. 修改生成的 MainActivity 類,新增 CHANNEL 欄位和 configureFlutterEngine 方法

    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 更改中已引用了該 Activity

    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 功能的便捷方法。