1.setState()干了啥?
@protected
void setState(VoidCallback fn) {
//調(diào)用我們傳入的函數(shù)
final dynamic result = fn() as dynamic;
//當前的element執(zhí)行markNeedsBuild方法
_element.markNeedsBuild();
}
/// owner The object that manages the lifecycle of this element.
/// BuildOwner owner負責管理所有element的構(gòu)建以及生命周期
void markNeedsBuild() {
//element將自己標記為臟
_dirty = true;
//scheduleBuildFor 譯:計劃構(gòu)建
owner.scheduleBuildFor(this);
}
void scheduleBuildFor(Element element) {
onBuildScheduled!();
//將element添加到BuildOwner的_dirtyElements集合中
_dirtyElements.add(element);
//當前element已在臟列表中屬性設(shè)置為true
element._inDirtyList = true;
}
- 小結(jié):setState()就是將當前的
element標記成臟然后交由BuildOwner,并加入到BuildOwner的_dirtyElements臟列表中。
2.那頁面是如何更新的呢?
@override
void drawFrame() {
try {
if (renderViewElement != null)
// buildOwner就是前面提到的負責管理widgetbuild的對象
// This is initialized the first time [runApp] is called.
// 這里的renderViewElement是整個UI樹的根節(jié)點
buildOwner.buildScope(renderViewElement);
super.drawFrame();
//將不再活躍的節(jié)點從UI樹中移除
buildOwner.finalizeTree();
} finally {
/·················/
}
}
void buildScope(Element context, [ VoidCallback callback ]) {
...
//取出臟elements調(diào)用rebuild() rebuild方法最終會觸發(fā)performRebuild
final Element element = _dirtyElements[index];
element.rebuild();
...
}
void performRebuild() {
Widget built;
//這里解釋了為啥setState會觸發(fā)build方法
built = build();
//執(zhí)行updateChild操作(即更新得到最新的子節(jié)點)
//_child為當前element的子element,built則是build后element最新當前的widget
_child = updateChild(_child, built, slot);
}
//setState會觸發(fā)build方法的原因
class StatefulElement extends ComponentElement {
...
Widget build() => state.build(this);
...
//child為子element newWidget為re->build后新的當前element的widget
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
//新的子節(jié)點是空的 老的是有子節(jié)點 則清除子節(jié)點即可
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
//??新的節(jié)點和老的是同一個Widget實例 則return newChild;
if (hasSameSuperclass && child.widget == newWidget) {
newChild = child;
e.g 類似這種情況 返回的都是同一個實例化后的widget
典型的就是`const`優(yōu)化,因為`const`限制了同一個`widget`實例,則這個`widget`里面的`build`方法就不會被調(diào)用
class _Flutter_Test_Widget_LifecycleState
extends State<Flutter_Test_Widget_Lifecycle> {
bool a = true;
HeHe he = HeHe("HEHE");
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
a = !a;
});
},
child: a == true
? he
: he,
//或者是 const HeHe("HEHE");
);
}
}
//????符合這種情況則是child.widget和newWidget是同一類型,但是不是同一個widget實例
//則直接進行child.update操作,直接進行子節(jié)點的子節(jié)點的更新操作(A child B,B child C, 當前是A 判斷到B是同類型但是不同實例,
//則B進行update操作去觸發(fā)B的rebuild操作, A則不會執(zhí)行inflateWidget來對B進行操作,而是對B的子節(jié)點進行操作-性能優(yōu)化)
//??????大坑:child.widget和newWidget是同一類型,但是不是同一個widget實例
//??????會自動進行優(yōu)化,實際上掛載的child還是之前的child(child是elemet,也就是實際渲染元素)
//??????因為inflateWidget也不會調(diào)用,所以`initState`方法也不會被調(diào)用
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
child.update(newWidget);
//??????widget既不符合同一個實例widget也不符合同一類型的weidget,則移除老節(jié)點,觸發(fā)inflateWidget,重新構(gòu)建子節(jié)點
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
//????????如果child為空 即老節(jié)點為空子節(jié)點不為空
newChild = inflateWidget(newWidget, newSlot);
//文字表述偽代碼 表示各種情況
如果之前的位置child為null
A、如果newWidget為null的話,說明這個位置始終沒有子節(jié)點,直接返回null即可。
B、如果newWidget不為null,說明這個位置新增加了子節(jié)點調(diào)用inflateWidget(newWidget, newSlot)生成一個新的Element返回
如果之前的child不為null
C、如果newWidget為null的話,說明這個位置需要移除以前的節(jié)點,調(diào)用 deactivateChild(child)移除并且返回null
D、如果newWidget不為null的話,先調(diào)用Widget.canUpdate(child.widget, newWidget)對比是否能更新。
????????這個方法會對比兩個Widget的runtimeType和key,如果一致則說明子Widget沒有改變,
????????只是需要根據(jù)newWidget(配置清單)更新下當前節(jié)點的數(shù)據(jù)child.update(newWidget);
如果不一致說明這個位置發(fā)生變化,則deactivateChild(child)后返回inflateWidget(newWidget, newSlot)
}
//根據(jù)newWidget(配置清單)更新下當前節(jié)點的數(shù)據(jù)的update 會觸發(fā)`rebuild();`
void update(StatefulWidget newWidget) {
super.update(newWidget);
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
//這個rebuild則是會再次觸發(fā)`performRebuild()`,進而`updateChild()`,于是子節(jié)點再走這樣的一個流程,子節(jié)點的子節(jié)點再走這樣的一個流程,不斷的遞歸直到頁面的最子一級節(jié)點
rebuild();
}
開始FrameWork層會通知Engine表示自己可以進行渲染了,在下一個Vsync信號到來之時,Engine層會通過Windows.onDrawFrame回調(diào)Framework進行整個頁面的構(gòu)建與繪制。每次收到渲染頁面的通知后,Engine調(diào)用Windows.onDrawFrame最終交給_handleDrawFrame()方法進行處理。最后會走到WidgetsBinding.drawFrame()=>buildOwner.buildScope(renderViewElement)=>_dirtyElements[index].rebuild()=>performRebuild()這里會觸發(fā)當前element的widget的build方法=>updateChild()注意這里已經(jīng)是子節(jié)點進行接下來的操作了=>子節(jié)點update()=>子節(jié)點rebuild()=>子節(jié)點performRebuild()...
小結(jié):所以說在widget樹中,越高層的build()里調(diào)用setState()會導致遍歷所有的子節(jié)點=>遍歷所有子節(jié)點的子節(jié)點...
話術(shù)總結(jié):setState()會將當前的element標記為臟,并交由buildOwner,由buildOwner加入自己的臟列表中,等收到頁面渲染的通知后(這里流程簡略掉),會調(diào)用buildOwenr. buildScope (),這里會遍歷臟列表然后每一個都會調(diào)用rebuild(),rebuild()又會調(diào)用performRebuild(),performRebuild()則會調(diào)用build()方法重建當前的element,然后調(diào)用updateChild ()開始更新子節(jié)點,進而觸發(fā)子節(jié)點的rebuild()方法,進行下一輪的周期...一直到最后一個節(jié)點