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<RenderBox>(
  // 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<RenderBox>?);

if (isBootstrapFrame) {
   // 首帧主动更新一下,内部本质是调用
   // SchedulerBinding的scheduleFrame()方法。
   // 进而本质调用了window.scheduleFrame()方法。
  SchedulerBinding.instance!.ensureVisualUpdate();
}

}
}

复制代码
内部主要调用了RenderObjectToWidgetAdapter 类,进入类的内部

createElement 方法:每个widget都提供createElement方法,每个Element初始化时都必须有个widget参数,调用createElement时会把调用者传入

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
......
// 我们编写dart的runApp函数参数中传递的Flutter应用Widget树根,上一步传进来的
final Widget? child;
// 继承自RenderObject,来自PipelineOwner对象的rootNode属性,
// 一个Flutter App全局只有一个PipelineOwner实例
// 上一步传进来的
final RenderObjectWithChildMixin<T> container;
......
// 重写Widget的createElement实现,构建了一个RenderObjectToWidgetElement实例,它继承于Element。
// Element树的根结点是RenderObjectToWidgetElement。
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

// 重写Widget的createRenderObject实现,container本质是一个RenderView。
// RenderObject树的根结点是RenderView。
@override
RenderObjectWithChildMixin<T> 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<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? 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<T extends RenderObject> 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<T extends RenderObject> 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<ParentData>? 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<Element> children = List<Element>.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<Element?>(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<RenderBox>类型的Element作为根Element,并让Widget、renderView和BuildOwner和根Element产生关系,然后通过 mount 方法生成树形结构。
最后调用SchedulerBinding.instance.ensureVisualUpdate()函数,等待下一帧渲染
scheduleAttachRootWidget是一个耗时操作,异步运行。runApp会优先调用scheduleWarmUpFrame()渲染预热帧。

页面构建流程如下:

更新页面
在 Flutter 中我们直接通过 setState 方法来对页面进行刷新,所以直接查看源码,去掉了 assert 异常处理相关代码
abstract class State<T extends StatefulWidget> 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;

}
}
复制代码
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相关方法最后更新界面


陈进
10 声望1 粉丝