上一篇文章講到,Widget是描述一個UI元素的配置數據,Element才真正表明屏幕顯示元素,是某個位置的Widget生成的實例。本篇文章則主要介紹Element的主要功能。html
經過上篇文章介紹的Widget Tree,Flutter Framework會生成一系列Element,這些Element構成了Element Tree,其主要功能以下:node
如上圖所示,Element從功能上看,能夠分爲兩大類:markdown
組合類Element。這類Element主要用來組合其餘更基礎的Element,獲得功能更加複雜的Element。開發時經常使用到的StatelessWidget和StatefulWidget相對應的Element:StatelessElement和StatefulElement,即屬於ComponentElement。架構
渲染類Element,對應Renderer Widget,是框架最核心的Element。RenderObjectElement主要包括LeafRenderObjectElement,SingleChildRenderObjectElement,和MultiChildRenderObjectElement。其中,LeafRenderObjectElement對應的Widget是LeafRenderObjectWidget,沒有子節點;SingleChildRenderObjectElement對應的Widget是SingleChildRenderObjectWidget,有一個子節點;MultiChildRenderObjectElement對應的Widget是MultiChildRenderObjecWidget ,有多個子節點。框架
Element有4種狀態:initial,active,inactive,defunct。其對應的意義以下:less
Element4種狀態間的轉換關係以下圖所示: ide
如上文所述,ComponentElement分爲StatelessElement和StatefulElement,這兩種Element同核心元素Widget以及State之間的關係以下圖所示。 如圖:函數
一個Element的核心操做流程有,建立、更新、銷燬三種,下面將分別介紹這三個流程。oop
ComponentElement的建立起源與父Widget調用inflateWidget,而後經過mount將該Element掛載至Element Tree,並遞歸建立子節點。post
由父Element執行更新子節點的操做(updateChild),因爲新舊Widget的類型和Key均未發生變化,所以觸發了Element的更新操做,並經過performRebuild將更新操做傳遞下去。其核心函數updateChild以後會詳細介紹。
由父Element或更上級的節點執行更新子節點的操做(updateChild),因爲新舊Widget的類型或者Key發生變化,或者新Widget被移除,所以致使該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的建立流程和ComponentElement的建立流程基本一致,其最大的區別是ComponentElement在mount後,會調用build來建立子Widget,而RenderObjectElement則是create和attach其RenderObject。
RenderObjectElement的更新流程和ComponentElement的更新流程也基本一致,其最大的區別是ComponentElement的update函數會調用build函數,從新觸發子Widget的構建,而RenderObjectElement則是調用updateRenderObject對綁定的RenderObject進行更新。
RenderObjectElement的銷燬流程和ComponentElement的銷燬流程也基本一致。也是由父Element或更上級的節點執行更新子節點的操做(updateChild),致使該Element被停用,並被加入未激活列表,並在下一幀被失效。其不同的地方是在unmount Element的時候,會調用didUnmountRenderObject失效對應的RenderObject。
下面對RenderObjectElement中的核心方法進行介紹。
該函數和ComponentElement的inflateWidget函數徹底一致,此處再也不復述。
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的主要職責以下:
該函數和ComponentElement的inflateWidget函數徹底一致,此處再也不復述。
@protected
List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element> forgottenChildren }) {
int newChildrenTop = 0;
int oldChildrenTop = 0;
int newChildrenBottom = newWidgets.length - 1;
int oldChildrenBottom = oldChildren.length - 1;
final List<Element> newChildren = oldChildren.length == newWidgets.length ?
oldChildren : List<Element>(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<Element>(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<Key, Element> oldKeyedChildren;
if (haveOldChildren) {
oldKeyedChildren = <Key, Element>{};
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<Element>(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<Element>(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
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget