## 構建(build)階段
結合上文 build階段主要再`buildOwner.buildScope(renderViewElement)`;
~~~
void buildScope(Element context, [VoidCallback callback]) {
try {
_scheduledFlushDirtyElements = true;
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
_dirtyElements[index].rebuild();
} catch (e, stack) {
...
}
index += 1;
}
} finally {
for (Element element in _dirtyElements) {
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
}
}
~~~
還記得在調度幀之前會把需要更新的`Element`標記為“臟”(dirty)并放入`BuildOwner`的`_dirtyElements`列表。這里Flutter會先按照深度給這個列表排個序。因為`Element`在重建的時候其子節點也都會重建,這樣如果父節點和子節點都為“臟”的話,先重建父節點就避免了子節點的重復重建。
排完序就是遍歷`_dirtyElements`列表。依次調用`Element.rebuild()`。這個函數又會調用到`Element.performRebuild()`。我們之前介紹`Element`的時候說過`performRebuild()`由其子類實現。
## rebuild
`Element.rebuild()`
~~~
void rebuild() {
if (!\_active || !\_dirty)
return;
performRebuild();
}
~~~
`performRebuild()`是在`Element`的父類`ComponentElement`里:
~~~
void performRebuild() {
Widget built;
built = build();
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
...
}
}
~~~
## Element.updateChild()
~~~
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);
}
~~~
函數`updateChild()`比較重要,用來更新一個孩子節點。更新有四種情況:
* 新`Widget`為空,老`Widget`也為空。則啥也不做。
* 新`Widget`為空,老`Widget`不為空。這個`Element`被移除。
* 新`Widget`不為空,老`Widget`為空。則調用`inflateWidget()`以這個`Wiget`為配置實例化一個`Element`。
* 新`Widget`不為空,老`Widget`不為空。調用`update()`函數更新子`Element`。`update()`函數由子類實現。
## update
`StatefulElement`和`StatelessElement`的`update()`函數最終都會調用基類`Element`的`rebuild()`函數。
`RenderObjectElement`的`update()`。
~~~
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget);
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
~~~
更新只是調用了一下`RenderObjectWidget.updateRenderObject()`。這個函數我們之前介紹過,只是把新的配置設置到現有的`RenderObject`上。
StatelessElement的update實現
~~~
class StatelessElement extends ComponentElement {
@override
Widget build() => widget.build(this);
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
_dirty = true;
rebuild();
}
}
~~~
StatefulElement的update實現
~~~
class StatefulElement extends ComponentElement {
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = _state._widget;
// Notice that we mark ourselves as dirty before calling didUpdateWidget to
// let authors call setState from within didUpdateWidget without triggering
// asserts.
_dirty = true;
_state._widget = widget;
rebuild();
}
}
~~~
而LeafRenderObjectElement沒有實現update,不會rebuild(()
因此假設我們有這樣的一個三層element tree進行更新重建。
~~~
父(StatefulElement)
子(StatefulElement)
孫(LeafRenderObjectElement)
~~~
那么從父節點開始,調用順序如下:
父.rebuild()--->父.performRebuild()--->父.updateChild()--->子.update()--->子.rebuild()--->子.performRebuild()--->子.updateChild()--->孫.update()。
可見構建(build)過程是從需要重建的`Element`節點開始一層層向下逐個更新子節點。直到遇到葉子節點為止。