之前幾篇文章都提過 Flutter 的思想十分類似 React,通過 Widget Tree 來表示整個 app 的檢視。一般來講,常規 app 的開發中我們只需要與 和它的子類打交道,然而 Widget
並不是我們真實看到的檢視,本文就來簡單介紹一下 Flutter 檢視系統背後的細節。
首先先擺 3 個關鍵的概念:Widget、Element、RenderObject。
Widget
為了解釋 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
的實際工作也就是描述如何建立 Element
,Widget
是一個不可變物件,它可以被複用,請注意,這裡的複用不是指在兩次渲染的時候將物件從舊樹中拿過來放到新樹,而是在同一個 Widget Tree 中,某個子 Widget
可以出現多次,因為它只是一個 description。
在一次渲染中,Flutter Framework 會呼叫 Widget
的 方法,這個方法會建立一個新的對應的 物件並返回。所以即使 Widget 被重複使用,框架還是會建立多個不同的 Element
物件。
到這裡,Widget
的工作就暫告一段落了。
Element
Element
是 Widget
的例項體現,上面說過 Widget 可以重複使用,但是 Flutter Framework 仍然會對這幾個相同的 Widget 依次建立幾個全新的 Element
。 一次渲染會生成一個 Element Tree 並放在記憶體中,在下次渲染時 Flutter Framework 會用嘗試用新的 Widget
去更新舊的 Element
,此時 Element
被複用,這裡的複用是指不再建立新的 Element
的物件了,但每個相同的 Widget
還是各自對應了一個不同的 Element(好像有點繞,不知道大家能不能理解 -。-)。
那麼 Element 到底是做什麼的呢,概括地說就是儲存了一個樹形結構以便更新時做 diff、patch 和管理元件生命週期用的,由於 Widget
都是沒有狀態的,如果你想修改 Widget
的某一屬性就必須要重新建立一遍 Widget
,而 StatefulWidget
內部包含 State
,如果重新建立了狀態就會丟失,所以必須有一個地方來儲存這些暫時的狀態。
用 StatefulWidget
來舉例說明吧。
第一次渲染時,StatefulWidget
通過 createElement
建立出一個 StatefulElement
,然後我們來看 StatefulElement
的構造方法:
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
內部會負責建立和儲存 State
,這樣就是為什麼 StatefulWidget
被重新建立了而內部的狀態不會丟失的原因。
然後在 Widget Tree 發生變化的時候,Flutter Framework 通過 Element
的 update
來根據新 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();
}
複製程式碼
所以如果你去看官方文件對 Element
的介紹,就會發現它就是負責狀態和生命週期管理的物件,實際上很少需要去自己實現 Element
。
RenderObject
這個東西通過名字就能知道,它就是負責檢視渲染的。實際上,所有的佈局、繪製和事件響應全都由它負責,開發複雜檢視時我們可能經常需要與之打交道。
而它又是由 Element
的子類 RenderObjectElement
建立出來的,RenderObject
也會構成一個 Render Tree,並且每個 RenderObject
也都會被儲存下來以便在更新時複用。
通常我們也不直接 subclass RenderObject
本身,而是它的子類 RenderBox
,RenderBox
幫我們實現了很多基礎設施,然而它並不能容納子 RenderObject
,想要實現一個容器,我們需要使用 RenderObjectWithChildMixin<ChildType>
等 mixin,它們提供了對 children 的支援。
由於 RenderObject
本身又涉及很多細節,所以本文不再深入探討。
以上就是 Flutter 檢視系統對外大致的結構,希望對大家學習 Flutter 開發有所幫助~
References