Flutter多引擎&dart多入口设计
仿照工行投资理财页面,使用Flutter实现。通过此讲述混合开发中,项目设计中,需求下如何创建多个Flutter引擎。Flutter引擎如何对应各自的dart入口,并实现每个Flutter引擎下路由切换页面。从而完成Flutter项目架构的设计搭建。
Flutter项目混合架构
FlutterFragment普通实现设计
系统有提供用来容放Flutter UI的容器
,即FlutterFragment继承自androidx.fragment.app.Fragment
。FlutterFragment作为FlutterEngine控制面提供Android资源和生命周期事件,并会attach到Activity。FlutterFragment终以内部方法public <T extends FlutterFragment> T build()
反射获取Fragment实例。并作为当前Activity页面一部分显示出来。
说白了,就是使用混合开发。从原生项目转入到Flutter项目中,是将Flutter项目UI作为原生项目(如Activity)显示页面的一部分(撑满整个页面)来实现。
解释截图中代码片逻辑含义
红圈5, withCachedEngine(id) 中的 id ,有被FlutterEngineCache.getInstance().put(id, flutterEngine)
和.get(id)
用到,表示会将该id作为缓存FlutterEngine实例的键值对key。
追踪代码withCachedEngine(id)
的执行,在FlutterFragment源码中,静态内部类构造方法CachedEngineFragmentBuilder
中,id会被赋值给FlutterFragment全局变量String engineId
。之后在反射获取FlutterFragment实例逻辑的同时,储到FlutterFragment的Bundle中。
FlutterFragment中方法getCachedEngineId
,对外提供获取该id
。
FlutterActivityAndFragmentDelegate
在Activity启动过程会执行onAttach方法,内部会执行到方法setupFlutterEngine
中通过该id获取已缓存的FlutterEngine实例。这个是id的使用目的。
红圈5, shouldAttachEngineToActivity(true) 设置true,目的则是同上面逻辑分析,执行到onAttach方法时,则会执行下面代码片【使用获取到的已缓存的FlutterEngine实例,将已附着插件资源的Flutter引擎附着到Activity中,且生命周期与Activity同步】
Log.v(TAG, "Attaching FlutterEngine to the Activity that owns this delegate.");
flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle());
系统FlutterFragment自带普通设计实现
- FlutterEngine实例的创建,缓存。如红圈3和4.
- 使用引擎实例配置默认(初始态)的路由path,如
/fragment-icbc
。如红圈1。 - 使用引擎实例配置进入到Flutter-dart代码的入口。如红圈2。
- 创建MethodChannel实例,并传入设置通道名称name。
MethodChannel实例.setMethodCallHandler
配置Flutter端向原生端的通信监听。
以上配置完成后,在Flutter项目源码中,dart代码入口文件main.dart。使用路由实例.push(name: window.defaultRouteName);
,即可将Flutter项目首页展示原生项目已配置的默认(初始态)路由path对应的页面。
自定义FlutterFragment
Flutter多引擎设计
Flutter的多引擎设计,利用Flutter中引擎的缓存容器FlutterEngineCache实现。
// 这里定了方法:用来初始化创建,新建flutter引擎实例。
private fun initFlutterEngine(context: Context, moduleName/**缓存到FlutterEngineCache的key*/: String) {
var flutterEngine = FlutterEngine(context, FlutterLoader(), FlutterJNI())
// ...省略 MethodChannel 的配置..
flutterEngine.dartExecutor.
executeDartEntrypoint(DartExecutor.DartEntrypoint(FlutterLoader().findAppBundlePath(), moduleName/**因为入口方法名,配置在这里了!*/))
}
// DartExecutor.java
// 系统源码中,有介绍 `dartEntrypointFunctionName`是dart入口方法名,且在DartEntrypoint构造方法中传入并赋值。
/** The name of a Dart function to execute. */
public final String dartEntrypointFunctionName;
对已创建的Flutter引擎,之后使用FlutterEngineCache缓存起来。此时moduleName
则对应dart入口方法名!
【注:Native和Flutter通信,MethodChannel配置的name须一致。】
Flutter dart入口设计
在Flutter项目入口文件main.dart中,仿照默认初始入口定义规范
void main() {
runApp(const FinanceApp());
}
在同一Flutter项目,自定义其他dart入口
// 定义新的dart入口,需要使用注解 @pragma('vm:entry-point')。其他不变。
('vm:entry-point')
void finance() {
runApp(const FinanceApp());
}