向 Android 应用添加 Flutter 屏幕
了解如何向现有的 Android 应用添加单个 Flutter 屏幕。
本指南介绍了如何向现有的 Android 应用添加单个 Flutter 屏幕。可以将 Flutter 屏幕添加为常规的不透明屏幕,或添加为可透视的半透明屏幕。本指南将介绍这两种选项。
添加普通的 Flutter 屏幕
#
第 1 步:将 FlutterActivity 添加到 AndroidManifest.xml
#Flutter 提供了 FlutterActivity 以便在 Android 应用中展示 Flutter 体验。与其他任何 Activity 一样,FlutterActivity 必须在 AndroidManifest.xml 中进行注册。请将以下 XML 添加到 AndroidManifest.xml 文件的 application 标签内:
<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 系统界面(如 Android 导航栏)的颜色,以及 Flutter UI 首次渲染前 FlutterActivity 的背景颜色。
第 2 步:启动 FlutterActivity
#在清单文件中注册 FlutterActivity 后,在应用中您希望的位置添加代码以启动 FlutterActivity。以下示例展示了如何从 OnClickListener 中启动 FlutterActivity。
MyButton(onClick = {
startActivity(
FlutterActivity.createDefaultIntent(this)
)
})
@Composable
fun MyButton(onClick: () -> Unit) {
Button(onClick = onClick) {
Text("Launch Flutter!")
}
}
myButton.setOnClickListener {
startActivity(
FlutterActivity.createDefaultIntent(this)
)
}
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(currentActivity)
);
}
});
上述示例假设您的 Dart 入口点名为 main(),且初始的 Flutter 路由为 '/'。Dart 入口点无法通过 Intent 更改,但初始路由可以通过 Intent 更改。以下示例演示了如何启动一个在 Flutter 中初始渲染自定义路由的 FlutterActivity。
MyButton(onClick = {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("/my_route")
.build(this)
)
})
@Composable
fun MyButton(onClick: () -> Unit) {
Button(onClick = onClick) {
Text("Launch Flutter!")
}
}
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("/my_route")
.build(this)
)
}
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:
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)
}
}
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 传递给任何应该使用该缓存 FlutterEngine 的 FlutterActivity 或 FlutterFragment。接下来将讨论如何将 FlutterActivity 与缓存的 FlutterEngine 一起使用。
有了预热后的缓存 FlutterEngine,您现在需要指示 FlutterActivity 使用该缓存引擎,而不是创建一个新的。为此,请使用 FlutterActivity 的 withCachedEngine() 构建器:
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
)
}
myButton.addOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(currentActivity)
);
}
});
使用 withCachedEngine() 工厂方法时,请传递与缓存所需 FlutterEngine 时使用的相同的 ID。
现在,当您启动 FlutterActivity 时,Flutter 内容显示的延迟会显著减少。
缓存引擎的初始路由
#在配置带有新 FlutterEngine 的 FlutterActivity 或 FlutterFragment 时,可以使用初始路由的概念。然而,当使用缓存引擎时,FlutterActivity 和 FlutterFragment 不提供初始路由的概念。这是因为缓存的引擎预期已经在运行 Dart 代码,这意味着现在配置初始路由为时已晚。
开发者如果希望其缓存引擎以自定义初始路由启动,可以在执行 Dart 入口点之前,配置其缓存的 FlutterEngine 以使用自定义初始路由。以下示例展示了如何在缓存引擎中使用初始路由:
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)
}
}
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() 初次执行后更改导航通道的初始路由属性不会产生任何效果。如果开发者希望在不同的 Activity 和 Fragment 之间使用相同的 FlutterEngine,并在这些界面之间切换路由,则需要设置方法通道(MethodChannel),并显式指示 Dart 代码更改 Navigator 路由。
添加半透明的 Flutter 屏幕
#
大多数全屏 Flutter 体验都是不透明的。然而,一些应用希望部署看起来像模态框的 Flutter 屏幕,例如对话框或底部工作表(bottom sheet)。Flutter 开箱即支持半透明的 FlutterActivity。
要使您的 FlutterActivity 半透明,请对创建和启动 FlutterActivity 的常规流程进行以下更改。
第 1 步:使用支持透明度的主题
#对于以半透明背景渲染的 Activity,Android 需要一个特殊的主题属性。请创建或更新具有以下属性的 Android 主题:
<style name="MyTheme" parent="@style/MyParentTheme">
<item name="android:windowIsTranslucent">true</item>
</style>
然后,将该半透明主题应用于您的 FlutterActivity。
<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:
// 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)
);
// 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。