跳到主內容

將 Flutter 螢幕新增到 Android 應用

瞭解如何將單個 Flutter 螢幕新增到您現有的 Android 應用。

本指南介紹瞭如何將單個 Flutter 螢幕新增到現有的 Android 應用。Flutter 螢幕可以作為普通的、不透明的螢幕新增,也可以作為半透明的螢幕新增。本指南介紹了這兩種選項。

新增一個普通的 Flutter 螢幕

#
Add Flutter Screen Header

步驟 1:將 FlutterActivity 新增到 AndroidManifest.xml

#

Flutter 提供了 FlutterActivity,用於在 Android 應用中顯示 Flutter 體驗。與任何其他 Activity 一樣,FlutterActivity 必須在您的 AndroidManifest.xml 中註冊。將以下 XML 新增到您的 AndroidManifest.xml 檔案中,位於您的 application 標籤下

xml
<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/LaunchTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

@style/LaunchTheme 的引用可以替換為任何您想要應用於 FlutterActivity 的 Android 主題。主題的選擇決定了應用於 Android 系統 chrome 的顏色,例如 Android 的導航欄,以及在 Flutter UI 首次渲染之前 FlutterActivity 的背景顏色。

步驟 2:啟動 FlutterActivity

#

在您的清單檔案中註冊了 FlutterActivity 後,新增程式碼以從您應用中的任何位置啟動 FlutterActivity。以下示例顯示了從 OnClickListener 啟動 FlutterActivity

ExistingActivity.kt
kotlin
MyButton(onClick = {
    startActivity(
        FlutterActivity.createDefaultIntent(this)
    )
})

@Composable
fun MyButton(onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Launch Flutter!")
    }
}
ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity.createDefaultIntent(this)
  )
}
ExistingActivity.java
java
myButton.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity.createDefaultIntent(currentActivity)
    );
  }
});

前面的示例假定您的 Dart 入口點名為 main(),並且您的初始 Flutter 路由為“/”。不能使用 Intent 更改 Dart 入口點,但可以使用 Intent 更改初始路由。以下示例演示瞭如何啟動一個 FlutterActivity,該 FlutterActivity 最初在 Flutter 中渲染自定義路由。

ExistingActivity.kt
kotlin
MyButton(onClick = {
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this)
  )
})

@Composable
fun MyButton(onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Launch Flutter!")
    }
}
ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this)
  )
}
ExistingActivity.java
java
myButton.addOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity
        .withNewEngine()
        .initialRoute("/my_route")
        .build(currentActivity)
      );
  }
});

"/my_route" 替換為您所需的初始路由。

使用 withNewEngine() 工廠方法配置一個 FlutterActivity,該方法會在內部建立自己的 FlutterEngine 例項。這需要一個非微不足道的初始化時間。另一種方法是指示 FlutterActivity 使用預熱的、快取的 FlutterEngine,這可以最大限度地減少 Flutter 的初始化時間。接下來將討論這種方法。

步驟 3:(可選)使用快取的 FlutterEngine

#

每個 FlutterActivity 預設情況下都會建立自己的 FlutterEngine。每個 FlutterEngine 都有一個非微不足道的預熱時間。這意味著啟動標準的 FlutterActivity 會在您的 Flutter 體驗可見之前產生短暫的延遲。為了最大限度地減少此延遲,您可以在到達 FlutterActivity 之前預熱 FlutterEngine,然後可以使用預熱的 FlutterEngine

要預熱 FlutterEngine,請在您的應用中找到一個合理的位置來例項化 FlutterEngine。以下示例在 Application 類中任意預熱 FlutterEngine

MyApplication.kt
kotlin
class MyApplication : Application() {
  lateinit var flutterEngine : FlutterEngine

  override fun onCreate() {
    super.onCreate()

    // Instantiate a FlutterEngine.
    flutterEngine = FlutterEngine(this)

    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )

    // Cache the FlutterEngine to be used by FlutterActivity.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine)
  }
}
MyApplication.java
java
public class MyApplication extends Application {
  public FlutterEngine flutterEngine;

  @Override
  public void onCreate() {
    super.onCreate();
    // Instantiate a FlutterEngine.
    flutterEngine = new FlutterEngine(this);

    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );

