Flutter 页面构建渲染更新流程解析

1、UI 编程的两种方式
目前主流的UI编写方式主要有两种,命令式UI和声明式UI。在大家所熟悉的iOS中UI编程方式是命令式UI(Imperative UI),而声明式UI的意思就是让我们描述我们需要一个什么样的界面。在Flutter中就是采用了声明式UI(Declarative UI)
下面这样一个简单的例子:
在iOS中我们需要这么写
viewB.backgroundColor = [UIColor red];
for (UIView *view in viewB.subviews) {

[view removeFromSuperview];

}
UIView *viewC3 = [[UIView alloc] initWithFrame:CGRectMake(100,80,120,40)];
[viewB addSubview:viewC3];
复制代码
在Flutter声明式UI中我们只需要这样
return ViewB(
color: red,
child: ViewC(...),
)
复制代码
声明式 UI 相对来说减轻了开发者的负担,不需要考虑如何调用 UI 实例的方法来改变不同的状态,只需要开发者描述当前的 UI 状态 (即各属性的值),框架会自动将 UI 从之前的状态切换到开发者描述的当前状态。
2、Flutter渲染三棵树
在Flutter中我们几乎所有的代码都以Widget方式呈现,在官方文档中对Widget描述如下:
/// Describes the configuration for an [Element].
///
/// Widgets are the central class hierarchy in the Flutter framework. A widget
/// is an immutable description of part of a user interface. Widgets can be
/// inflated into elements, which manage the underlying render tree.
复制代码
主要包含以下几个信息
Widget是指一部分 UI 的描述
Widget是不可变的
Widget是对Element配置的描述,而Element管理着底层的渲染树
第一个信息显而易见,各个Widget会根据我们所写的属性 (在 Flutter 里指状态 State) 展示在屏幕上。那么第二个信息,如果Widget是不可变的,而且用的是声明式 UI,那么随着用户的操作或者数据的变更,UI 是怎么更新的,Element 与 render tree 又是什么
Flutter 渲染树简介
在Flutter中,除了Widget树,还有Element树和Render树,这三棵树各司其职,完成了Flutter的渲染更新。其主要功能如下:
Widget是Element的配置描述,持有公共属性和提供公开方法
Element是Widget在树中特定位置的一个实例,这个是真正的节点,用来关联Widget与渲染对象。每一个Widget都对应着一个Element,但是Widget实例会经常变动,但是渲染树不能经常改变,因此Element会尽可能改变而不是重新创建
Render树中的对象,主管渲染,测量自身Size并进行绘制放置子节点
页面创建过程
我们编写Flutter应用入口函数都是 main 方法,其内部调用了 runApp 方法,所以我们直接看 runApp方法的实现
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame();

}
复制代码
整个函数使用连级符调用了三个方法:
WidgetsFlutterBinding 初始化(ensureInitialized)
根节点核心三棵树绑定穿件工作(scheduleAttachRootWidget)
绘制热身帧
我们主要查看前两个函数
WidgetsFlutterBinding.ensureInitialized() 函数
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null) WidgetsFlutterBinding(); return WidgetsBinding.instance!;

}
}
复制代码
WidgetsFlutterBinding 继承自 BindingBase,并且 with 了大量的 mixin 类。可能有人还不熟悉 mixin 语法,这里简单介绍一下:mixins 的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用mixins实现类似多继承的功能。
通过 ensureInitialized() 方法我们可以得到一个全局单例的 WidgetsFlutterBinding 实例,且 mixin 的一堆 xxxBinding 也被实例化。 BindingBase 抽象类的构造方法中会调用 initInstances()方法,而各种 mixin 的 xxxBinding 实例化重点也都在各自的initInstances()方法中,每个 xxxBinding 的职责不同,具体如下:
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
WidgetsFlutterBinding:核心桥梁主体,Flutter app 全局唯一。
BindingBase:绑定服务抽象类。
GestureBinding:Flutter 手势事件绑定,处理屏幕事件分发及事件回调处理,其初始化方法中重点就是把事件处理回调_handlePointerDataPacket函数赋值给 window 的属性,以便 window 收到屏幕事件后调用,window 实例是 Framework 层与 Engine 层处理屏幕事件的桥梁。
SchedulerBinding:Flutter 绘制调度器相关绑定类,debug 编译模式时统计绘制流程时长等操作。
ServicesBinding:Flutter 系统平台消息监听绑定类。即 Platform 与 Flutter 层通信相关服务,同时注册监听了应用的生命周期回调。
PaintingBinding:Flutter 绘制预热缓存等绑定类。
SemanticsBinding:语义树和 Flutter 引擎之间的粘合剂绑定类。
RendererBinding:渲染树和 Flutter 引擎之间的粘合剂绑定类,内部重点是持有了渲染树的根节点。
WidgetsBinding:Widget 树和 Flutter 引擎之间的粘合剂绑定类。主要作用就是调度帧渲染任务,当然也可以运行非渲染任务。主要是瞬间渲染、持久渲染与渲染回调任务等,例如持久的帧渲染监听注册
我们主要看下渲染相关的 Binding
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances(); _instance = this; ...... /** *创建一个管理Element的类对象 *BuildOwner类用来跟踪哪些Element需要重建,并处理用于Element树的其他任务, 例如管理不活跃的Element等,调试模式触发重建等。 */ _buildOwner = BuildOwner(); // 回调方法赋值,当第一个可构建元素被标记为脏时调用。 buildOwner!.onBuildScheduled = _handleBuildScheduled; ......

}
}
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances(); _instance = this; /** * 创建管理rendering渲染管道的类 * 提供接口调用用来触发渲染。 */ _pipelineOwner = PipelineOwner( onNeedVisualUpdate: ensureVisualUpdate, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, ); // 一堆window变化相关的回调监听 window ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; // 添加页面刷新回调函数 addPersistentFrameCallback(_handlePersistentFrameCallback); // 创建RenderView对象,也就是RenderObject渲染树的根节点 initRenderView(); ......

}
void initRenderView() {
...... // 渲染树的根节点对象 renderView = RenderView(configuration: createViewConfiguration(), window: window); // renderView 添加到 _nodesNeedingLayout 和 _nodesNeedingPaint 列表 renderView.prepareInitialFrame();

}
RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
set renderView(RenderView value) {
assert(value != null); _pipelineOwner.rootNode = value;

}
}
复制代码
初始化过程我们已经得到了一些信息,注意两点:
RendererBinding中的RenderView就是RenderObject渲染树的根节点,代码中所有生成的 RenderObject 都会挂在到它的下面
WidgetsBinding内部的BuildOwner类是管理Element的类对象,用来跟踪哪些Widget需要重建
PipelineOwner是管理RenderObject类,提供接口调用用来触发渲染
接下来看初始化之后调用的函数scheduleAttachRootWidget
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
// 一个耗时操作,异步运行。runApp会优先调用 scheduleWarmUpFrame() 渲染预热帧 Timer.run(() { attachRootWidget(rootWidget); });

}
void attachRootWidget(Widget rootWidget) {
// 检测是否是第一帧 final bool isBootstrapFrame = renderViewElement == null; _readyToProduceFrames = true; // 桥梁创建RenderObject、Element、Widget关系树, // _renderViewElement值为attachToRenderTree方法返回值 // 继承自RenderObjectToWidgetElement,把他看成是一个普通的RenderObjectElement即可 _renderViewElement = RenderObjectToWidgetAdapter( // RenderObjectWithChildMixin类型,继承自RenderObject,RenderObject继承自AbstractNode。 // 来自初始化时候RendererBinding的_pipelineOwner.rootNode // 一个Flutter App全局只有一个PipelineOwner实例。 container: renderView, debugShortDescription: '[root]', // 我们平时写的dart Widget app child: rootWidget, // attach过程,buildOwner来自WidgetsBinding初始化时实例化的BuildOwner实例, // renderViewElement值就是_renderViewElement自己, // 此时由于调用完appach才赋值,所以首次进来也是null ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement?); if (isBootstrapFrame) { // 首帧主动更新一下,内部本质是调用 // SchedulerBinding的scheduleFrame()方法。 // 进而本质调用了window.scheduleFrame()方法。 SchedulerBinding.instance!.ensureVisualUpdate(); }

}
}
复制代码
内部主要调用了RenderObjectToWidgetAdapter 类,进入类的内部
createElement 方法:每个widget都提供createElement方法,每个Element初始化时都必须有个widget参数,调用createElement时会把调用者传入
class RenderObjectToWidgetAdapter extends RenderObjectWidget {
......
// 我们编写dart的runApp函数参数中传递的Flutter应用Widget树根,上一步传进来的
final Widget? child;
// 继承自RenderObject,来自PipelineOwner对象的rootNode属性,
// 一个Flutter App全局只有一个PipelineOwner实例
// 上一步传进来的
final RenderObjectWithChildMixin container;
......
// 重写Widget的createElement实现,构建了一个RenderObjectToWidgetElement实例,它继承于Element。
// Element树的根结点是RenderObjectToWidgetElement。
@override
RenderObjectToWidgetElement createElement() => RenderObjectToWidgetElement(this);
// 重写Widget的createRenderObject实现,container本质是一个RenderView。
// RenderObject树的根结点是RenderView。
@override
RenderObjectWithChildMixin createRenderObject(BuildContext context) => container;
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
/**
  • 上面代码片段中RenderObjectToWidgetAdapter实例创建后调用
  • owner来自WidgetsBinding初始化时实例化的BuildOwner实例,element 值就是自己。
  • 该方法创建根Element(RenderObjectToWidgetElement),
  • 并将Element与Widget进行关联,即创建WidgetTree对应的ElementTree。
  • 如果Element已经创建过则将根Element中关联的Widget设为新的(即_newWidget)。
  • 可以看见Element只会创建一次,后面都是直接复用的。
    */
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement? element ]) {
// 由于首次实例化RenderObjectToWidgetAdapter调用attachToRenderTree后才不为null,所以当前流程为null if (element == null) { // 在lockState里面代码执行过程中禁止调用setState方法 owner.lockState(() { // 创建一个Element实例 // 构建一个RenderObjectToWidgetElement实例, // 继承RootRenderObjectElement,又继续继承RenderObjectElement,接着继承Element。 element = createElement(); assert(element != null); // 给根Element的owner属性赋值为WidgetsBinding初始化时实例化的BuildOwner实例。 element!.assignOwner(owner); }); // 重点!mount里面RenderObject owner.buildScope(element!, () { element!.mount(null, null); }); } else { // 更新widget树时_newWidget赋值为新的,然后element数根标记为markNeedsBuild element._newWidget = this; element.markNeedsBuild(); } return element!;

}
......
}
复制代码
在创建页面时候BuildOwner.buildScope 方法可以简单的看成只执行了 block 方法,主要看 element 的 mount 方法
class RenderObjectToWidgetElement extends RootRenderObjectElement {
......
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); _rebuild();

}
...
}
复制代码
基类Element的mount方法,仅仅是把parent记录在此element中,更新slot和depth信息
abstract class Element extends DiagnosticableTree implements BuildContext {
void mount(Element? parent, Object? newSlot) {
...... // 查找父 element ,获取父element对自己的布局约束等 _parent = parent; _slot = newSlot; _lifecycleState = _ElementLifecycle.active; _depth = _parent != null ? _parent!.depth + 1 : 1; if (parent != null) { _owner = parent.owner; } final Key? key = widget.key; if (key is GlobalKey) { owner!._registerGlobalKey(key, this); } _updateInheritance();

}
}
复制代码
再看 _rebuild 方法
class RenderObjectToWidgetElement extends RootRenderObjectElement {
......
void _rebuild() {
try { // updateChild同样也是界面创建与刷新时的重要处理过程,后面会详细说明, // 这里只需要认为这里会进行子控件的添加,而且是递归添加处理,分别调用子控件的mount操作。 // 其中widget.child就是我们传入的Widget实例 _child = updateChild(_child, widget.child, _rootChildSlot); } catch (exception, stack) { final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'widgets library', context: ErrorDescription('attaching to the render tree'), ); FlutterError.reportError(details); final Widget error = ErrorWidget.builder(details); _child = updateChild(null, error, _rootChildSlot); }

}
...
}
复制代码
目前加载过程流程图如下:
创建时 updateChild 的操作
abstract class Element extends DiagnosticableTree implements BuildContext {
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
...... final Element newChild; // 第一次,Element child是null,执行else里的逻辑, // inflateWidget使用子widget来创建一个子Element if (child != null) { ...... } else { newChild = inflateWidget(newWidget, newSlot); } ...... return newChild;

}
Element inflateWidget(Widget newWidget, Object? newSlot) {
assert(newWidget != null); final Key? key = newWidget.key; // 如果 widget的Key是GlobalKey的话,会先从GlobalKey中获取引用的Element, // 如果有lement的话就更新复用 if (key is GlobalKey) { final Element? newChild = _retakeInactiveElement(key, newWidget); if (newChild != null) { assert(newChild._parent == null); assert(() { _debugCheckForCycles(newChild); return true; }()); newChild._activateWithParent(this, newSlot); final Element? updatedChild = updateChild(newChild, newWidget, newSlot); assert(newChild == updatedChild); return updatedChild!; } } // 否则就用 widget调用其createElement()来创建了一个element // Element初始化需要Widget参数,创建完成后newChild的widget参数就是newWidget final Element newChild = newWidget.createElement(); assert(() { _debugCheckForCycles(newChild); return true; }()); // 接着就调用新建的子element的mount方法 newChild.mount(this, newSlot); assert(newChild._lifecycleState == _ElementLifecycle.active); return newChild;

}
}
复制代码
mount方法比较复杂根据不同的 element 类型有几种分支, element 分为两种类型, RenderObjectElement 和 ComponentElement,具体类如下图
具体代码调用过程如下:ComponentElement类的mount方法
abstract class ComponentElement extends Element {
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); assert(_child == null); assert(_lifecycleState == _ElementLifecycle.active); _firstBuild(); assert(_child != null);

}
void _firstBuild() {
rebuild();

}
@override
void performRebuild() {
...... Widget? built; try { ...... // build函数为子类StatefulElement和StatelessElement的build方法 built = build(); ..... } catch (e, stack) { ..... } finally { ...... } ...... try { _child = updateChild(_child, built, slot); } catch (e, stack) { ...... } ......

}
}
复制代码
class StatelessElement extends ComponentElement {
......
@override
Widget build() => widget.build(this);
......
}
class StatefulElement extends ComponentElement {
......
@override
Widget build() => state.build(this);
......
}
复制代码
在 Flutter 里面最常见的 StatelessWidget 和 StatefulWidget 的 build 方法就是在这里被调用的。然后就又调用到了updateChild方法,这就回到了上边流程一直往下遍历创建widget树。
SingleChildRenderObjectElement类和MultiChildRenderObjectElement类都继承自RenderObjectElement先看下RenderObjectElement类的mount方法
abstract class RenderObjectElement extends Element {
@override
RenderObjectWidget get widget => super.widget as RenderObjectWidget;
@override
RenderObject get renderObject => _renderObject!;
RenderObject? _renderObject;
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); ...... // 通过widget树调用createRenderObject方法传入Element实例自己获取RenderObject渲染树。 _renderObject = widget.createRenderObject(this); attachRenderObject(newSlot); _dirty = false; ......

}
@override
void attachRenderObject(Object? newSlot) {
assert(_ancestorRenderObjectElement == null); _slot = newSlot; // 寻找可用的父RenderObject,再添加新的节点 _ancestorRenderObjectElement = _findAncestorRenderObjectElement(); _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot); final ParentDataElement? parentDataElement = _findAncestorParentDataElement(); if (parentDataElement != null) _updateParentData(parentDataElement.widget);

}
}
复制代码
SingleChildRenderObjectElement类和MultiChildRenderObjectElement类的mount方法
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); _child = updateChild(_child, widget.child, null);

}
}
class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); final List children = List.filled(widget.children.length, _NullElement.instance, growable: false); Element? previousChild; for (int i = 0; i < children.length; i += 1) { final Element newChild = inflateWidget(widget.children[i], IndexedSlot(i, previousChild)); children[i] = newChild; previousChild = newChild; } _children = children;

}
}
复制代码
通过代码可以看到 Element 调用mount()方法时
componentElement的mount方法主要作用是执行build(根据类型区分widget.build, state.build)
renderObjectElement 的mount方法主要作用是生成RenderObject
Element创建完成时就会调用 mount, 调用顺序为 mount -> _firstBuild -> reBuild -> performRebuild -> build
总结一下:updateChild是一个递归的过程,总结下来有下面几个步骤
Element如果是RenderObjectElement则创建RenderObject,并从祖先找到上一个RenderObjectElement,然后调用祖先RenderObjectElement的RenderObject的insertRenderObjectChild方法插入创建的RenderObject
如果子widget需要build出来就调用build方法创建子widget,如果不需要直接在成员变量可以拿到子widget
调用子widget的createElement创建子Element
调用子Element的mount方法将子Element的parent设置成自己,然后子Element去到第1步
从页面的创建过程可以看出,Widget、Element 和 Render,这三棵树的对应关系大概可以表示为:
每个Widget都有一个Element,Widget从渲染的角度进行分类,分为可渲染Widget与不可渲染Widget,只有生成Element对应为RenderObjectElement和它的子类才有RenderObject可以渲染到页面上,我们常用的 StatelessWidget 与 StatefulWidget 就属于不可渲染的Widge,具体对应关系如下:
WidgetElementRenderObjectStatelessWidgetStatelessElement-StatefulWidgetStatefulElement-ProxyWidgetProxyElement-InheritedWidgetInheritedElement-SingleChildRenderObjectWidgetSingleChildRenderObjectElementRenderObjectMultiChildRenderObjectWidgetMultiChildRenderObjectElementRenderObjectRenderObjectWidgetRenderObjectElementRenderObject
构建流程如下:
scheduleAttachRootWidget 函数完成Flutter App 中的 Widget、Element 和 RenderObject树生成和相互关联。在函数最后调用了SchedulerBinding.instance!.ensureVisualUpdate(); 通知Engine有UI需要更新渲染页面 (后面详细描述)
总结一下runApp方法的大体过程
调用runApp(Widget)函数传入一个Widget作为根Widget。 Widget只是一个配置类,不是实际的UI元素。
runApp通过WidgetsFlutterBindingmixIn继承一众父类进行初始化。
其中,RendererBinding 中的 renderView对象,是实际的渲染对象。
通过RenderObjectToWidgetAdapter类(继承自 RenderObjectWidget我们 runApp 中传递的 Widget 树就被追加到了这个树根的 child 属性上)生成一个RenderObjectToWidgetElement类型的Element作为根Element,并让Widget、renderView和BuildOwner和根Element产生关系,然后通过 mount 方法生成树形结构。
最后调用SchedulerBinding.instance.ensureVisualUpdate()函数,等待下一帧渲染
scheduleAttachRootWidget是一个耗时操作,异步运行。runApp会优先调用scheduleWarmUpFrame()渲染预热帧。
页面构建流程如下:
更新页面
在 Flutter 中我们直接通过 setState 方法来对页面进行刷新,所以直接查看源码,去掉了 assert 异常处理相关代码
abstract class State with Diagnosticable {
@protected
void setState(VoidCallback fn) {
final Object? result = fn() as dynamic; _element!.markNeedsBuild();

}
}
复制代码
直接调用 setState 传入的函数,然后调用 Element 的 markNeedsBuild 方法
abstract class Element extends DiagnosticableTree implements BuildContext {
void markNeedsBuild() {
if (_lifecycleState != _ElementLifecycle.active) return; .....if (dirty) return; _dirty = true; owner!.scheduleBuildFor(this);

}
}
复制代码
这里面将 Element 标记为 dirty,然后调用 BuildOwner 类的 scheduleBuildFor 方法,BuildOwner 实例在 WidgetsBinding 中初始化整个App中只有一个实例 继续查看:
class BuildOwner {
void scheduleBuildFor(Element element) {
...... // 判断 element 是否已经加入到 _dirtyElements 列表中, // 若是已经在列表中,就直接返回,不用再执行下面的操做 if (element._inDirtyList) { _dirtyElementsNeedsResorting = true; return; } // 判断 _scheduledFlushDirtyElements 是否为 false ,这个变量表示当前是否正在 rebuild // _dirtyElements 中的元素。若是没有正在 rebuild ,而且 onBuildScheduled 回调不为空 // 就调用 onBuildScheduled 函数 if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { _scheduledFlushDirtyElements = true; onBuildScheduled!(); }_dirtyElements.add(element); element._inDirtyList = true; ......

}
}
复制代码
这里将该element加入到_dirtyElements中,标记这个节点刷新时需要进行处理。onBuildScheduled 方法在初始化中设置的具体代码为 WidgetsBinding 中 buildOwner!.onBuildScheduled = _handleBuildScheduled; 具体代码如下
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void _handleBuildScheduled() {
ensureVisualUpdate();

}
}
复制代码
直接调用到SchedulerBinding类的ensureVisualUpdate方法
mixin SchedulerBinding on BindingBase {
void ensureVisualUpdate() {
switch (schedulerPhase) { case SchedulerPhase.idle: case SchedulerPhase.postFrameCallbacks: scheduleFrame(); return; case SchedulerPhase.transientCallbacks: case SchedulerPhase.midFrameMicrotasks: case SchedulerPhase.persistentCallbacks: return; }

}
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) return; ensureFrameCallbacksRegistered(); // 执行代码 // void scheduleFrame() => platformDispatcher.scheduleFrame(); // void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; // 调用 Flutter Engine 方法 window.scheduleFrame(); _hasScheduledFrame = true;

}
@protected
void ensureFrameCallbacksRegistered() {
// 调用 void scheduleFrame() native 'PlatformConfiguration_scheduleFrame' // 在下一个适当的机会调用 onBeginFrame 和 onDrawFrame 回调 () // onBeginFrame 主要进行是做一些准备工作,让framework准备好绘制工作,例如重新设置状态、变量等等 window.onBeginFrame ??= _handleBeginFrame; window.onDrawFrame ??= _handleDrawFrame;

}
void _handleDrawFrame() {
...... handleDrawFrame();

}
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); Timeline.finishSync(); // end the "Animate" phase try { // 执行 _persistentCallbacks 数组内的 callback // _persistentCallbacks 数组在初始化 WidgetsBinding 中 addPersistentFrameCallback 方法插入 _schedulerPhase = SchedulerPhase.persistentCallbacks; for (final FrameCallback callback in _persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp!); ...... } finally { ...... _currentFrameTimeStamp = null; }

}
}
复制代码
经过一系列的函数调用,调用 SchedulerBinding.instance.ensureVisualUpdate 最后会调用到WidgetsBinding 中 addPersistentFrameCallback 设置的方法
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame(); _scheduleMouseTrackerUpdate();

}
}
复制代码
drawFrame函数有两个,一个在 WidgetsBinding 内一个在
RendererBinding内,优先调用WidgetsBinding内的函数
@override
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void drawFrame() {
...... try { if (renderViewElement != null) buildOwner!.buildScope(renderViewElement!); super.drawFrame(); // 清理不再使用的 element buildOwner!.finalizeTree(); } finally { ...... } ......

}
}
复制代码
又调用到了buildOwner.buildScope方法,之前创建界面时调用了这个方法,现在刷新时也用到了,创建页面时只是简单的看成只执行了 block 方法,现在详细说明一下:
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback == null && _dirtyElements.isEmpty) return; ...... Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent); try { _scheduledFlushDirtyElements = true; if (callback != null) { ...... _dirtyElementsNeedsResorting = false; try { callback(); } finally { ...... } } // 首先将_dirtyElements进行排序,这是因为节点可能有很多个, // 如果其中两个节点存在级联关系,父级的Widget build操作必然会调用到子级的Widget build, // 如果子级又自己build一次,相当于出现了重复操作。因此通过深度排序就会避免这个问题 _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; int dirtyCount = _dirtyElements.length; int index = 0; // 对每一个Element进行遍历 while (index < dirtyCount) { ...... try { // 执行rebuild操作 _dirtyElements[index].rebuild(); } catch (e, stack) { ...... } index += 1; // 如果在遍历过程中增加了新的节点,那么就需要重新排序 if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) { _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; dirtyCount = _dirtyElements.length; while (index > 0 && _dirtyElements[index - 1].dirty) { index -= 1; } } } ...... return true; }()); } finally { // 所有Element都rebuild后,清空 _dirtyElements 集合,节点状态恢复正常 for (final Element element in _dirtyElements) { assert(element._inDirtyList); element._inDirtyList = false; } _dirtyElements.clear(); _scheduledFlushDirtyElements = false; _dirtyElementsNeedsResorting = null; Timeline.finishSync(); ...... } assert(_debugStateLockLevel >= 0);

}
复制代码
Element.rebuild()方法调用了子类的 performRebuild()方法ComponentElement类页面创建过程看到过,在更新时会再次调用
@override
void performRebuild() {
...... Widget? built; try { ...... // build函数为子类StatefulElement和StatelessElement的build方法 built = build(); ..... } catch (e, stack) { ..... } finally { ...... _dirty = false; } ...... try { _child = updateChild(_child, built, slot); } catch (e, stack) { ...... } ......

}
复制代码
最后还是回到 updateChild 方法,构建时只看了 child 为空的情况,现在看 child 不为空只更新的情况
abstract class Element extends DiagnosticableTree implements BuildContext {
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
// 如果不存在新的Widget,那么说明这一个节点应该取消掉了, // 执行deactivateChild 删除节点方法。 if (newWidget == null) { if (child != null) deactivateChild(child); return null; }final Element newChild; if (child != null) { bool hasSameSuperclass = true; if (hasSameSuperclass && child.widget == newWidget) { // 如果子节点的widget和新的widget一致(这里的一致指的是同一个对象) // 直接返回这个子节点。 if (child.slot != newSlot) updateSlotForChild(child, newSlot); newChild = child; } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { // 如果两个widget不是同一个对象,判断类型是否相同,通过canUpdate方法判断 // 依据是Widget类型一致,同时Key一致 // 这种情况下,只需要更新子节点 // 因此这一步就是widget变更,但是element不变更 if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); assert(child.widget == newWidget); assert(() { child.owner!._debugElementWasRebuilt(child); return true; }()); newChild = child; } else { // 其它情况下则认为子节点是新增的,先删除原来的再建新的 // 调用`inflateWidget`进行子节点创建 // 里面与创建界面相同,执行了mount操作 deactivateChild(child); assert(child._parent == null); newChild = inflateWidget(newWidget, newSlot); } } else { ...... } ...... return newChild;

}
}
复制代码
element.update方法会把newWidget记录下来
abstract class Element extends DiagnosticableTree implements BuildContext {
@mustCallSuper
void update(covariant Widget newWidget) {
_widget = newWidget;

}
}
复制代码
StatelessElement.update方法会调用rebuild,rebuild中会调用performRebuild()去重建其子widget,类似一个递归的流程
class StatelessElement extends ComponentElement {
@override
void update(StatelessWidget newWidget) {
super.update(newWidget); _dirty = true; rebuild();

}
}
复制代码
StatefulElement.update方法,先回调state.didUpdateWidget(这里就是我们在自定义Widget写的生命周期回调函数就是在这里触发的),最后又调用rebuild
@override
class StatefulElement extends ComponentElement {
void update(StatefulWidget newWidget) {
super.update(newWidget); final StatefulWidget oldWidget = state._widget!; _dirty = true; state._widget = widget as StatefulWidget; try { final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; } finally { } rebuild();

}
}
复制代码
SingleChildRenderObjectElement.update方法,调用 updateChild
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget); assert(widget == newWidget); _child = updateChild(_child, widget.child, null);

}
}
复制代码
MultiChildRenderObjectElement.update方法,调用 updateChildren,内部循环调用updateChild方法
class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void update(MultiChildRenderObjectWidget newWidget) {
super.update(newWidget); assert(widget == newWidget); _children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren); _forgottenChildren.clear();

}
}
复制代码
RenderObjectElement.update方法,update方法里面只是更新widget的配置,这里会对 renderObject进行修改
abstract class RenderObjectElement extends Element {
@override
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget); widget.updateRenderObject(this, renderObject); _dirty = false;

