flutter widget 体系
flutter 中的 WidgetsFlutterBinding 集成了 GestureBinding、ServicesBinding、SchedulerBinding、PaintingBinding、SemanticsBinding、RendererBinding、WidgetsBinding 等 7 种 Binding,它们都有自己在功能上的划分,其中,WidgetsBinding 主要负责的是 widget 处理相关的,与之密切相关的是 widget/element 树,WidgetsBinding 所做的就是 widget 与 element 之间的转换,以及 element 的处理,RenderObject 的生成等。
首先从 initInstances 看起:
void initInstances() {
super.initInstances();
_instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
这里主要就是声明了几个回调函数,分别是:
-
onBuildScheduled 刷新视图时调用,用于请求下一帧
onLocaleChanged Locale 变化时的回调,重新设置当前整个 widget 树的 locale,这会改变所有文字类 widget 的 text 等
-
onAccessibilityFeaturesChanged 一些全局性的配置变化时的回调,比如系统设置里面的颜色反转、动画禁用等
-
_handleNavigationInvocation 路由发生变化时的回调,这个多用于在 native 环境中切换 flutter 路由时使用
-
_handleSystemMessage 接收 system message 时的回调,目前只有 memoryPressure 这一项
从介绍来看,以上几个回调都是与 widget 树的构建息息相关的,其回调也一般会需要重建部分的 widget 结点,所以被放到了 WidgetsBinding 中,与其他几个 Binding 不同的是,WidgetsBinding 的初始化相对来说比较精简,可能这是因为还有一部分的初始化放在了后面。
在 runApp 中连续调用了 WidgetsFlutterBinding 的三个函数,第一个函数 ensureInitialized 对应的就是各个 Binding 类的 initInstance 函数,而后面还有两个函数,attachRootWidget 和 scheduleWarmUpFrame,后者在 SchedulerBinding 中已经有讲过,是用于第一次刷新视图用的,而在此之前的 attachRootWidget,则是用于将用户自定义的 widget 加载到整个 widget 树中的,它的实现就在 WidgetsBinding 中。
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
这个过程传入了两个参数,renderView 和 buildOwner,而 RenderObjectToWidgetAdapter 本身又是一个 widget,所以,总的来说,从这一步开始,RenderObject、BuildOwner 和 Widget 之间关联了起来,并且 renderView 带来了 render 树的根结点,使得 widget 树创建出的 RenderObject 都能够被加载到 render 树上,BuildOwner 类似于 render 树中的 PipelineOwner,负责总管 widget 树的构建、刷新等等,至此,整个结构可以基本构建完成,从用户着手的 widget 开始,通过生成 element 树对数据进行管理,然后确定需要生成的 render 树,最后由 render 树完成整体的渲染。
那么这里需要先说一说在 flutter 中的“树结构”,在 flutter 中其实有很多的树结构,组成这些树的就是一个一个的结点,包括 widget、element 在内的都是结点,而树结构又包括 widget 树、element 树、render 树、layer 树,以及 semantic 体系中的一些,_SemanticsFragment 树、SemanticsNode 树等,很多。在 RendererBinding 中讲了很多 render 树到 layer 树的转换,而前面三种树的转换,主要就是在 WidgetsBinding 里面进行的。
WidgetsBinding 也继承了 drawFrame 函数,回想 RendererBinding 中的 draw 函数,那是直接从 RenderObject 的 layout 开始执行的,但是在此之前需要先有一个生成 RenderObject 并将其标记为 needLayout 状态的过程,这个过程就是 WidgetsBinding 的 drawFrame。
不过在此之前,先对 widget 树中的结点进行一下划分,方面理解之后的整个流程。
widget 种类
首先,所有的 widget 都继承自基类 Widget,从 Widget 往下,它有四个子类,StatelessWidget、StatefulWidget、ProxyWidget 和 RenderObjectWidget。
StatelessWidget/StatefulWidget
StatelessWidget 是一个无状态的 Widget,也就是说它内部无法保存/生成数据,只能根据初始化的时候传进来的值来确定自己将要展示的内容,与之对应的 Element 是 StatelessElement,而 StatefulWidget 是一个有状态的 Widget,它可以通过 State 保存数据,并在数据更新的时候可以调用 setState 完成数据的重新加载,最终更新界面,与之对应的 Element 是 StatefulElement。
同时,StatelessWidget 和 StatefulWidget 都是组合类的 widget,也就是说它们本身一般不会生产出具体的内容,而是通过组合其他 widget,将其他 widget 组成一个复杂的 widget 来展示复杂的内容,比如通过加入 Text 来显示文字,通过加入 Image 显示图片,再通过加入 Container 对内部的 widget 位置/大小进行设置,最终形成一个完整的页面,比如一般开发的 flutter 应用的根结点,就是继承自 StatelessWidget 实现的。
由于 flutter 是一个将大部分功能都抽象成 Widget 实现的,如果要在页面中进行数据的管理,单靠 Widget 是完成不了的,所以就需要引入 StatefulWidget,StatefulWidget 中引入了 State 进行状态管理,包括子 widget 的组合,也都转移到这里来做,并且它提供给了 State 一个完整的生命周期,比如 Widget 的 mount 阶段,对应着 State 的 initState,unmount 对应着 dispose,加上这些之后,State 就可以有一个完整的状态管理能力了—它能够找到合适的时机进行数据的初始化和释放,但是还差最重要的一点,如何将改变后的数据加载到 Widget 中,这就需要引入 setState 函数,调用了这个函数之后,该 State 对应的 Element 结点就会进行更新,创建新的 Widget。
ProxyWidget
ProxyWidget 是代理类的 widget,本身不提供 widget,它与 StatelessWidget 有点类似,都是只能返回其他的 Widget,但是后者能够提供一个组合效果,但 ProxyWidget 更多的是用于给某些 widget 提供附加信息,或者在整个 widget 树中附加信息。
比如它的最常用的一个子类,InheritedWidget 是 flutter widget 树中自上而下传递数据的类,可以将其看作是一个数据容器,简单而言,所有的 InheritedWidget 都会被传递给自己的子结点,并且然后一步步向下传递,使得子结点能够向上追溯,我们平常使用的 MediaQuery.of 等都是使用这种方式从上层结点取出数据的。
再比如 ParentDataWidget,跟 InheritedWidget 类似都是可以给子结点提供数据,但是与 InheritedWidget 的灵活不同,ParentDataWidget 只能沿着树向上寻找,但是更加简单,不需要依赖 InheritedWidget 的 runTime,从某种角度来看,它像是一种特化的 InheritedWidget。
RenderObjectWidget
RenderObjectWidget 是 widget 树中负责具体产出的 widget,它是构成整个页面的基础单元,每一个 RenderObjectWidget 都是对应着一个 RenderObject,比如 RawImage 是最基础的图片展示 widget,它对应着 RenderImage,上面的 StatelessWidget/StatefulWidget/ProxyWidget 本质上都是功能型 widget,它们负责组合/粘合 widgets,以保证最终的界面是符合预期的。
RenderObjectWidget 有两个标准函数,createRenderObject 和 updateRenderObject,前者是在 mount 阶段创建出对应的 RenderObject,后者用于 update 阶段更新 RenderObject 的参数,再将其细分一点,RenderObjectWidget 有叶子结点、单子结点、多子结点三种分类,三者主要是在处理子结点上的实现有所不同。
widget 树原理
以上就是 widget 的大致介绍,所以 widget 树的大致工作原理可以描述如下:
首先,开发者以某一个 Widget(一般是 StatelessWidget)为根结点,开始定义一个个页面,这些页面共同组成了 widget 树,widget 树是存在于代码中的,每一个 widget 都是一个结点,它的 build 函数返回的便是自己的子结点。
程序开始运行之后,以 attachRootWidget 开始,将 widget 树转化成 element 树,Widget 与 Element 是一一对应的关系,element 树是存在于运行过程中的动态的树,一方面它通过 widget 承载具体的绘制信息,另一方面 element 本身负责管理 widget 以及协调与子结点的关系等,可以说 element 维持了树的结构,并负责重建树等。element 树的存在更多是为了方便树的管理,它是由 widget 树直接映射过来的,每一个 element 结点对应一个 widget 结点,每一个 element 的子结点也都是由 widget 子结点转换而来。
再下一步,element 树虽然代表了整个页面的结构,但是这里面还有很多 ProxyElement 类型的功能型结点,所以在执行绘制之前,还需要进行一次转换,只保留 RenderObjectWidget 对应的结点。转到 RenderObject 树的过程与转 element 的过程其实是同步执行的,在 RenderObjectElement 的 mount 过程中,会进行 RenderObject 的创建以及树的建立。在运行过程中与 RenderObject 进行沟通主要还是通过 RenderObjectWidget 的 updateRenderObject 函数,它会将数据传递给 RenderObject。在 RenderObject 数据变更的时候,它会自行调用 markNeedsPaint、markNeedLayout 等标记脏结点。
attachRootWidget
了解了 widget 树的一些大致原理后,再看 flutter App 的初始化过程,attachRootWidget 这一步是将我们自定义的根结点与 RenderView 和 BuildOwner 联系起来,进而创建出一个初始的 element 树的过程。
首先创建出 RenderObjectToWidgetAdapter,它能够将一个 RenderObject 包装成 Widget,也就是将 RenderView 包装成 Widget 成为 widget 树的根结点,然后成为 RenderOjbect 树的根结点。
然后调用 attachToRenderTree 创建树。
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
初始状态下 element 为 null,所以会先创建 element,然后调用 buildScope,这个函数用于重建 element 树,它会调用所有脏结点的 rebuild 函数,同时它还允许传入一个回调函数,在处理脏结点之前执行,这里调用的 mount 函数,会直接建立 element 树。
mount
根据已知信息,根结点对应的是 RenderObjectToWidgetAdapter,它创建的是 RenderObjectToWidgetElement。它的 mount 实现如下:
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
_rebuld 会直接创建子结点,它会调用 updateChild,然后通过 inflateWidget 创建出 element。
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
return child;
}
deactivateChild(child);
}
return inflateWidget(newWidget, newSlot);
}
具体逻辑如上,它会根据 newWidget(新创建出的 widget)和 child(旧的 element,第一次调用为 null)的值判断是否复用旧的 element,创建新的等,
Element inflateWidget(Widget newWidget, dynamic newSlot) {
assert(newWidget != null);
final Key key = newWidget.key;
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;
}
}
final Element newChild = newWidget.createElement();
assert(() { _debugCheckForCycles(newChild); return true; }());
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
新创建的一个 element 的逻辑如上,它也会调用 mount 函数,构建自己的子结点,过程类似。
向上追溯,RenderObjectToWidgetElement 的父类 RenderObjectElement 中 mount 的实现如下:
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
assert(() { _debugUpdateRenderObjectOwner(); return true; }());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
可以看到 RenderObject 就是在 RenderObjectElement mount 过程中创建的,然后调用 attachRenderObject 将当前的 RenderObject 挂载到 RenderObject 树中。
void attachRenderObject(dynamic newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
_findAncestorRenderObjectElement 能够沿着 element 树向上找到一个父结点, 然后 insertChildRenderObject 负责将当前结点插入到父结点中,关于这个函数的实现,在三种不同的 RenderObjectWidget 中的实现不同。以 SingleChildRenderObjectElement 为例,
void insertChildRenderObject(RenderObject child, dynamic slot) {
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject;
assert(slot == null);
assert(renderObject.debugValidateChild(child));
renderObject.child = child;
assert(renderObject == this.renderObject);
}
set child(ChildType value) {
if (_child != null)
dropChild(_child);
_child = value;
if (_child != null)
adoptChild(_child);
}
void adoptChild(RenderObject child) {
assert(_debugCanPerformMutations);
assert(child != null);
setupParentData(child);
markNeedsLayout();
markNeedsCompositingBitsUpdate();
markNeedsSemanticsUpdate();
super.adoptChild(child);
}
void adoptChild(covariant AbstractNode child) {
assert(child != null);
assert(child._parent == null);
assert(() {
AbstractNode node = this;
while (node.parent != null)
node = node.parent;
assert(node != child); // indicates we are about to create a
return true;
}());
child._parent = this;
if (attached)
child.attach(_owner);
redepthChild(child);
}
以上就是 SingleChildRenderObjectElement 中 insertChildRenderObject 函数的整个调用链,总的来说,它将子结点与父结点建立联系,同时将其标记为脏结点,最后,attach 函数,负责传给子结点 owner,也就是所有 RenderObject 共享的 PiplineOwner,负责管理 RenderObject 的重绘等。
void attach(PipelineOwner owner) {
super.attach(owner);
// If the node was dirtied in some way while unattached, make sure to add
// it to the appropriate dirty list now that an owner is available
if (_needsLayout && _relayoutBoundary != null) {
// Don't enter this block if we've never laid out at all;
// scheduleInitialLayout() will handle it
_needsLayout = false;
markNeedsLayout();
}
if (_needsCompositingBitsUpdate) {
_needsCompositingBitsUpdate = false;
markNeedsCompositingBitsUpdate();
}
if (_needsPaint && _layer != null) {
// Don't enter this block if we've never painted at all;
// scheduleInitialPaint() will handle it
_needsPaint = false;
markNeedsPaint();
}
if (_needsSemanticsUpdate && _semanticsConfiguration.isSemanticBoundary) {
// Don't enter this block if we've never updated semantics at all;
// scheduleInitialSemantics() will handle it
_needsSemanticsUpdate = false;
markNeedsSemanticsUpdate();
}
}
在一个 RenderObject 结点执行到 attach 之前它是没有持有 PipelineOwner 的,所以如果在这个阶段之前就调用了它的 markNeedsLayout 等函数,并不能受到 PipelineOwner 的管理,所以在这个阶段还会重新调用一次这些函数,不过总体感觉这个代码的存在是有问题的,要么一个结点在 attach 之前不能被标记,要么就设置了标记也无效,像这样处理虽然能够解决一些问题,但是感觉有点奇怪,这一点感觉以后会有所更改吧。
以上是 RenderObjectElement mount 函数的实现内容,总结下来就是创建 RenderObject 并将其 attach 到当前的 RenderObject 树上。
再往上就是 Element 的 mount 实现,
void mount(Element parent, dynamic newSlot) {
assert(_debugLifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._debugLifecycleState == _ElementLifecycle.active);
assert(slot == null);
assert(depth == null);
assert(!_active);
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner;
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._register(this);
}
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
}
一个标准的赋值过程,值得关注的一点是 _updateInheritance,它就是 InheritedWidget 实现的基础,会继承父结点的 _inheritedWidgets 并将自己加入到其中(如果这是一个 InheritedElement 的话)。
以上就是 RenderObjectElement 系列的 mount 总过程,如果再沿着 Element 自上而下,比如 ComponentElement,
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_active);
_firstBuild();
assert(_child != null);
}
_firstBuild 会调用 rebuild,并且在子类 StatefulElement 中会调用 State 的 initState 函数。
从 mount 的整个过程来看,在这个阶段 element 树、RenderObject 树已经建立好了。
buildScope
mount 函数是被传入 buildScope 的参数,在 buildScope 中,执行完回调函数之后,还有一部分处理脏结点的逻辑:
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
assert(context != null);
assert(_debugStateLockLevel >= 0);
assert(!_debugBuilding);
assert(() {
if (debugPrintBuildScope)
debugPrint('buildScope called with context $context; dirty list is: $_dirtyElements');
_debugStateLockLevel += 1;
_debugBuilding = true;
return true;
}());
Timeline.startSync('Build', arguments: timelineWhitelistArguments);
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
assert(_debugStateLocked);
Element debugPreviousBuildTarget;
assert(() {
context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
debugPreviousBuildTarget = _debugCurrentBuildTarget;
_debugCurrentBuildTarget = context;
return true;
}());
_dirtyElementsNeedsResorting = false;
try {
callback();
} finally {
assert(() {
context._debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
assert(_debugCurrentBuildTarget == context);
_debugCurrentBuildTarget = debugPreviousBuildTarget;
_debugElementWasRebuilt(context);
return true;
}());
}
}
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
assert(_dirtyElements[index] != null);
assert(_dirtyElements[index]._inDirtyList);
assert(!_dirtyElements[index]._active || _dirtyElements[index]._debugIsInScope(context));
try {
_dirtyElements[index].rebuild();
} catch (e, stack) {
_debugReportException(
ErrorDescription('while rebuilding dirty elements'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(_dirtyElements[index]));
yield _dirtyElements[index].describeElement('The element being rebuilt at the time was index $index of $dirtyCount');
},
);
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
// It is possible for previously dirty but inactive widgets to move right in the list.
// We therefore have to move the index left in the list to account for this.
// We don't know how many could have moved. However, we do know that the only possible
// change to the list is that nodes that were previously to the left of the index have
// now moved to be to the right of the right-most cleaned node, and we do know that
// all the clean nodes were to the left of the index. So we move the index left
// until just after the right-most clean node.
index -= 1;
}
}
}
assert(() {
if (_dirtyElements.any((Element element) => element._active && element.dirty)) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('buildScope missed some dirty elements.'),
ErrorHint('This probably indicates that the dirty list should have been resorted but was not.'),
Element.describeElements('The list of dirty elements at the end of the buildScope call was', _dirtyElements)
]);
}
return true;
}());
} finally {
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
assert(_debugBuilding);
assert(() {
_debugBuilding = false;
_debugStateLockLevel -= 1;
if (debugPrintBuildScope)
debugPrint('buildScope finished');
return true;
}());
}
assert(_debugStateLockLevel >= 0);
}
很长,可以分为两部分,上部分就是执行回调,下部分是处理脏结点 _dirtyElements。最外层就是一个 while 循环,不断处理脏结点,通过调用起 rebuild 函数,这个函数会调用 performRebuild,performRebuild 大致分为两类实现,一类是 RenderObjectElement,一类是 ComponentElement,因为这两类有着不同的功能,前者主要用于创建 RenderObject,所以它的 rebuild 主要是更新 RenderObject,后者属于容器类,主要是调用子结点的更新。
RenderObjectElement
void performRebuild() {
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
这里调用了 Widget 另一个与 RenderObject 相关的 updateRenderObject,以 ClipRect 为例:
void updateRenderObject(BuildContext context, RenderClipRect renderObject) {
renderObject
..clipper = clipper
..clipBehavior = clipBehavior;
}
它只是调用了 RenderObject 的 set 函数更新变量,但是在 set 函数中,决定了是否重新布局、绘制 RenderObject 等,
set clipBehavior(Clip value) {
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsPaint();
}
}
所以结合在 RenderBinding 中 drawFrame 的实现,就可以了解这整个流程中,是如何确定是否更新 RenderObject 以及更新哪部分的。
ComponentElement
void performRebuild() {
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.startSync('${widget.runtimeType}', arguments: timelineWhitelistArguments);
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
Widget built;
try {
built = build();
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
)
);
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
)
);
_child = updateChild(null, built, slot);
}
}
ComponentElement 的 performRebuild 的逻辑主要在于更新子结点,首先,build 函数创建最新的 Widget 实例,然后 updateChild 执行更新逻辑,跟之前说的一致,会整体评估新旧 Widget 的变化决定是否重用,如果重用,会调用 update 函数将新的 Widget 保存到 Element 中。
update 函数的实现也是分为几类的,在 RenderObjectElement 它的作用与 performRebuild 类似,而在 ComponentElement 的子类中,它会继续调用 Element 的 rebuild 函数进行树的重建。所以说,在实际开发的过程中一定要尽量控制 setState 函数调用的位置,如果一个容器类结点被标记为脏结点,那么它的子结点都要走一遍这个过程,一个不小心还会导致底下的 RenderObject 重新绘制,当然这个能通过某种手段解决,但是还是要保持优良开发习惯。另外,为了方便使用,也衍生出了许多的状态管理插件,可以完成类似于数据-视图绑定的功能,比如 provider 等,它的实现就在很大程度上依赖了 InheritedWidget 。
drawFrame
另外,在 WidgetsBinding 中也有 drawFrame 的实现,
void drawFrame() {
if (_needToReportFirstFrame && _reportFirstFrame) {
assert(!_firstFrameCompleter.isCompleted);
// TODO(liyuqian): use a broadcast stream approach
final TimingsCallback oldCallback = WidgetsBinding.instance.window.onReportTimings;
WidgetsBinding.instance.window.onReportTimings = (List<FrameTiming> timings) {
if (!kReleaseMode) {
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
}
if (oldCallback != null) {
oldCallback(timings);
}
WidgetsBinding.instance.window.onReportTimings = oldCallback;
_firstFrameCompleter.complete();
};
}
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
if (!kReleaseMode) {
if (_needToReportFirstFrame && _reportFirstFrame) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
}
前半部分是关于第一帧的一些处理逻辑,只会在第一次执行的时候调用,后面则是调用了 buildScope,逻辑同上,主要用于重建 element 树,然后执行 super.drawFrame 进行 RenderObject 的绘制,最后,finalizeTree 负责清理一些无用的 Element 结点,这个主要是在 updateChild 阶段,会将一些需要替换的 element 结点收集起来,在这个阶段统一进行销毁。
setState
State 负责 flutter 中的状态管理,它最主要的一个函数就是 setState,能够完成数据更换及 element 树重建,它的实现比较简单:
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element.markNeedsBuild();
}
调用回调函数,一般我们是在这个函数中完成数据的更换,但是理论上只要能在下一帧的 drawFrame 执行前完成数据更换就行,这个函数最主要的就是调用 markNeedsBuild 将其标记为脏结点,这样就能在 buildScope 阶段对其进行重建。在 markNeedsBuild 中,能够将 Element 标记为脏结点,然后将其加入到 BuildOwner 的脏结点列表中,最后请求刷新视图。
以上就是 WidgetsBinding 中整个的内容,综上来看,其实它与 RenderBinding 的功能是类似的,只不过处理的对象不同,WidgetsBinding 使用 BuildOwner 完成对 element 树的管理,RenderBinding 使用 PipelineOwner 完成对 RenderObject 树的管理(主要是控制更新,一般不对树的结构做调整),它们两个将 flutter 的结构分成了两个部分,一个是以 widget/element 为核心的上层业务部分,着点与业务逻辑、页面布局的实现,一个是以 RenderObject 为核心的底层渲染部分,主要处理各个元素布局、绘制的实现。