Widget
widget存储视图的配置信息,没有状态所以是不可变的,每个绘制周期(帧率),widget都不会刷新,而是会被重建。
StatelessWidget和StatefulWidget
- - - 怎样判断是需要更新element,还是替换element
abstract class Widget extends DiagnosticableTree {const Widget({ this.key });final Key key;···static bool canUpdate(Widget oldWidget, Widget newWidget) {return oldWidget.runtimeType == newWidget.runtimeType&& oldWidget.key == newWidget.key;}
- element只更新widget里的配置信息,若 canUpdate 方法返回 true 说明不需要替换 Element,直接更新 Widget 就可以了。
- element替换,而我们给 Widget 一个 key 之后,canUpdate 方法将会比较两个 Widget 的 runtimeType 以及 key。并返回 false。(这里 runtimeType 相同,key 不同)
此时 RenderObjectElement 会用新 Widget 的 key 在老 Element 列表里面查找,找到匹配的则会更新 Element 的位置并更新对应 renderObject 的位置,对于这个例子来讲就是交换了 Element 的位置并交换了对应 renderObject 的位置。- 比较范围
为了提升性能 Flutter 的比较算法(diff)是有范围的,它并不是对第一个 StatefulWidget 进行比较,而是对某一个层级的 Widget 进行比较。
-slot:slot 能够描述子级在其父级列表中的位置。多子部件 Widget 例如 Row,Column 都为它的子级提供了一系列 slot。在调用 Element.updateChild 的时候有一个细节,若新老 Widget 的实例相同,注意这里是实例相同而不是类型相同, slot 不同的时候,Flutter 所做的仅仅是更新 slot,也就给他换个位置。因 为 Widget 是不可变的,实例相同意味着显示的配置相同,所以要做的仅仅是挪个地方而已。
- 比较范围
abstract class Element extends DiagnosticableTree implements BuildContext {
···dynamic get slot => _slot;dynamic _slot;
···Element updateChild(Element child, Widget newWidget, dynamic newSlot) {···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);assert(child.widget == newWidget);assert(() {child.owner._debugElementWasRebuilt(child);return true;}());return child;}deactivateChild(child);assert(child._parent == null);}return inflateWidget(newWidget, newSlot);}
- 重建element,状态也被更新。
Element
Element 是 Widget 的一个实例化对象,具有状态,是可变的,同时持有 Widget 和 RenderObject。
因为 Widget 具有不可变性,但 Element 却是可变的。实际上,Element 树这一层将 Widget 树的变化做了抽象(Diff),可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个RenderObject树重建。
RenderObject
布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历渲染对象树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia 搞定。
RenderObjectWidget
RenderObjectWidget 是一个抽象类。我们通过源码可以看到,这个类中同时拥有创建 Element、RenderObject,以及更新 RenderObject 的方法。
abstract class RenderObjectWidget extends Widget {RenderObjectElement createElement();RenderObject createRenderObject(BuildContext context);void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }...
}
布局和绘制
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {...void layout(Constraints constraints, { bool parentUsesSize = false }) {...}void paint(PaintingContext context, Offset offset) { }
}