跳到主內容

從 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,並在特定的使用者互動(例如點選按鈕)時呼叫它。

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 Activity 做好準備。

第一個需要修改的檔案是 android/app/build.gradle

  1. 將以下內容新增到現有的 android 塊中

    android/app/build.gradle.kts
    kotlin
    android {
      // Begin adding here
      buildFeatures {
        compose = true
      }
      composeOptions {
        // https://developer.android.com/jetpack/androidx/releases/compose-kotlin
        kotlinCompilerExtensionVersion = "1.4.8"
      }
      // End adding here
    }
    
    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.kts
    kotlin
    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: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.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.kts
    kotlin
    buildscript {
        dependencies {
            // Replace with the latest version.
            classpath("com.android.tools.build:gradle:8.1.1")
        }
        repositories {
            google()
            mavenCentral()
        }
    }
    
    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. 透過新增 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 功能的簡單方法。