由于Flutter框架出色的UI渲染能力,多平台一致性,大大的提高了研发效率,降低了人力成本。越来越多的厂商开始接入Flutter,但是很多厂商都是成熟的App,完全从头使用Flutter开发应用不现实,采用混合开发则是一种非常好的切入方式。
那混合栈管理则是一个避免不了的话题。
本文从0到1的角度阐释Flutter混合栈实现思路,介绍关键技术点而忽略部分细节,让大家从全局认识Flutter混合栈管理。
一、引导问题
- 为什么同一个原生页面不能显示两个及以上的flutter页面?
- 为什么在使用flutterboost时不能用官方的Navigator的API?
二、页面栈结构分析
常见的几种flutter页面与原生页面混合的情况如下:
- 交替出现:原生页面与flutter页面交替出现
- 连续出现:多个flutter页面连续出现
- 同时出现:原生页面与flutter页面在子tab中同时出现
- 复合情况:上述情况组合出现,页面结构比较复杂
2.1 标准栈的情况
对于1,2两种情况比较简单,可以抽象成两个比较标准的栈结构,如下图:
对于Android侧:页面栈结构无需做任何改变,直接复用原生页面栈管理即可。
对于Flutter侧:页面栈结构可以由Navigator实现,也是可以直接使用现有的栈管理即可。
关键点:
- Flutter与Activity都可以复用各自的线性栈结构功能
- Flutter与Activity(或Fragment)的页面映射关系实现
- 连续多个Flutter页面使用一个原生容器优化内存
2.2 同级Tab的情况
对于情况3,多个Flutter页面同级可以分为两种情况:
- 多个Flutter需要同时显示页面内容,会同时出现,如Pad情况
由于一个FlutterEngine同一时刻只能渲染一块画布FlutterView,故如果需要显示这种页面结构,则必须使用多引擎。
如富途牛牛Pad的情况,FlutterPage1所在主屏需要一个独立的引擎渲染,FlutterPage2所在副屏使用一个独立引擎并关联其上的其他页面栈,则可以转换为标准的栈结构情况。
- 多个Flutter页面不需要同时显示页面内容,会交替出现,如手机App一级主tab,二级页面的子Tab
由于页面不会同时出现,这样的情况可以使用单引擎来实现,可以减少不必要的内存开销,也可以避开多引擎内存无法共享的问题。
由于有多个同级的FlutterPage页面,flutter侧不能简单的使用Navigator线行栈结构实现。
对于这种情况更好的做法是:抛弃强耦合的线性栈结构,用key-value的形式映射Flutter页面与原生页面,哪个原生页面在前台则渲染其对应的FlutterPage即可。
关键点:
- 在Flutter页面交替出现时单引擎如何渲染不同的Flutter页面
- 原生页面与Flutter页面实现k-v映射关系
2.3 复合情况
对于复合情况,这里只考虑Flutter页面交替出现的场景,对于同时显示的页面需要使用到多引擎,页面管理更复杂,本次暂不考虑。
这种复合情况可以看组1,2,3种情况的结合体,将上述两小节的关键点合考虑即可。
2.4 总结
综合上述关键点,得出我们需要的栈管理结构需要支持一下的关键点:
- 内存优化与共享:使用单引擎,需要解决在页面交替出现时单引擎如何渲染不同页面
- 连续多个Flutter页面:使用单个原生容器承载,内部可以使用Navigator管理多个Flutter页面
- 不连续的Flutter页面:一个Flutter页面一个原生页面(Fragment/Activity),栈结构由原生维护,原生页面与Flutter页面实现k-v映射关系(可以由Overlay实现)。
整个栈结构可以抽象成如下结构图:
三、页面功能分析
混合栈管理的主要功能是路由管理和页面生命周期管理,实现这两项能力就完成了混合栈管理的主体框架,后续可以在此基础上进行拓展,丰富使用能力。
3.1 页面导航
页面导航的基础能力:
- push:打开一个新的Flutter页面并携带参数,需要考虑内部路由情况
- pop:关闭一个Flutter页面并携带参数,需要考虑内部路由情况
打开页面可以分两种:
- 原生侧触发打开页
- Flutter侧触发打开页
路由管理:
- 注册路由
- 路由表管理
3.2 页面生命周期
页面生命周期的基础能力:
- onPageShow:页面显示
- onPageHide:页面隐藏
- onBackground:App进入后台
- onForeground:App回到前台
由于上节页面栈结构分析可知,原生页面与Flutter页面是一对多关系,由于多个连续的Flutter页面在同一个原生容器内并有FlutterNavigator管理,故可以看成一个整体。
所以,页面生命周期分发关系如下:
- 原生页面与Flutter页面一对一,将原生页面生命周期事件通知给Flutter页面即可。
- 原生页面与Flutter页面一对多,将原生页面生命周期事件通知给Flutter页面管理层,由管理层再次分发给到Flutter栈顶页面,故将一对多关系转化为一对一关系。