開篇
Flutter中頁面的渲染渲染離不開三個(gè)重要的元素:Widget、Element、RenderObject。是一個(gè)從Widget到Element再到RenderObject的過程。而具體到源碼中,這個(gè)轉(zhuǎn)換的工作時(shí)如何實(shí)現(xiàn)的呢,今天我們就來跟著源碼簡(jiǎn)略分析一下。為了更流程的分析它們?nèi)咧g的關(guān)系,我們先根據(jù)Framework層源碼的注釋了解一下它們,然后再?gòu)腇lutter項(xiàng)目入口進(jìn)行分析。
Widget、Element和RenderObject
根據(jù)源碼注釋我么可以大致知道:
-
Widget是對(duì)UI的一種廉價(jià)的、不可變的,描述(如何配置子樹)Element的配置,并可以生成Element -
Widget本身沒有狀態(tài),所有屬性都是final,Element持有Widget和RenderObject的實(shí)例 - 給定
Widget可不斷加入到樹中不同的位置,并生成Element作為它在樹中的實(shí)例,它們是一對(duì)多的關(guān)系 -
RenderObject是實(shí)際的渲染對(duì)象,Element就可以理解為將變化的Widget轉(zhuǎn)化為較為穩(wěn)定的RenderObject
Flutter工程的入口
新建一個(gè)Flutter項(xiàng)目后,我們會(huì)看到其工程目錄如下
其中,
main.dart文件中有main函數(shù)作為程序的入口:
void main() {
runApp(MyApp());
}
其中MyApp()就是一個(gè)Widget,我們忽略其他代碼,只關(guān)心它在代碼中的走向,發(fā)現(xiàn)它最終作為一個(gè)child用于RenderObjectToWidgetAdapter的初始化,并且通過attachToRenderTree方法生成了一個(gè)Element。
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
繼續(xù)看它的attachToRenderTree方法:
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
// This is most likely the first time the framework is ready to produce
// a frame. Ensure that we are asked for one.
SchedulerBinding.instance.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
可以發(fā)現(xiàn):在我們創(chuàng)建的“根”Widget之前,會(huì)有一個(gè)RenderObjectToWidgetAdapter作為真正的根Widget,而與之對(duì)應(yīng)的是一個(gè)RenderObjectToWidgetElement作為根Element。通過調(diào)用createElement方法初始化與之對(duì)應(yīng)的Element。該方法在抽象類Widget中被定義,可被子類重寫。
而觀察Element的初始化方式以及定義:
Element(Widget widget)
: assert(widget != null),
_widget = widget;
不難發(fā)現(xiàn):Element初始化時(shí)接受了當(dāng)前的Widget并作為私有變量持有。那么我們的Widget,MyApp是在何時(shí)轉(zhuǎn)換成Element的呢?
自上而下的調(diào)用創(chuàng)建Element
繼續(xù)追蹤attachToRenderTree方法源碼,我們發(fā)現(xiàn)在Element被初始化后,會(huì)調(diào)用element.mount(null, null)方法(只保留關(guān)鍵代碼),該代碼在framework.dart文件中的Element類中。
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
void _rebuild() {
try {
//_child是其子Element,也就是`MyApp`將要生成的Element,此時(shí)進(jìn)行初始化
//widget.child是我們的`MyApp`
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
} catch (exception, stack) {
...
}
}
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
//如果newWidget為空,說明沒有子樹,返回null
if (child != null)
deactivateChild(child);
return null;
}
Element newChild;
//此時(shí)_child為null
if (child != null) {
...
} else {
//初始化
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
if (key is GlobalKey) {
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild;
}
}
//調(diào)用`MyApp`的`createElement`方法進(jìn)行初始化
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
//執(zhí)行monut方法,繼續(xù)下一個(gè)Widget的轉(zhuǎn)換
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
可以發(fā)現(xiàn),當(dāng)我們的程序啟動(dòng)時(shí),runApp會(huì)創(chuàng)建一個(gè)根Widget并調(diào)用其createElement方法生成element。并執(zhí)行element的mount方法。之后,element會(huì)讀取并判斷自己所持有的Widget對(duì)象是否有子Widget,就是上面代碼中widget.child。當(dāng)發(fā)現(xiàn)child不為空,就會(huì)繼續(xù)執(zhí)行createElement()方法,并執(zhí)行mount()方法進(jìn)入下一次子樹。依次類推直至遍歷完所有子樹。
依舊是自上而下的調(diào)用卻有點(diǎn)不一樣
在追蹤上述代碼時(shí),我們并沒有直接發(fā)現(xiàn)類似createElement的createRenderObject()方法被調(diào)用,我們觀察RenderObjectToWidgetElement的繼承關(guān)系:
最終可在
RenderObjectElement的mount()方法里看到這樣的代碼:
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
....
_renderObject = widget.createRenderObject(this);
....
}
在這里,我們發(fā)現(xiàn)了初始化RenderObject的地方??梢园l(fā)現(xiàn),RenderObjectWidget實(shí)在Element里被初始化,并且和Widget一樣被Element所持有。并且,采取和Element一樣,自上而下的不斷遍歷整個(gè)樹進(jìn)行初始化。
但是有一點(diǎn)我們需要注意:
createElement()方法在所有widget的基類Widget中被定義了。但是createRenderObject()方法是在Widget的子類RenderObjectWidget中被定義的,于此同時(shí),createRenderObject()方法也只會(huì)在RenderObjectElement中被調(diào)用。
這說明:每個(gè)Widget都會(huì)生成一個(gè)Element,但并不是每個(gè)Element都會(huì)創(chuàng)建一個(gè)RenderObject,只有RenderObjectWidget類型的子類的Widget創(chuàng)建的繼承自RenderObjectElement類型的element才會(huì)創(chuàng)建RenderObject。也就是說,widget樹、element樹和RenderObject樹并不是一是一對(duì)應(yīng)的。
總結(jié)
本文只是Flutter UI相關(guān)的冰山一角,并沒有深入了解Flutter的繪制原理。旨在通過源碼了解Widget、Element和RenderObject直接的關(guān)系,理清他們之間的邏輯。