    // Cache the FlutterEngine to be used by FlutterActivity.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}

傳遞給 FlutterEngineCache 的 ID 可以是您想要的任何內容。請確保將相同的 ID 傳遞給任何應該使用快取的 FlutterEngineFlutterActivityFlutterFragment。接下來討論使用快取的 FlutterEngineFlutterActivity

使用預熱的、快取的 FlutterEngine 後,您現在需要指示您的 FlutterActivity 使用快取的 FlutterEngine,而不是建立一個新的 FlutterEngine。為此,使用 FlutterActivitywithCachedEngine() 構建器

ExistingActivity.kt
kotlin
myButton.setOnClickListener {
  startActivity(
    FlutterActivity
      .withCachedEngine("my_engine_id")
      .build(this)
  )
}
ExistingActivity.java
java
myButton.addOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    startActivity(
      FlutterActivity
        .withCachedEngine("my_engine_id")
        .build(currentActivity)
      );
  }
});

在使用 withCachedEngine() 工廠方法時,傳遞您在快取所需的 FlutterEngine 時使用的相同 ID。

現在,當您啟動 FlutterActivity 時,顯示 Flutter 內容的延遲明顯減少了。

使用快取引擎的初始路由

#

初始路由的概念在配置使用新的 FlutterEngineFlutterActivityFlutterFragment 時可用。但是,當使用快取引擎時,FlutterActivityFlutterFragment 不提供初始路由的概念。這是因為快取引擎預計已經正在執行 Dart 程式碼,這意味著配置初始路由為時已晚。

希望其快取引擎從自定義初始路由開始的開發人員可以在執行 Dart 入口點之前配置其快取的 FlutterEngine 以使用自定義初始路由。以下示例演示了使用快取引擎的初始路由

MyApplication.kt
kotlin
class MyApplication : Application() {
  lateinit var flutterEngine : FlutterEngine
  override fun onCreate() {
    super.onCreate()
    // Instantiate a FlutterEngine.
    flutterEngine = FlutterEngine(this)
    // Configure an initial route.
    flutterEngine.navigationChannel.setInitialRoute("your/route/here");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine)
  }
}
MyApplication.java
java
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // Instantiate a FlutterEngine.
    flutterEngine = new FlutterEngine(this);
    // Configure an initial route.
    flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}

透過設定導航通道的初始路由,關聯的 FlutterEngine 在初始執行 runApp() Dart 函式時會顯示所需的路由。

在初始執行 runApp() 之後更改導航通道的初始路由屬性無效。希望在不同的 ActivityFragment 之間使用相同的 FlutterEngine 並在這兩個顯示之間切換路由的開發人員需要設定一個方法通道,並顯式指示他們的 Dart 程式碼更改 Navigator 路由。

新增一個半透明的 Flutter 螢幕

#
Add Flutter Screen With Translucency Header

大多數全屏 Flutter 體驗是不透明的。但是,有些應用希望部署看起來像模態的 Flutter 螢幕,例如對話方塊或底部工作表。Flutter 開箱即用地支援半透明的 FlutterActivity

要使您的 FlutterActivity 半透明,請對建立和啟動 FlutterActivity 的常規過程進行以下更改。

步驟 1:使用具有半透明度的主題

#

Android 需要一個特殊的屬性來表示具有半透明背景渲染的 Activity 的主題。建立或更新具有以下屬性的 Android 主題

xml
<style name="MyTheme" parent="@style/MyParentTheme">
  <item name="android:windowIsTranslucent">true</item>
</style>

然後,將半透明主題應用於您的 FlutterActivity

xml
<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/MyTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

您的 FlutterActivity 現在支援半透明度。接下來,您需要以明確的透明度支援啟動您的 FlutterActivity

步驟 2:以透明方式啟動 FlutterActivity

#

要以透明背景啟動您的 FlutterActivity,請將適當的 BackgroundMode 傳遞給 IntentBuilder

ExistingActivity.kt
kotlin
// Using a new FlutterEngine.
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);

// Using a cached FlutterEngine.
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);
ExistingActivity.java
java
// Using a new FlutterEngine.
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

// Using a cached FlutterEngine.
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

現在,您擁有一個具有透明背景的 FlutterActivity