在 Android 应用中添加 Flutter 页面
本指南讲述了如何在一个现有的 Android 应用中添加单个 Flutter 页面。添加到应用中的单个 Flutter 页面可以是不透明的普通页面,也可以是透明的页面。这两种页面的使用都会在本指南中提到。
添加一个普通的 Flutter 页面
步骤 1:在 AndroidManifest.xml 中添加 FlutterActivity
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.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(currentActivity)
);
}
});
myButton.setOnClickListener {
startActivity(
FlutterActivity.createDefaultIntent(this)
)
}
上述的例子假定了你的 Dart 代码入口是调用 main()
,并且你的 Flutter 初始路由是 ‘/’。
Dart 代码入口不能通过 Intent
改变,但是初始路由可以通过 Intent
来修改。下面的例子讲解了如何打开一个自定义 Flutter 初始路由的 FlutterActivity
。
myButton.addOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("/my_route")
.build(currentActivity)
);
}
});
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("/my_route")
.build(this)
)
}
可以用你想要的初始路由替换掉 "/my_route"
。
工厂方法 withNewEngine()
可以用于配置一个 FlutterActivity
,它会在内部创建一个属于自己的 FlutterEngine
实例,这会有一个明显的初始化时间。另外一种可选的做法是让
FlutterActivity
使用一个预热且缓存的 FlutterEngine
,这可以最小化 Flutter 初始化的时间。这种方式接下来会讨论到。
步骤 3:(可选)使用缓存的 FlutterEngine
每一个 FlutterActivity
默认会创建它自己的 FlutterEngine
。每一个 FlutterEngine
会有一个明显的预热时间。这意味着加载一个标准的 FlutterActivity
时,在你的 Flutter 交互页面可见之前会有一个短暂的延迟。想要最小化这个延迟时间,你可以在抵达你的 FlutterActivity
之前,初始化一个 FlutterEngine
,然后使用这个已经预热好的 FlutterEngine
。
要预热一个 FlutterEngine
,可以在你的应用中找一个合理的地方实例化一个 FlutterEngine
。下面的这个例子是在 Application
类中预先初始化一个 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);
}
}
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)
}
}
传给 FlutterEngineCache
的 ID 可以是你想要的任何名称。确保 FlutterActivity
或 FlutterFragment
在使用缓存的
FlutterEngine
时,传递了同样的 ID。基于缓存的 FlutterEngine
来使用 FlutterActivity
会在后续讨论到。
要使用预热且缓存的 FlutterEngine
时,让你的 FlutterActivity
从缓存中获取 FlutterEngine
,而不是创建一个新的。可以使用 FlutterActivity
的 withCachedEngine()
方法来实现:
myButton.addOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(currentActivity)
);
}
});
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
)
}
当使用 withCachedEngine()
方法时,传递你缓存对应 FlutterEngine
时用的相同 ID。
现在,当你加载 FlutterActivity
时,在展示 Flutter 内容前的延迟会明显降低。
为缓存的 FlutterEngine 设置初始路由
当配置一个使用新 FlutterEngine
的 FlutterActivity
或者 FlutterFragment
时,会使用到初始路由的概念。但是,使用缓存中的 Flutter 引擎时,
FlutterActivity
或者 FlutterFragment
则没有涉及初始路由的概念。这是因为被缓存的引擎理论上已经执行了 Dart 代码,在这时配置初始路由已经太迟了。
开发者如果想要让缓存中的引擎从自定义的初始路由开始运行,那么可以执行 Dart 入口前,为缓存的 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);
}
}
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)
}
}
通过设置导航通道中的初始路由,会让关联的 FlutterEngine
在 runApp()
方法首次执行后,展示已配置的路由页面。
在 runApp()
的首次执行之后,修改导航通道中的初始路由属性是不会生效的。想要在不同的 Activity
和 Fragment
之间使用同一个 FlutterEngine
,并且在其展示时切换不同的路由,开发者需要设置一个方法通道,来显式地通知他们的 Dart 代码切换 Navigator
路由。
添加一个透明主题的 FlutterActivity
大部分的全屏 Flutter 交互页面是不透明的。但是,一些应用可能会发布一个类似模态框的 Flutter 页面,例如,一个对话框或者底部工作表。
Flutter 默认支持透明的 FlutterActivity
。
要将你的 FlutterActivity
设置为透明,在创建和加载 FlutterActivity
的常规步骤中做如下的变更。
步骤 1:使用透明的主题
Android 需要一个特殊的主题属性来让 Activity
以一个透明的背景渲染。使用如下属性来创建或者修改一个 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(context)
);
// Using a cached FlutterEngine.
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
.build(context)
);
// 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)
);
现在你的 FlutterAcivity
的背景已经是透明的了。