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) { }
}