繼上一篇 Flutter學(xué)習(xí)筆記(1)介紹了一下Flutter框架構(gòu)造谈撒,本篇主講Flutter的視圖體系,下一篇開始布局
引言
- Flutter官方文檔里的一句話:you build your UI out of widgets(萬物皆控件)
- 首先搀突,F(xiàn)lutter沒有css萎攒,沒有xml让歼,Dart語言是面向?qū)ο蟮?/li>
- 其次Widget并不是我們真正看到的視圖,背后究竟是什么复凳?
- Flutter界面開發(fā)是一種響應(yīng)式編程瘤泪,而Widget又是一層不變的,那真正的渲染染坯、布局均芽、刷新是誰來處理?
Flutter的視圖體系
Flutter Framework提供了三種視圖樹单鹿,即: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
是用來描述如何創(chuàng)建Element
的掀宋,Widget
是一個(gè)不可變對(duì)象,它可以被復(fù)用仲锄,請(qǐng)注意劲妙,這里的復(fù)用不是指在兩次渲染的時(shí)候?qū)?duì)象從舊樹中拿過來放到新樹,而是在同一個(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è)類中都包含哪些屬性:
- KEY key
- int hasCode
- TYPE runtimeType
Widget是用戶界面的一部分,并且是不可變的(immutable)耘分。Widget會(huì)被inflate到Element,并由Element管理底層渲染樹举塔。Widget本身沒有可變狀態(tài)(所有的字段必須是final)。如果想要把可變狀態(tài)與Widget關(guān)聯(lián)起來求泰,可以使用StatefulWidget央渣,StatefulWidget通過使用StatefulWidget.createState方法創(chuàng)建State對(duì)象,并將之?dāng)U充到Element以及合并到樹中渴频;
- Element
負(fù)責(zé)狀態(tài)和生命周期管理的對(duì)象芽丹,實(shí)際上很少需要去自己實(shí)現(xiàn) Element
Element 是 Widget 的實(shí)例體現(xiàn),上面說過 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 到底是做什么的呢惹悄,概括地說就是保存了一個(gè)樹形結(jié)構(gòu)以便更新時(shí)做 diff、patch 和管理組件生命周期用的肩钠,由于 Widget 都是沒有狀態(tài)的泣港,如果你想修改 Widget 的某一屬性就必須要重新創(chuàng)建一遍 Widget,而 StatefulWidget 內(nèi)部包含 State价匠,如果重新創(chuàng)建了狀態(tài)就會(huì)丟失当纱,所以必須有一個(gè)地方來存儲(chǔ)這些暫時(shí)的狀態(tài)。
用 StatefulWidget 來舉例說明吧踩窖。 第一次渲染時(shí)坡氯,StatefulWidget 通過 createElement 創(chuàng)建出一個(gè) StatefulElement,然后我們來看 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 通過 Element 的 update 來根據(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.渲染樹中的一個(gè)對(duì)象悯恍。從其名字,我們可以很直觀地知道伙狐,它就是負(fù)責(zé)渲染的工作涮毫;
RenderObject的屬性太多瞬欧,且該類的細(xì)節(jié)涉及很多渲染知識(shí),我們會(huì)在后面的系列中再詳細(xì)說明其工作原理罢防。
總結(jié)
Widget:存放渲染內(nèi)容黍判、視圖布局信息,widget的屬性最好都是immutable
Element:存放上下文,通過Element遍歷視圖樹篙梢,Element同時(shí)持有Widget和RenderObject
RenderObject:根據(jù)Widget的布局屬性進(jìn)行l(wèi)ayout顷帖,paint Widget傳人的內(nèi)容