为什么需要 Flutter
Flutter 通过自建绘制引擎,具备与 Native 相媲美的性能,同时能够磨平平台开发的差异性。
Flutter 架构
Flutter 架构可分为 Dart APP 层、Flutter 框架层、引擎层(面向操作系统的 C++ 引擎以及面前 Web 的 Web 引擎)、平台嵌入层。
Flutter 框架层
Flutter 框架层实现一套基础库用于处理动画、绘图和手势,在这之上 Rendering 层用于组件的底层渲染的结构数据,Widgets 层面向开发者提供一套描述视图结构的语言,并基于绘图封装了两种风格的 UI 组件库
底层引擎层
引擎层可分为 C++ 引擎和 Web 引擎,其中 C++ 引擎包括 Skia 渲染引擎、Dart 运行时以及文字排版引擎等
设计思想
Widget 是视图的基本单元和配置信息
视图以 Widget 为单位,Widget 的状态描述了页面的状态,状态发生变化,Widget 会重新构建 Widget。
Widget 有以下特点:
-
widget 是不可变的
Flutter 中 widget 重建是频繁发生,因此经常需要进行新旧 widget 树的比较。为了降低比较时间采用浅比较,widget 的 Immutable 特性能够很好契合这一使用场景。只要 widget 发生变化,就会返回一个新的 widget,从旧的 widget 创建一个新的 widget 采用了优化算法,达到快速比较和快速创建的性能
最小颗粒度更新渲染树
Flutter 会比对前后状态的变化,以确定底层渲染树从一个状态切换到下一个状态所需要的最小改变。
渲染流水线
Flutter 渲染视图的本质就是在 Vsync 信号中,通过 UI 线程快速的构建并生成抽象的视图结构数据,并将抽象视图结构数据发送给 GPU 线程,GPU 线程将视图结构数据进行光栅化和上屏;
首先从宏观上理解 Flutter 的渲染流水线:
- Flutter 引擎启动时,通过平台线程,向系统注册接收 Vsync 信号的回调
- UI 线程接收到 Vsync 信号后,会构建三棵树(widget/element/RenderObject)并生成布局信息,这一工作主要在 Flutter 框架层处理
- 根据布局信息进行绘制,生成一系列的绘制指令组成 layer tree,这一工作主要在 Flutter 框架层处理
- GPU 线程从 UI 线程中获取 layer tree,进行光栅化和上屏操作,这一工作主要在底层引擎层处理
- 然后请求接收系统下一次的 Vsync 信号,又回到第 2 步
线程模型
理解 Flutter 的渲染流水线,需要先理解 Flutter 的线程模型,Flutter 的四大线程配合来完成渲染 UI 工作:
值得注意的是,Flutter Engine自己不创建管理线程。Flutter Engine线程的创建和管理是由embedder负责的。Embeder指的是将引擎移植到平台的中间层代码。
- Platform 线程: Flutter 线程模型中的主线程,负责提供 Native 窗口,作为 GPU 渲染的目标。接受平台的 VSync 信号并发送到 UI 线程,驱动渲染管线运行。
- UI 线程: 负责 UI 组件管理,维护 3 颗树,Dart VM 管理,UI 渲染指令生成。同时负责把承载渲染指令的 LayerTree 提交给 GPU 线程去光栅化。
- GPU 线程: 通过 flow 模块完成光栅化,并调用底层渲染 API(opengl/vulkan/meta),合成并输出到屏幕。
- IO 线程: 包括若干 worker 线程会去请求图片资源并完成图片解码,之后在 IO 线程中生成纹理并上传 GPU ,由于通过和 GPU 线程共享 EGL Context,在 GPU 线程中可以直接使用 IO 线程上传的纹理,通过并行化,提高渲染的性能
UI 线程
当用于描述 UI 的 widget 树发生变化,会触发 build 阶段,build 阶段后会进入 layout 阶段生成 RenderObject tree,layout 完后进入 paint 阶段生成绘制指令 layer tree。
build 阶段
Flutter 对视图结构的抽象描述采用了三棵树:widget tree、element tree 和 render tree。可把它们类比成:
widget tree => jsx
element tree => virtual dom
render tree => rendering tree (dom + cssom)
widget tree
widget tree 是 Dart 代码中所写的组件结构,描述 UI 元素的配置信息,widget 是 immutable 的,如果 widget 的状态发生更新,则会进行重建。实际业务场景中, widget 会频繁进行重建;
element tree
element tree 是 widget 的一种抽象,是视图中真正显示的元素,也是连接 widget tree 和 render tree 的桥梁;
当 widget 挂载到 widget tree 的时候,会调用 widget.createElement() 方法,创建其对应的 Element。Flutter 再将这个 Element 挂载到 element tree 并持有创建它的 widget 的引用
当 widget 发生变化时,会将其 element 标记为 dirty element,在下一次更新视图时根据这个状态只更新被修改的内容,从而提升渲染性能;
element 的更新时机是根据 widget 的两个属性进行的:
- widget 的类型,前后组件是否为同一个类所创建
- widget key
如果二者都没发生改变,则会复用该 element,并将新的 widget 配置数据更新到 element;
render tree
render tree 是真正进行组件布局渲染的工作,render tree 每个节点都是继承自 RenderObject 类对象,其由 Element 中的 renderObject 或 RenderObjectWidget 的 createRenderObject 方法生成;
布局阶段
UI 线程根据 RenderObject tree 进行布局,具体可分为两个线性过程:从顶部往下传递布局约束,从底部往上传递布局信息:
绘制阶段
UI 线程根据 RenderObject tree 生成绘制指令并形成 layer tree
状态管理
StatefulWidget
Flutter 的 widget 可分为 StatefulWidget 和 StatelessWidget。
-
stateless widget
stateless widget 没有自己的内部状态,只用于展示从外部传递的或是内部的静态数据
-
stateful widget
stateful widget 拥有自己的状态用于对外部做出反馈,widget 的状态保存在一个 State 对象中,并通过 setState 去重构 widget
StatefulWidget 的生命周期
- 当 stateful widget 挂载到 widget 树时,会调用 widget.createState() 方法
- 新创建的 State 会和 BuildContext(widget 对应的 element) 关联,此时 initState 会调用
- 当 statefulWidget 使用 InheritedWidget 的数据时,并且 InheritedWidget 的 updateShouldNotify() 返回 true 时,说明 statefulWidget 的依赖数据发生变化,因此会触发 didChangeDependencies()
- 进入 build 阶段构建视图
- 当 State 暂时从视图树中移除或是页面切换时,该函数就会调用
- 当 State 被永久从视图树中移除,该函数会调用
- 当 widget 的配置发生变化时就会调用这个函数,例如 hot-reload
- setState 会重新触发 build
理解 setState 的更新机制
setState()过程主要工作是记录所有的脏元素,添加到BuildOwner对象的_dirtyElements成员变量,然后调用scheduleFrame来注册Vsync回调。 当下一次vsync信号的到来时会执行handleBeginFrame()和handleDrawFrame()来更新UI。
状态管理机制
-
widget 自己管理自己的状态
状态是 UI 状态,交由 widget 自身管理
-
父 widget 管理子 widget 状态
状态是用户数据,交由父 widget 管理
混合管理
widget 间的通信机制
-
父 widget 向子 widget 通信
通过实例化子 widget 并向子 widget 传值到子 widget 的实例属性
-
子 widget 向父 widget 通信
通过设置回调函数形式
-
兄弟组件间通信
- 通过将数据挂载到同一个父组件
- 数据共享的数量较少,使用 InheritedWidget(React Context)
-
跨组件通信
-
从上往下
InheritedWidget
class ShareDataWidget extends InheritedWidget { ShareDataWidget({ @required this.data, Widget child }) :super(child: child); final int data; //需要在子树中共享的数据,保存点击次数 //定义一个便捷方法,方便子树中的widget获取共享数据 static ShareDataWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType
(); } //该回调决定当data发生变化时,是否通知子树中依赖data的Widget @override bool updateShouldNotify(ShareDataWidget old) { //如果返回true,则子树中依赖(build函数中有调用)本widget //的子widget的`state.didChangeDependencies`会被调用 return old.data != data; } } class _TestWidget extends StatefulWidget { @override __TestWidgetState createState() => new __TestWidgetState(); } class __TestWidgetState extends State<_TestWidget> { @override Widget build(BuildContext context) { //使用InheritedWidget中的共享数据 return Text(ShareDataWidget .of(context) .data .toString()); } @override void didChangeDependencies() { super.didChangeDependencies(); //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。 //如果build中没有依赖InheritedWidget,则此回调不会被调用。 print("Dependencies change"); } } class InheritedWidgetTestRoute extends StatefulWidget { @override _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState(); } class _InheritedWidgetTestRouteState extends State { int count = 0; @override Widget build(BuildContext context) { return Center( child: ShareDataWidget( //使用ShareDataWidget data: count, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(bottom: 20.0), child: _TestWidget(),//子widget中依赖ShareDataWidget ), RaisedButton( child: Text("Increment"), //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新 onPressed: () => setState(() => ++count), ) ], ), ), ); } } -
从下往上
Notification 通知冒泡
class MyNotification extends Notification { final String msg; Mynotification(this.msg); } class NotificationRoute extends StatefulWidget { @override NotificationRouteState createState() { return new NotificationRouteState(); } } class NotificationRouteState extends State
{ String _msg = ""; @override Widget build(BuildContext context) { return NotificationListener ( onNotification: (notification) { setState(() => { _msg += notification.msg + " "; }) return true; }, child: RaisedButton( onPressed: () => MyNotification('hi'),dispatch(context), child: Text('Send Notification'), ), ); } }
-
-
Event Bus
适用于共享数据量较少的跨组件通信场景
-
状态管理工具
- flutter_redux
- flutter_mobx
- bloc
参考资料
Flutter原理:三棵重要的树(渲染过程、布局约束、应用视图的构建等)
硬件绘图原理
Flutter 实战
Flutter 跨平台演进及架构开篇
Flutter UI 绘制原理引导
Flutter WebView 与 JS 通信
Flutter WebView与JS交互简易指南
Dart