上一篇文章讲到,Widget是描述一个UI元素的配置数据,Element才真正代表屏幕显示元素,是某个位置的Widget生成的实例。本篇文章则主要介绍Element的主要功能。
通过上篇文章介绍的Widget Tree,Flutter Framework会生成一系列Element,这些Element构成了Element Tree,其主要功能如下:
Element有4种状态:initial,active,inactive,defunct。其对应的意义如下:
如上文所述,ComponentElement分为StatelessElement和StatefulElement,这两种Element同核心元素Widget以及State之间的关系如下图所示。
如图:
一个Element的核心操作流程有,创建、更新、销毁三种,下面将分别介绍这三个流程。
下面对ComponentElement中的核心方法进行介绍。
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
//复用GlobalKey对应的Element
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild;
}
}
//创建Element,并挂载至Element Tree
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
inflateWidget的主要职责如下:
void mount(Element parent, dynamic newSlot) {
//更新_parent等属性,将元素加入Element Tree
_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;
//注册GlobalKey
final Key key = widget.key;
if (key is GlobalKey) {
key._register(this);
}
_updateInheritance();
}
当Element第一次被插入Element Tree的时候,该方法被调用。其主要职责如下:
@override
void performRebuild() {
//调用build函数,生成子Widget
Widget built;
built = build();
//根据新的子Widget更新子Element
_child = updateChild(_child, built, slot);
}
performRebuild的主要职责如下:
@mustCallSuper
void update(covariant Widget newWidget) {
_widget = newWidget;
}
此函数主要职责为:
updateChild
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
//新的Child Widget为null,则返回null;如果旧Child Widget,使其未激活
if (child != null)
deactivateChild(child);
return null;
}
Element newChild;
if (child != null) {
//新的Child Widget不为null,旧的Child Widget也不为null
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)){
//Key和RuntimeType相同,使用update更新
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
newChild = child;
} else {
//Key或RuntimeType不相同,使旧的Child Widget未激活,并对新的Child Widget使用inflateWidget
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
//新的Child Widget不为null,旧的Child Widget为null,对新的Child Widget使用inflateWidget
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
该方法的主要职责为:
根据新的子Widget,更新旧的子Element,或者得到新的子Element。其核心逻辑可以用表格表示:
newWidget == null | newWidget != null | |
---|---|---|
Child == null | 返回null | 返回新Element |
Child != null | 移除旧的子Element,返回null | 如果Widget能更新,更新旧的子Element,并返回之;否则创建新的子Element并返回。 |
该逻辑概括如下:
RenderObjectElement同核心元素Widget及RenderObject之间的关系如下图所示:
如图:
如ComponentElement一样,RenderObjectElement的核心操作流程有,创建、更新、销毁三种,接下来会详细介绍这三种流程。
下面对RenderObjectElement中的核心方法进行介绍。
inflateWidget
该函数和ComponentElement的inflateWidget函数完全一致,此处不再复述。
mount
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
该函数的调用时机和ComponentElement的一致,当Element第一次被插入Element Tree的时候,该方法被调用。其主要职责也和ComponentElement的一致,此处只列举不一样的职责,职责如下:
@override
void performRebuild() {
//更新renderObject
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
performRebuild的主要职责如下:
调用updateRenderObject更新对应的RenderObject。
@override
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget);
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
update的主要职责如下:
updateChild
该函数和ComponentElement的inflateWidget函数完全一致,此处不再复述。
updateChildren
@protected
List updateChildren(List oldChildren, List newWidgets, { Set forgottenChildren }) {
int newChildrenTop = 0;
int oldChildrenTop = 0;
int newChildrenBottom = newWidgets.length - 1;
int oldChildrenBottom = oldChildren.length - 1;
final List newChildren = oldChildren.length == newWidgets.length ?
oldChildren : List(newWidgets.length);
Element previousChild;
// 从顶部向下更新子Element
// Update the top of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
final Widget newWidget = newWidgets[newChildrenTop];
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot(newChildrenTop, previousChild));
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// 从底部向上扫描子Element
// Scan the bottom of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
final Widget newWidget = newWidgets[newChildrenBottom];
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
oldChildrenBottom -= 1;
newChildrenBottom -= 1;
}
// 扫描旧的子Element列表里面中间的子Element,保存Widget有Key的Element到oldKeyChildren,其他的失效
// Scan the old children in the middle of the list.
final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
Map oldKeyedChildren;
if (haveOldChildren) {
oldKeyedChildren = {};
while (oldChildrenTop <= oldChildrenBottom) {
final Element oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
if (oldChild != null) {
if (oldChild.widget.key != null)
oldKeyedChildren[oldChild.widget.key] = oldChild;
else
deactivateChild(oldChild);
}
oldChildrenTop += 1;
}
}
// 根据Widget的Key更新oldKeyChildren中的Element。
// Update the middle of the list.
while (newChildrenTop <= newChildrenBottom) {
Element oldChild;
final Widget newWidget = newWidgets[newChildrenTop];
if (haveOldChildren) {
final Key key = newWidget.key;
if (key != null) {
oldChild = oldKeyedChildren[key];
if (oldChild != null) {
if (Widget.canUpdate(oldChild.widget, newWidget)) {
// we found a match!
// remove it from oldKeyedChildren so we don't unsync it later
oldKeyedChildren.remove(key);
} else {
// Not a match, let's pretend we didn't see it for now.
oldChild = null;
}
}
}
}
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot(newChildrenTop, previousChild));
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
}
newChildrenBottom = newWidgets.length - 1;
oldChildrenBottom = oldChildren.length - 1;
// 从下到上更新底部的Element。.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = oldChildren[oldChildrenTop];
final Widget newWidget = newWidgets[newChildrenTop];
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot(newChildrenTop, previousChild));
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// 清除旧子Element列表中其他所有剩余Element
// Clean up any of the remaining middle nodes from the old list.
if (haveOldChildren && oldKeyedChildren.isNotEmpty) {
for (final Element oldChild in oldKeyedChildren.values) {
if (forgottenChildren == null || !forgottenChildren.contains(oldChild))
deactivateChild(oldChild);
}
}
return newChildren;
}
该函数的主要职责如下:
其步骤如下:
本文主要介绍了Element相关知识,重点介绍了其分类,生命周期,和核心函数。重点如下:
深入浅出 Flutter Framework 之 Element
Flutter实战
Flutter框架分析(一)–架构总览
Flutter框架分析(二)-- Widget