跳至主要內容

從你的 Flutter 應用程式啟動 Jetpack Compose 活動

原生 Android 活動允許您啟動完全由 Android 平台執行和控制的全螢幕 UI。您只會在這些視圖中編寫 Kotlin 程式碼(儘管它們可能會傳遞訊息給您的 Dart 程式碼,並從 Dart 程式碼接收訊息),並且您將可以使用完整的原生 Android 功能。

新增此功能需要對您的 Flutter 應用程式及其內部產生的 Android 應用程式進行多項變更。在 Flutter 端,您需要建立一個新的平台方法通道,並呼叫其 invokeMethod 方法。在 Android 端,您需要註冊一個匹配的原生 MethodChannel 以接收來自 Dart 的訊號,然後啟動一個新的活動。請回想一下,所有 Flutter 應用程式(在 Android 上執行時)都存在於一個完全被 Flutter 應用程式消耗的 Android 活動中。因此,正如您將在程式碼範例中看到的,原生 MethodChannel 回呼的工作是啟動第二個活動。

並非所有 Android 活動都使用 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" 鍵的 Map。

在 Android 端

#

您必須對產生的 Android 應用程式中的 4 個檔案進行變更,才能準備好啟動新的 Compose 活動。

第一個需要修改的檔案是 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. 在根應用程式區塊中,新增以下 <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 的變更中引用了該活動

    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 活動,這有時可能是連接到特定 Android 功能的簡單方法。