Skip to main content

将 Flutter 屏幕添加到 Android 应用

本指南介绍如何将单个 Flutter 屏幕添加到现有的 Android 应用中。Flutter 屏幕可以作为普通的、不透明的屏幕添加,也可以作为半透明的屏幕添加。本指南将介绍这两种方法。

添加普通 Flutter 屏幕

#

Add Flutter Screen Header

步骤 1:在 AndroidManifest.xml 中添加 FlutterActivity

#

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 更改初始路由。以下示例演示如何启动一个最初在 Flutter 中呈现自定义路由的 FlutterActivity

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() 工厂方法会配置一个内部创建其自己的 FlutterEngine 实例的 FlutterActivity。这会带来非微不足道的初始化时间。另一种方法是指示 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()

    // 实例化 FlutterEngine。
    flutterEngine = FlutterEngine(this)

    // 开始执行 Dart 代码以预热 FlutterEngine。
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )

    // 缓存 FlutterEngine 以供 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();
    // 实例化 FlutterEngine。
    flutterEngine = new FlutterEngine(this);

    // 开始执行 Dart 代码以预热 FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );

    // 缓存 FlutterEngine 以供 FlutterActivity 使用。
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}

传递给 FlutterEngineCache 的 ID 可以是任何您想要的 ID。确保将相同的 ID 传递给应使用缓存的 FlutterEngine 的任何 FlutterActivityFlutterFragment。接下来将讨论使用缓存 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 内容的延迟会大大减少。

使用缓存引擎的初始路由

#

在使用新的FlutterEngine配置FlutterActivityFlutterFragment时,可以使用初始路由的概念。但是,当使用缓存的引擎时,FlutterActivityFlutterFragment不提供初始路由的概念。这是因为缓存的引擎应该已经在运行 Dart 代码,这意味着配置初始路由为时已晚。

希望他们的缓存引擎以自定义初始路由开始的开发者可以在执行 Dart 入口点之前,将他们的缓存FlutterEngine配置为使用自定义初始路由。以下示例演示了在缓存引擎中使用初始路由:

MyApplication.kt
kotlin
class MyApplication : Application() {
  lateinit var flutterEngine : FlutterEngine
  override fun onCreate() {
    super.onCreate()
    // 实例化一个 FlutterEngine。
    flutterEngine = FlutterEngine(this)
    // 配置初始路由。
    flutterEngine.navigationChannel.setInitialRoute("your/route/here");
    // 开始执行 Dart 代码以预热 FlutterEngine。
    flutterEngine.dartExecutor.executeDartEntrypoint(
      DartExecutor.DartEntrypoint.createDefault()
    )
    // 缓存 FlutterEngine 以供 FlutterActivity 或 FlutterFragment 使用。
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine)
  }
}
MyApplication.java
java
public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // 实例化一个 FlutterEngine。
    flutterEngine = new FlutterEngine(this);
    // 配置初始路由。
    flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");
    // 开始执行 Dart 代码以预热 FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(
      DartEntrypoint.createDefault()
    );
    // 缓存 FlutterEngine 以供 FlutterActivity 或 FlutterFragment 使用。
    FlutterEngineCache
      .getInstance()
      .put("my_engine_id", flutterEngine);
  }
}

通过设置导航通道的初始路由,关联的FlutterEnginerunApp() 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
// 使用新的 FlutterEngine。
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);

// 使用缓存的 FlutterEngine。
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(this)
);
ExistingActivity.java
java
// 使用新的 FlutterEngine。
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

// 使用缓存的 FlutterEngine。
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

现在,您拥有一个具有透明背景的 FlutterActivity