Fusion
简介
Fusion 是新一代的混合管理框架,用于 Flutter 与 Native 页面统一管理,并支持页面通信、页面生命周期监听等功能。
Fusion 使用了 Flutter 新的导航框架 Navigator2.0,这使得 Fusion 可以更加灵活对 Flutter 路由栈进行管理。此外 Fusion 基于 FlutterEngineGroup 实现多 Engine,通过 FlutterEngineGroup 来创建新的 Engine,Flutter 官方宣称内存损耗仅占 180K,其本质是使 Engine 可以共享 GPU 上下文、字形和 isolate group snapshot,从而实现了更快的初始速度和更低的内存占用。这样在保证了性能的前提下混合栈的管理也变得更加便捷。
不像其他类似框架,随着 Flutter 版本的更新往往需要对框架本身进行版本适配工作,如果开发者维护不及时就会导致整个项目都无法使用新版 Flutter, Fusion 未对 Flutter Framework 层进行 Hook,较好的兼容性使得使用者可以更加从容地升级 Flutter。
设计严格遵循以下原则:
- 轻量化
- 最小侵入
- 三端一致API
开始使用
0、准备
在开始前需要按照 Flutter 官方文档,将 Flutter Module 项目接入到 Android 和 iOS 工程中。
1、初始化
Flutter 侧
使用 FusionApp 替换之前使用的 App Widget,并传入自定义的路由表
void main() {
runApp(FusionApp(
routeMap,
));
}
// 路由表
final Map<String, PageFactory> routeMap = {
'/test': (arguments) => TestPage(arguments: arguments),
unknownRoute: (arguments) => UnknownPage(arguments: arguments),
};
Android 侧
在 Application 中进行初始化,并实现 FusionRouteDelegate 接口
class MyApplication : Application(), FusionRouteDelegate {
override fun onCreate() {
super.onCreate()
Fusion.install(this, this)
}
override fun pushNativeRoute(name: String?, arguments: Map<String, Any>?) {
// Flutter 跳转 Native 页面时被调用
// 根据路由 name 跳转对应原生 Activity
}
override fun pushFlutterRoute(name: String?, arguments: Map<String, Any>?) {
// Native 跳转 Flutter 页面时被调用
// 根据路由 name 跳转对应 FusionActivity 或其子类
context?.let {
it.startActivity(buildFusionIntent(it, CustomFusionActivity::class.java, name, arguments))
}
}
}
iOS 侧
在 AppDelegate 中进行初始化,并实现 FusionRouteDelegate 代理
@UIApplicationMain
@objc class AppDelegate: UIResponder, UIApplicationDelegate, FusionRouteDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...
Fusion.instance.install(delegate: self)
...
return true
}
func pushNativeRoute(name: String?, arguments: Dictionary<String, Any>?) {
// Flutter 跳转 Native 页面时被调用
// 根据路由 name 跳转对应原生 VC
}
func pushFlutterRoute(name: String?, arguments: Dictionary<String, Any>?) {
// Native 跳转 Flutter 页面时被调用
// 根据路由 name 跳转对应 FusionViewController 或其子类
guard let name = name else {
return
}
let navController = self.window?.rootViewController as? UINavigationController
let fusionVc = CustomViewController(routeName: name, routeArguments: arguments)
navController?.pushViewController(fusionVc, animated: true)
}
}
2、Flutter 容器
页面模式
Android 使用 FusionActivty,启动 FusionActivty(或其子类)时需要使用 Fusion 提供的 buildFusionIntent
方法。
iOS 使用 FusionViewController,可以直接使用,也可通过继承其创建新的 ViewController。FusionViewController 默认隐藏了 UINavigationController,并且将 iOS 和 Flutter 右滑手势返回兼容,使得 iOS 原生页面和 Flutter 页面都可通过手势返回。
P.S: 如果存在手势冲突,可以关闭手势自适应模式
Fusion.instance.adaptiveGesture = false
子页面模式
Android 使用 FusionFragment 支持子页面模式,创建 FusionFragment 对象需要使用 FusionFragment.buildFragment
方法。
iOS 使用 FusionViewController 并传入 childMode: true
以支持子页面模式。
Fusion 支持多个 Flutter 页面以 Tab 形式嵌入一个 Native 容器中
3、路由API(FusionNavigator)
✅ push:指定页入栈,支持获取返回值
✅ pop:栈顶页出栈,支持设置返回值
✅ replace:替换栈顶页面
P.S. 除页面外其他类型如 Dialog 等使用 Navigator 的 push 和 pop.
4、Flutter Plugin 注册
框架内部会自动注册插件,无须手动调用 GeneratedPluginRegistrant.registerWith
5、自定义 Channel
如果需要 Native 与 Flutter 进行通信,则需要自行创建 Channel,创建 Channel 方式如下(以 MethodChannel 为例):
Android 侧
①、如果使用的是 FusionFragment 容器,则需在对应的 Activity 类上实现 FusionMessengerProvider 接口,在接口方法中创建 Channel
class MyActivity : FragmentActivity(), FusionMessengerProvider {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
//加载 FusionFragment
}
override fun configureFlutterChannel(binaryMessenger: BinaryMessenger) {
val channel = MethodChannel(binaryMessenger, "自定义的channel名")
channel.setMethodCallHandler { call, result ->
}
}
}
②、如果使用的是 FusionActivity 容器,则需要创建一个新的 Activity 并继承 FusionActivity 并实现 FusionMessengerProvider 接口,在接口方法中创建 Channel
class CustomActivity : FusionActivity(), FusionMessengerProvider {
override fun configureFlutterChannel(binaryMessenger: BinaryMessenger) {
val channel = MethodChannel(binaryMessenger, "自定义的channel名")
channel.setMethodCallHandler { call, result ->
}
}
}
iOS 侧
①、如果直接使用了 FusionViewController 作为 Flutter 容器
let fusionVc = FusionViewController(routeName: name, routeArguments: arguments)
let channel = FlutterMethodChannel(name: "自定义的channel名", binaryMessenger: fusionVc.binaryMessenger)
channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
//根据call.method匹配路由
}
②、如果创建一个继承自 FusionViewController 的 ViewController 作为 Flutter 容器
既可以按照①中的方式创建channel,也可以实现 FusionMessengerProvider 协议,在协议方法中创建 Channel
class CustomViewController : FusionViewController, FusionMessengerProvider {
func configureFlutterChannel(binaryMessenger: FlutterBinaryMessenger) {
let channel = FlutterMethodChannel(name: "自定义的channel名", binaryMessenger: binaryMessenger)
channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
//根据call.method匹配路由
}
}
}
BasicMessageChannel 和 EventChannel 使用也是类似。另外务必确保 Flutter 和 Native 使用的 Channel类型统一。
需要注意的是,自行创建的 Channel,Android 需要在 Activity 的 onDestroy、iOS 需要在 ViewController 的析构函数中对其置空,避免可能的内存泄漏。
Android 侧
override fun onDestroy() {
super.onDestroy()
channel?.setMethodCallHandler(null)
channle = null
}
iOS 侧
deinit {
channel?.setMethodCallHandler(nil)
channel = nil
}
6、生命周期
支持 页面模式
下监听 Flutter 页面的生命周期。
- ①、在需要监听生命周期页面的 State 中
implements
PageLifecycleListener - ②、在 didChangeDependencies 中注册监听
- ③、在 dispose 中注销监听
class LifecyclePage extends StatefulWidget {
const LifecyclePage({Key? key}) : super(key: key);
@override
State<LifecyclePage> createState() => _LifecyclePageState();
}
class _LifecyclePageState extends State<LifecyclePage>
implements PageLifecycleListener {
@override
Widget build(BuildContext context) {
return Container();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
PageLifecycleBinding.instance.register(this);
}
@override
void onPageVisible() {}
@override
void onPageInvisible() {}
@override
void onForeground() {}
@override
void onBackground() {}
@override
void dispose() {
super.dispose();
PageLifecycleBinding.instance.unregister(this);
}
}
PageLifecycleListener 生命周期回调函数:
- onForeground: 应用进入前台会被调用,所有注册了生命周期监听的页面都会收到
- onBackground: 应用退到后台会被调用,所有注册了生命周期监听的页面都会收到
- onPageVisible: 该 Flutter 页面可见时被调用,如:从 Native 页面或其他 Flutter 页面
push
到该 Flutter 页面时;从 Native 页面或其他 Flutter 页面pop
到该 Flutter 页面时;但当应用进入前台时不会被调用,与 iOS 的viewDidAppear
类似,与 Android 的onResume
稍有差异 - onPageInvisible: 该 Flutter 页面不可见时被调用,如:从该 Flutter 页面
push
到 Native 页面或其他 Flutter 页面时;如从该 Flutter 页面pop
到 Native 页面或其他 Flutter 页面时;但当应用退到后台时不会被调用,与 iOS 的viewDidDisappear
类似,与 Android 的onStop
稍有差异
7、页面通信
支持多种情况下页面消息传递:
- Flutter -> Flutter
- Flutter -> Native
- Native -> Flutter
- Native -> Native
注册消息监听
Flutter侧
- ①、在需要监听消息的页面的 State 中
implements
PageNotificationListener,并复写onReceive
方法,该方法可收到发送过来的消息 - ②、在 didChangeDependencies 中注册监听
- ③、在 dispose 中注销监听
class TestPage extends StatefulWidget {
@override
State<TestPage> createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> implements PageNotificationListener {
@override
void onReceive(String msgName, Map<String, dynamic>? msgBody) {
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
PageNotificationBinding.instance.register(this);
}
@override
void dispose() {
super.dispose();
PageNotificationBinding.instance.unregister(this);
}
}
Android侧
- 在需要监听页面消息的 Activity 中 实现 PageNotificationListener 接口,并复写
onReceive
方法,该方法可收到发送过来的消息
iOS侧
- 在需要监听页面消息的 ViewController(不支持子VC) 中 实现 PageNotificationListener 协议,并复写
onReceive
方法,该方法可收到发送过来的消息
发送消息
三端均可使用FusionNavigator
的 sendMessage
方法来发送消息。