Widget實際上是Element的配置項,Flutter中真正表明屏幕上顯示元素的類是Element
。bash
Tips:Element自己並不處理laying out, painting, 和hit testing這些操做,這些操做是由RenderObject來實現的框架
Element通常並非直接調用的,而是經過調用Widget.createElement方法來初始化元素,經過閱讀Widget源碼Element createElement()
,咱們能夠知道createElement方法返回一個Elment類對象。ide
由Element Class源碼中,咱們能夠得知,完成了createElement後Element的狀態爲initial
。佈局
_ElementLifecycle _debugLifecycleState = _ElementLifecycle.initial;
複製代碼
Tips:_debugLifecycleState內部屬性用於判斷當前Element所處的狀態。測試
當RenderObject調用mount方法時,會將新Element添加到Parent的render樹中,具體實現:動畫
RenderObject的mount方法先是執行Element的mount方法,根據Element類的mount方法assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
,咱們能夠得知此時Element的狀態已經被改成active
。ui
由於Element自己不進行layout,因此Elment自己的mount方法只有生命週期的改變,那麼layout是在哪裏進行的呢?咱們能夠打開RenderObjectElement的源碼,看到mount方法,經過調用attachRenderObject
方法將render object 添加到 render tree中,在此,實現了Element的Layout。this
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner;
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._register(this);
}
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
}
複製代碼
當前狀態:active,而且此時已經在屏幕中展現spa
當Parent Rebuilt的時候,Widget將會調用update方法,Flutter會根據runtimeType和key兩個屬性來判斷新舊Widget是否相同,如果相同,則會調用Element.update方法進行更新;若不相同,舊的Element將會從Render Tree中移除,新的Element將會填充進(這一塊涉及到Widget的更新、插入、移除操做,這裏只是粗略一講)。那麼問題來了,咱們可以手動改變runtimeType麼?目前來看是不行的,官網也有一句話,debug
If the parent wishes to change the runtimeType or key of the widget at this location in the tree, can do so by unmounting this element and inflating the new widget at this location.
因此要想改變runtimeType就必須unmounting element。
若Ancestor元素須要將element從tree中移除,Ancestor將會調用deactivateChild方法,此方法將會將element的render object從render tree中移除,而且將該element推入到Ancestor的inactive elements list,在這list中的element將會被調用deactivate方法
當前狀態:inactive,而且此時已經從屏幕中移除,此時好像該element還存在於內存中
當動畫最後一幀結束後,全部inactive的element將會被卸載(unmounted)
void unmount() {
assert(_debugLifecycleState == _ElementLifecycle.inactive);
assert(widget != null);
assert(depth != null);
assert(!_active);
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._unregister(this);
}
assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; }());
}
複製代碼
當前狀態:defunct
若是在動畫最後一幀結束前該元素被再合併到tree中,框架將會將其從inactive’s list中移除,從新調用該element的activate方法,而且將該element的render object從新推到render tree中
void activate() {
assert(_debugLifecycleState == _ElementLifecycle.inactive);
assert(widget != null);
assert(owner != null);
assert(depth != null);
assert(!_active);
final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
_active = true;
// We unregistered our dependencies in deactivate, but never cleared the list.
// Since we're going to be reused, let's clear our list now.
_dependencies?.clear();
_hadUnsatisfiedDependencies = false;
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
if (_dirty)
owner.scheduleBuildFor(this);
if (hadDependencies)
didChangeDependencies();
}
複製代碼
瞭解了Element的生命週期,那麼咱們是否可以使用該生命週期,在某些特殊的場景下實現例如組件移除觸發的特殊需求呢?
答案固然是能夠的,可是有一點須要注意的是,須要借用RenderObjecgtElement或者ComponentElement類來實現,由於這兩個類繼承自Element類,有人問了,那我直接繼承Element類是否能夠,這固然也是能夠的,可是有一點,正如我最開始說的,Element自己不處理laying out, painting, 和hit testing這些操做,因此這些操做須要你本身實現,而使用RenderObjectElement或者ComponentElement則能夠借用其來進行佈局渲染等等。
咱們都是經過使用Widget來實現功能的,就算咱們追本溯源也是繼承Widget,那本文講述的Element又在哪裏觸發呢?其實上文已經有寫了,經過使用Widget的createElement返回Element,那麼,因此如果咱們想要使用該生命週期,須要建立一個新的類繼承自RenderObjectElment或者ComponentElement,使用mount、unmount、activate等方法觸發特定的生命週期。
如下代碼是我寫的一個Demo
class MyWidget extends Widget {
@override
MyElement createElement() => MyElement(this);
}
class MyElement extends ComponentElement {
MyElement(Widget widget) : super(widget);
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
print('觸發mount生命週期');
}
@override
void deactivate() {
super.deactivate();
print('觸發deactivate生命週期');
}
@override
void unmount() {
super.unmount();
print('觸發unmount生命週期');
}
@override
Widget build() {
// TODO: implement build
return Text('測試');
}
}
複製代碼
本文講述了一些特殊名詞,例如:Render Tree、RenderObject,這些若是有下篇的話會在下篇中進行詳細講解。