为了降低风险,大部分App采用渐进式方式引入Flutter,在App里选几个页面用Flutter来编写,但都碰到了相同的问题,在原生页面和Flutter页面共存的情况下,如何管理路由? 官方没有提供这样的解决方案,而FlutterBoost就是为了解决这个问题而生。FlutterBoost从开源后受到了社区开发者的欢迎,已经有很多App使用了FlutterBoost,社区开发者也很活跃,提了很多Issue和PR。感谢开发者的一路支持和包容,无论是意见反馈还是吐槽,我们都会认真看,会持续关注Issue。
作者:闲鱼技术——石磬
背景
随着Flutter的发展,国内越来越多的App开始使用Flutter。为了降低风险,大部分App采用渐进式方式引入Flutter,在App里选几个页面用Flutter来编写,但都碰到了相同的问题,在原生页面和Flutter页面共存的情况下,如何管理路由? 官方没有提供这样的解决方案,而FlutterBoost就是为了解决这个问题而生。FlutterBoost从开源后受到了社区开发者的欢迎,已经有很多App使用了FlutterBoost,社区开发者也很活跃,提了很多Issue和PR。感谢开发者的一路支持和包容,无论是意见反馈还是吐槽,我们都会认真看,会持续关注Issue。
使命
FlutterBoost的使命是让开发者非常简单的在原生App中开发Flutter页面。 FlutterBoost做为Flutter sdk上层的解决方案,有一定的局限性,我们需要依赖sdk更多的开放能力。因此我们同时在做两件事情:
- 推动Flutter官方开放更多的底层接口
我们参与Flutter 组织的 Multiple Flutters 的讨论。也多次发邮件给Flutter团队反馈sdk的Bug和一些无法支持的应用场景。 很欣慰的是在Flutter 2.0 上看到混合开发的重大进展,Flutter2.0 提供了 FlutterEngineGroup,FlutterEngineGroup创建一个新Engine,内存只增加180k,这个给我们提供了很多想象空间。但FlutterEngineGroup最大的问题是多Engine之间不是isolate层面的内存共享。 从目前看FlutterBoost这种单Engine内存共享的方式还不能被完全取代。
- FlutterBoost的升级
虽然开源社区很活跃,star很多,使用者也很多,但FlutterBoost离优秀的开源项目还很远。
FlutterBoost的问题
梳理了一下问题:
- 稳定性,每次Flutter发布一个stable版本,开发者会来问我,FlutterBoost针对新版本适配了没有?他们准备升级新版本,需要FlutterBoost能适配最新版本。而我每次都要针对新版本拉2个新分支(Androidx 和Support分支),进行适配。 时间长了,会产生很多分支,这个给分支管理带来很大的成本,比如在某个分支上修复的issue要同步到其他分支,一不小心就会遗漏同步。
- 社区的issue没有收敛的趋势。
- 设计过于复杂,概念太多。这让一个新手看FlutterBoost的代码很吃力。
这些问题促使我们重新梳理设计,为了彻底解决这些顽固的问题,我们做一次大升级,我们把这次升级命名为FlutterBoost 3.0(上一次升级是2.0)
FlutterBoost3.0做了什么
针对上面的问题,我们做了几个事项 不侵入引擎,兼容Flutter的各种版本,Flutter sdk的升级不需要再升级FlutterBoost,极大降低升级成本。 不区分Androidx和Support分支。 简化架构和接口,和FlutterBoost2.0比,代码减少了一半。 双端统一,包括接口和设计上的统一。 支持打开Flutter页面,不再打开容器场景。 页面生命周期变化通知更方便业务使用。 * 解决了2.0中的遗留问题,例如,Fragment接入困难、页面关闭后不能传递数据、dispose不执行,内存占用过高等。
架构图
FlutterBoost插件分为平台和Dart两端,中间通过Message Channel连接。平台侧提供了Flutter引擎的配置和管理、Native容器的创建/销毁、页面可见性变化通知,以及Flutter页面的打开/关闭接口等。而Dart侧除了提供类似原生Navigator的页面导航接口的能力外,还负责Flutter页面的路由管理
不入侵引擎
为了解决官方引擎复用引起的问题,FlutterBoost2.0拷贝了Flutter引擎Embedding层的一些代码进行改造,这使得后期的升级成本极高。而FlutterBoost3.0采用继承的方式扩展FlutterActivity/FlutterFragment等组件的能力,并且通过在适当时机给Dart侧发送appIsResumed消息解决引擎复用时生命周期事件错乱导致的页面卡死问题。FlutterBoost3.0 也兼容最新的官方发布的 Flutter 2.0。
不区分Androidx和Support分支
FlutterBoost2.0通过自己实现FlutterActivityAndFragmentDelegate.Host接口来扩展FlutterActivity和FlutterFragment的能力,而getLifecycle是必须实现的接口,这就导致对androidx的依赖。 这也是为什么FlutterBoostView的实现没有被放入FlutterBoost3.0插件中的原因。而FlutterBoost3.0通过继承的方式扩展FlutterActivity/FlutterFragment的能力的额外收益就是,可以做到不依赖androidx。
双端设计统一,接口统一
很多Flutter开发者只会一端,只会Android 或者只会IOS,但他需要接入双端,所以双端统一能降低他的 学习成本和接入成本。FlutterBoost3.0,在设计上 Android和IOS都做了对齐,特别接口上做到了参数级的对齐。
支持 “打开flutter页面不再打开容器” 场景。
在很多场景下,Flutter 页面跳转Flutter 页面,这个时候可以不需要再打开容器。不打开容器,能节省内存开销。 在FlutterBoost3.0上,打开容器和不打开容器的区别表现在用户接口上仅仅是withContainer参数是否为true就好。代码如下:
InkWell( child: Container( color: Colors.yellow, child: Text( '打开外部路由', style: TextStyle(fontSize: 22.0, color: Colors.black), )), onTap: () => BoostNavigator.of().push("flutterPage", arguments: <String, String>{'from': widget.uniqueId}), ), InkWell( child: Container( color: Colors.yellow, child: Text( '打开内部路由', style: TextStyle(fontSize: 22.0, color: Colors.black), )), onTap: () => BoostNavigator.of().push("flutterPage", withContainer: true, arguments: <String, String>{'from': widget.uniqueId}), )
生命周期的精准通知
在FlutterBoost2.0上,每个页面都会收到页面生命周期通知,而FlutterBoost3.0只会通知页面可见性实际发生了变化的页面,接口也更符合flutter的设计。
Top Issue 解决
对于反馈比较多的issue进行了统计和归类,主要解决了以下issue
- 页面关闭后参数的传递,之前只有iOS支持,android不支持,目前在dart侧实现,Ios 和Android 都支持
- 解决了Android 状态栏字体和颜色问题。
- 解决了页面回退willpopscope不起作用问题。
- 解决了不在栈顶的页面也收到生命周期回调的问题
- 解决了多次setState耗性能问题。
- 提供了Framgent 多种接入方式的Demo,方便tab 场景的接入。
- 生命周期的回调代码,可以用户代码里面with的方式接入,使用更简单。
- 全面简化了,接入成本,包括 dart侧,android侧和ios
- 丰富了demo,包含了基本场景,方便用户接入 和测试回归。
FlutterBoost3.0 接入和使用
接入方式
flutter_boost: git: url: 'https://github.com/alibaba/flutter_boost.git' ref: 'v3.0-beta.3'
FlutterBoost的未来发展
后续会继续做这3件事情:
- FlutterBoost3.0会继续在单Engine方向完善和优化,让他更稳定,支持更多场景。
- 持续和Flutter 官方沟通,包括能否支持FlutterEngineGroup在isolate层面的内存共享。
- 探索Flutter2.0 多engine方案下新的混合栈。