【Flutter 页面构建渲染更新流程解析】}
}
复制代码
RenderObjectElement 的 performRebuild方法
abstract class RenderObjectElement extends Element {
@override
void performRebuild() {
widget.updateRenderObject(this, renderObject); _dirty = false;

}
}
复制代码
widget.updateRenderObject每一种RenderObjectElement都会有自己的updateRenderObject处理方式,处理完成后如果需要重新计算大小宽高就会加到 PipelineOwner 的 _nodesNeedingLayout列表中,如果需要重新绘制就加到 PipelineOwner 的 _nodesNeedingPaint 列表中
剩下执行super.drawFrame() 这行代码就是调用 RendererBinding 类的 drawFrame 函数
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@protected
void drawFrame() {
assert(renderView != null); // 调用 RenderView.performLayout(),遍历子节点,子节点在widget.updateRenderObject已经加入到列表内 // 调用每个节点的 layout(),RenderObject的排版数据,使得每个RenderObject最终都能有正确的大小和位置 pipelineOwner.flushLayout(); // 更新渲染对象,此阶段每个渲染对象都会了解其子项是否需要合成 // 在绘制阶段使用此信息选择如何实现裁剪等视觉效果 pipelineOwner.flushCompositingBits(); // 会调用 RenderView.paint() 最终触发各个节点的 paint(),最终生成一棵Layer Tree,并把绘制指令保存在Layer中 pipelineOwner.flushPaint(); if (sendFramesToEngine) { // 把Layer Tree提交给GPU renderView.compositeFrame(); // pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. _firstFrameSent = true; }

}
}
复制代码
总结一下 setState 过程
首先调用 markNeedsBuild 方法,将 element 的 dirty 标记为 true,表示需要重建
接着调用 scheduleBuildFor ,将当前的 element 添加到 _dirtyElements 列表中
调用buildOwner.buildScope,函数内部对 _dirtyElements 列表中的 element 调用 rebuild 函数
rebuild 函数调用 updateChild 循环更新子 element
RenderObjectElement 调用 updateRenderObject ,对 RenderObject 更新
最后调用 pipelineOwner相关方法最后更新界面

    推荐阅读