繼上一篇 Flutter學(xué)習(xí)筆記(1)介紹了一下Flutter框架構(gòu)造,本篇主講Flutter的視圖體系,下一篇開(kāi)始布局
引言
- Flutter官方文檔里的一句話(huà):you build your UI out of widgets(萬(wàn)物皆控件)
- 首先,F(xiàn)lutter沒(méi)有css,沒(méi)有xml,Dart語(yǔ)言是面向?qū)ο蟮?/li>
- 其次Widget并不是我們真正看到的視圖,背后究竟是什么?
- Flutter界面開(kāi)發(fā)是一種響應(yīng)式編程,而Widget又是一層不變的,那真正的渲染、布局、刷新是誰(shuí)來(lái)處理?
Flutter的視圖體系
Flutter Framework提供了三種視圖樹(shù),即:Widget Element RenderObject

- Widget
官方文檔:
Describes the configuration for an Element.
Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface. Widgets can be inflated into elements, which manage the underlying render tree.
解釋一下:Widget 是用來(lái)描述如何創(chuàng)建Element 的,Widget是一個(gè)不可變對(duì)象,它可以被復(fù)用,請(qǐng)注意,這里的復(fù)用不是指在兩次渲染的時(shí)候?qū)?duì)象從舊樹(shù)中拿過(guò)來(lái)放到新樹(shù),而是在同一個(gè) Widget Tree 中,某個(gè)子 Widget 可以出現(xiàn)多次,因?yàn)樗皇且粋€(gè) description。
在一次渲染中,F(xiàn)lutter Framework 會(huì)調(diào)用 Widget 的 方法,這個(gè)方法會(huì)創(chuàng)建一個(gè)新的對(duì)應(yīng)的 對(duì)象并返回。所以即使 Widget 被重復(fù)使用,框架還是會(huì)創(chuàng)建多個(gè)不同的 Element 對(duì)象。
到這里,Widget 的工作就暫告一段落了。
Widget這個(gè)類(lèi)中都包含哪些屬性:
- KEY key
- int hasCode
- TYPE runtimeType
Widget是用戶(hù)界面的一部分,并且是不可變的(immutable)。Widget會(huì)被inflate到Element,并由Element管理底層渲染樹(shù)。Widget本身沒(méi)有可變狀態(tài)(所有的字段必須是final)。如果想要把可變狀態(tài)與Widget關(guān)聯(lián)起來(lái),可以使用StatefulWidget,StatefulWidget通過(guò)使用StatefulWidget.createState方法創(chuàng)建State對(duì)象,并將之?dāng)U充到Element以及合并到樹(shù)中;
- Element
負(fù)責(zé)狀態(tài)和生命周期管理的對(duì)象,實(shí)際上很少需要去自己實(shí)現(xiàn) Element
Element 是 Widget 的實(shí)例體現(xiàn),上面說(shuō)過(guò) Widget 可以重復(fù)使用,但是 Flutter Framework 仍然會(huì)對(duì)這幾個(gè)相同的 Widget 依次創(chuàng)建幾個(gè)全新的 Element。 一次渲染會(huì)生成一個(gè) Element Tree 并放在內(nèi)存中,在下次渲染時(shí) Flutter Framework 會(huì)用嘗試用新的 Widget 去更新舊的 Element,此時(shí) Element 被復(fù)用,這里的復(fù)用是指不再創(chuàng)建新的 Element 的對(duì)象了,但每個(gè)相同的 Widget 還是各自對(duì)應(yīng)了一個(gè)不同的 Element
那么 Element 到底是做什么的呢,概括地說(shuō)就是保存了一個(gè)樹(shù)形結(jié)構(gòu)以便更新時(shí)做 diff、patch 和管理組件生命周期用的,由于 Widget 都是沒(méi)有狀態(tài)的,如果你想修改 Widget 的某一屬性就必須要重新創(chuàng)建一遍 Widget,而 StatefulWidget 內(nèi)部包含 State,如果重新創(chuàng)建了狀態(tài)就會(huì)丟失,所以必須有一個(gè)地方來(lái)存儲(chǔ)這些暫時(shí)的狀態(tài)。
用 StatefulWidget 來(lái)舉例說(shuō)明吧。 第一次渲染時(shí),StatefulWidget 通過(guò) createElement 創(chuàng)建出一個(gè) StatefulElement,然后我們來(lái)看 StatefulElement 的構(gòu)造方法
StatefulElement(StatefulWidget widget)
: _state = widget.createState(), super(widget) {
assert(() {
if (!_state._debugTypesAreRight(widget)) {
throw new FlutterError(
'StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>\n'
'The createState function for ${widget.runtimeType} returned a state '
'of type ${_state.runtimeType}, which is not a subtype of '
'State<${widget.runtimeType}>, violating the contract for createState.'
);
}
return true;
}());
assert(_state._element == null);
_state._element = this;
assert(_state._widget == null);
_state._widget = widget;
assert(_state._debugLifecycleState == _StateLifecycle.created);
}
可以看到,StatefulElement 內(nèi)部會(huì)負(fù)責(zé)創(chuàng)建和保存 State,這樣就是為什么 StatefulWidget 被重新創(chuàng)建了而內(nèi)部的狀態(tài)不會(huì)丟失的原因。
然后在 Widget Tree 發(fā)生變化的時(shí)候,F(xiàn)lutter Framework 通過(guò) Element 的 update 來(lái)根據(jù)新 Widget 更新舊 Element:
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
assert(widget == 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;
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
_state.didUpdateWidget(oldWidget);
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
rebuild();
}
- RenderObject
官網(wǎng)定義:An object in the render tree.渲染樹(shù)中的一個(gè)對(duì)象。從其名字,我們可以很直觀地知道,它就是負(fù)責(zé)渲染的工作;
RenderObject的屬性太多,且該類(lèi)的細(xì)節(jié)涉及很多渲染知識(shí),我們會(huì)在后面的系列中再詳細(xì)說(shuō)明其工作原理。
總結(jié)
Widget:存放渲染內(nèi)容、視圖布局信息,widget的屬性最好都是immutable
Element:存放上下文,通過(guò)Element遍歷視圖樹(shù),Element同時(shí)持有Widget和RenderObject
RenderObject:根據(jù)Widget的布局屬性進(jìn)行l(wèi)ayout,paint Widget傳人的內(nèi)容