級別: ★☆☆☆☆
標(biāo)簽:「Flutter」「Element 」「重要方法與調(diào)用時機(jī) 」
作者: 沐靈洛
審校: QiShare團(tuán)隊
上一篇我們簡單介紹了Flutter中Element
是什么以及它的生命周期措拇。
本篇我們將與大家一起探討Element
的一些重要的方法的作用以及調(diào)用時機(jī)。
Element的重要方法
void mount(Element parent, dynamic newSlot)
描述:在parent element
中給定的插槽上废恋,加入當(dāng)前的 element
矢空。
調(diào)用時機(jī):新創(chuàng)建的element
首次加入樹中時,
Element updateChild(Element child, Widget newWidget, dynamic newSlot)
描述:使用新的配置widget
,更新樹中element
棋恼。這個方法是widges
系統(tǒng)的核心方法。
調(diào)用時機(jī):每次基于更新后的widget
,添加邦邦、更新或移除樹中的child element
時安吁。
方法原理:
newWidget == null | newWidget != null | |
---|---|---|
child == null | Returns null. | Returns new [Element]. |
child != null | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
///newWidget== null 棄用element
deactivateChild(child);
return null;
}
Element newChild;
if (child != null) {
///標(biāo)志位:解決hot reload前后Widget的類型不一致導(dǎo)致的類型錯誤 。
///比如:之前是`StatefulWidget`(StatefulElement) ,現(xiàn)在是`StatelessWidget`(StatelessElement) 燃辖。
bool hasSameSuperclass = true;
assert(() {
///element is StatefulElement ? 1 : element is StatelessElement ? 2 : 0;
final int oldElementClass = Element._debugConcreteSubtype(child);
/// widget is StatefulWidget ? 1 : widget is StatelessWidget ? 2 : 0;
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
///確保類型一致
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
///widget相等
if (hasSameSuperclass && child.widget == newWidget) {
///只有一個child的`element`鬼店,它的`slot`為null
if (child.slot != newSlot)
///作用:修改給定的`child`在其`parent`中占據(jù)的slot。
///調(diào)用時機(jī):`MultiChildRenderObjectElement`
///和其他擁有多個渲染對象的`RenderObjectElement`的子類黔龟。
///當(dāng)它們的`child`從`element`的`child`(renderObject)數(shù)組
///的一個位置移動到其他位置時妇智。
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
///詢問Widget.canUpdate
if (child.slot != newSlot)
///修改給定的`child`在其`parent`中占據(jù)的slot。
updateSlotForChild(child, newSlot);
///使用新的配置氏身,更新當(dāng)前child element
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
/// 棄用當(dāng)前的child element
deactivateChild(child);
assert(child._parent == null);
///注入新的widget
newChild = inflateWidget(newWidget, newSlot);
}
} else {
///child == null 創(chuàng)建新的
newChild = inflateWidget(newWidget, newSlot);
}
assert(() {
if (child != null)
_debugRemoveGlobalKeyReservation(child);
final Key key = newWidget?.key;
if (key is GlobalKey) {
key._debugReserveFor(this, newChild);
}
return true;
}());
return newChild;
}
void update(covariant Widget newWidget)
描述:使用新widget
修改用于配置此element
的widget
巍棱。
調(diào)用時機(jī):當(dāng)parent element
希望使用不同的widget
更新此element
時,并且新的widget
必須保證和舊的widget
擁有相同的runtimeType
蛋欣。
Element inflateWidget(Widget newWidget, dynamic newSlot)
描述:為給定的widget
創(chuàng)建一個element
航徙,并且將其作為child
添加到當(dāng)前element
的給定slot
中。
調(diào)用時機(jī):此方法通常由updateChild
調(diào)用陷虎,但也可以由需要對創(chuàng)建的element
進(jìn)行更細(xì)粒度控制的子類直接調(diào)用到踏。
注意: 如果給定的widget
有global key
,并且其對應(yīng)的element
已經(jīng)存在泻红,這個函數(shù)將復(fù)用此element
(可能從樹中的另一個位置移植它夭禽,或從inactive
元素列表中重新激活它)而不是創(chuàng)建一個新的元素。此方法返回的element
已經(jīng)被mounted
并且處于active
狀態(tài)谊路。
Element inflateWidget(Widget newWidget, dynamic newSlot) {
assert(newWidget != null);
final Key key = newWidget.key;
///判斷是否是`GlobalKey`
if (key is GlobalKey) {
///從處于`inactive`的列表中讹躯,重新獲取。
///1.final Element element = key._currentElement;
///2.element == null或!Widget.canUpdate(element.widget, newWidget)
///reture null
///3. final Element parent = element._parent;
///4. parent.forgetChild(element);
///5. parent.deactivateChild(element);
///6.owner._inactiveElements.remove(element);
///7.return element
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
///重新激活獲得的`element`缠劝。
///1.修改`parent`
///2._updateDepth(int parentDepth)
///3.遞歸遍歷修改`element`中所有子元素的狀態(tài)未`active`.
///針對擁有`children`的`element`潮梯。
///4.調(diào)用attachRenderObject(dynamic newSlot)
///添加渲染對象到樹中。
newChild._activateWithParent(this, newSlot);
///使用新的`widget`,更新此`element`,在新的`slot`上
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
///返回updatedChild
return updatedChild;
}
}
///如果不含globalKey
///創(chuàng)建新的元素
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
///掛載:加入樹中惨恭。
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
void deactivateChild(Element child)
描述:從inactive elements
列表中移除給定的element
并且 從渲染樹中分離此element
所對應(yīng)的render object
秉馏。
調(diào)用時機(jī):一般在元素更新(刪除)其子元素時調(diào)用,但是在global key
更換父節(jié)點時脱羡,新父會主動調(diào)用舊父的deactivateChild
萝究,并且在此之前會先調(diào)用舊父的forgetChild
,對舊父進(jìn)行更新锉罐。
void deactivateChild(Element child) {
assert(child != null);
assert(child._parent == this);
///去父
child._parent = null;
///分離其渲染對象
child.detachRenderObject();
///加入不活躍的數(shù)組中
owner._inactiveElements.add(child); // this eventually calls child.deactivate()
assert(() {
if (debugPrintGlobalKeyedWidgetLifecycle) {
if (child.widget.key is GlobalKey)
debugPrint('Deactivated $child (keyed child of $this)');
}
return true;
}());
}
void detachRenderObject()
描述:從渲染樹中移除渲染對象帆竹。
調(diào)用時機(jī):一般在deactivateChild
方法中被調(diào)用
注意:該函數(shù)的默認(rèn)實現(xiàn)只是對其child
遞歸調(diào)用detachRenderObject
,但是RenderObjectElement.detachRenderObject
重寫了此方法后脓规,對其進(jìn)行了覆蓋(未調(diào)用super
)栽连。
void detachRenderObject() {
visitChildren((Element child) {
child.detachRenderObject();
});
_slot = null;
}
void forgetChild(Element child)
描述:從元素的child
列表中刪除給定的child
,以準(zhǔn)備在元素樹中其他位置重用該child
。
調(diào)用時機(jī):一般在deactivateChild
方法調(diào)用前秒紧。update
會負(fù)責(zé)創(chuàng)建或者更新用于替換此child
的新的child
绢陌。
關(guān)于對此方法的詳細(xì)用法可以查看MultiChildRenderObjectElement
類,此類中forgetChild
會影響update
時對于replaceWithNullIfForgotten
方法的調(diào)用熔恢,也會影響visitChildren
時對于子元素的遍歷脐湾。
void updateSlotForChild(Element child, dynamic newSlot)
描述:修改給定的child
在父元素中占據(jù)的插槽 。
調(diào)用時機(jī):MultiChildRenderObjectElement
和其他擁有多個渲染對象的RenderObjectElement
的子類绩聘。當(dāng)它們的child
從element
的child
(renderObject)數(shù)組的一個位置移動到其他位置時沥割。
void updateSlotForChild(Element child, dynamic newSlot) {
assert(_debugLifecycleState == _ElementLifecycle.active);
assert(child != null);
assert(child._parent == this);
void visit(Element element) {
element._updateSlot(newSlot);
if (element is! RenderObjectElement)
element.visitChildren(visit);
}
visit(child);
}
void activate()
描述:將元素的生命周期由inactive
變?yōu)?code>active。
調(diào)用時機(jī):當(dāng)之前停用的元素重新合并到樹中時凿菩,框架將調(diào)用此方法。元素第一次激活時(即帜讲,狀態(tài)從initial
開始時)衅谷,framework
不會調(diào)用此方法,而是通過調(diào)用mount
似将。
void activate() {
....
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)
///將元素添加到`dirty element`列表中获黔,
///以便在`WidgetsBinding.drawFrame`調(diào)用`buildScope`
///時將對其進(jìn)行重建。
owner.scheduleBuildFor(this);
if (hadDependencies)
didChangeDependencies();
}
void attachRenderObject(dynamic newSlot)
描述:將renderObject
添加到渲染樹中slot
指定的位置在验。
調(diào)用時機(jī):該函數(shù)的默認(rèn)實現(xiàn)只是對其child遞歸調(diào)用attachRenderObject玷氏,但是RenderObjectElement.attachRenderObject重寫了此方法后,對其進(jìn)行了覆蓋(未調(diào)用super)腋舌。
void attachRenderObject(dynamic newSlot) {
assert(_slot == null);
visitChildren((Element child) {
child.attachRenderObject(newSlot);
});
_slot = newSlot;
}
void unmount()
描述: 生命周期由inactive
變?yōu)?defunct
盏触。
調(diào)用時機(jī):當(dāng)framework
確定處于inactive
狀態(tài)的元素永遠(yuǎn)不會被激活時調(diào)用。在每個動畫結(jié)束時块饺,framework
會對任何保持inactive
狀態(tài)的元素調(diào)unmount
赞辩,以防止inactive
元素保持inactive
的時間超過單個動畫幀。調(diào)用此函數(shù)后授艰,該元素將不再合并到樹中辨嗽。
void reassemble()
描述: 用于快速開發(fā)的一種debugging
策略。
調(diào)用時機(jī): 在調(diào)試期間重新組裝(reassemble
)應(yīng)用程序時調(diào)用淮腾,例如:hot reload
期間糟需。此函數(shù)僅在開發(fā)期間被調(diào)用,并且調(diào)用了reassemble
谷朝,build
將至少被調(diào)用一次洲押。
void didChangeDependencies()
調(diào)用時機(jī): 當(dāng)此元素的依賴項更改時調(diào)用。比如 此元素依賴的InheritedElement
更新了新的InheritedWidget
并且InheritedWidget.updateShouldNotify
返回true
的時候徘禁,framework
會調(diào)用此方法诅诱,通知此元素做出相應(yīng)的改變。
void markNeedsBuild()
描述: 將元素標(biāo)記為dirty
送朱,并將其添加到全局的widget
列表中娘荡,以在下一幀中進(jìn)行重建干旁。
調(diào)用時機(jī): setState
、reassemble
炮沐、didChangeDependencies
時争群。
void rebuild()
調(diào)用時機(jī):
- 當(dāng)
BuildOwner.scheduleBuildFor
已經(jīng)標(biāo)記此元素為dirty
時,由BuildOwner
調(diào)用大年; - 當(dāng)元素通過
mount
首次被構(gòu)建時會調(diào)用换薄; - 當(dāng)元素的
widget
通過update
已經(jīng)被改變時,會調(diào)用此方法翔试。
Flutter中的Element(上篇)
iOS 解決 [NSURL fileURLWithPath:] 對 # 的編碼問題
Xcode 調(diào)整導(dǎo)航目錄字體大小b
Swift 5.1 (21) - 泛型
Swift 5.1 (20) - 協(xié)議
Swift 5.1 (19) - 擴(kuò)展
Swift 5.1 (18) - 嵌套類型
Swift 5.1 (17) - 類型轉(zhuǎn)換與模式匹配
淺談編譯過程
深入理解HTTPS