Flutter 学习笔记 Element 流程
年初开始学习使用Flutter,对于setState()、element与widget的关系了解的不透彻,有一段时间的上手经验之后再看大佬的文章有了一些感觉,所以写了一些自己的学习总结防止忘记,也不一定正确敬请指正
以下是大佬的两篇文章,建议先多看几遍,当然其他大佬的文章也很多,关键一定多看几遍看仔细
Element
inflateWidget
Element inflateWidget(Widget newWidget, dynamic newSlot){
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
通过子widget创建新的子element
,并将子element
挂载到element
树中,源码的注释中解释,此方法通常由[updateChild]
调用,但可以由需要对创建元素进行更细粒度控制的子类直接调用。简单理解是有updateChild
调用的,为什么命名变量叫newChild
,就是因为一般在updateChild
中调用inflateWidget()
返回给父element
(也就是当前element)
mount
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
_updateInheritance();
}
_updateInheritance
方法顾名思义应该是inheritwidget
相关,先不管这个
注意到函数参数parent就是inflateWidget
的this,这也佐证了上面inflateWidget
通常由父element.updateChild
调用
updateChild
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
//child == null && newWidget == null
deactivateChild(child);
//child != null && newWidget == null
return null;
}
if (child != null) {
if (child.widget == newWidget) {
//child != null && newWidget == child.widget
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
//child != null && Widget.canUpdate(child.widget, newWidget)
child.update(newWidget);
return child;
}
deactivateChild(child);
}
// child != null && !Widget.canUpdate(child.widget, newWidget)
return inflateWidget(newWidget, newSlot);
}
- 如果之前的位置child为null
- 如果newWidget为null的话,说明这个位置始终没有子节点,直接返回null即可。
- 如果newWidget不为null,说明这个位置新增加了子节点调用inflateWidget(newWidget, newSlot)生成一个新的Element返回
- 如果之前的child不为null
- 如果newWidget为null的话,说明这个位置需要移除以前的节点,调用 deactivateChild(child)移除并且返回null
- 如果newWidget不为null的话,先调用Widget.canUpdate(child.widget, newWidget)对比是否能更新。这个方法会对比两个Widget的runtimeType和key,如果一致则说明子Widget没有改变,只是需要根据newWidget(配置清单)更新下当前节点的数据child.update(newWidget);如果不一致说明这个位置发生变化,则deactivateChild(child)后返回inflateWidget(newWidget, newSlot)
update
@mustCallSuper
void update(covariant Widget newWidget) {
_widget = newWidget;
}
抽象方法,内容也很简单,简单理解就是子element
更新自身的widget,相当于复用子element
,重新赋值了子widget
rebuild和performRebuild
void rebuild() {
performRebuild();
}
@protected
void performRebuild();
这两个方法就更简单了就是要给子类重写的....
疑问
大概理解了上面的内容之后,又出现了几个新的问题
updateChild
什么时候调用呢Element
没有_child变量,updateChild
之后生成的element又给了谁呢- 假如A的child是B,A通过
updateChild
生成B之后,B如何生成B自己的child
上面这些问题都可以在源码中找到答案,ComponentElement、StatelessElement、StatefulElement这些子类中重写上述的方法,我们的疑问可以在子类的得到解决
ComponentElement
mount
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_firstBuild();
}
这里新增了一个方法
_firstBuild
void _firstBuild() {
rebuild();
}
看到重新调用了rebuild()
;
在之前文章中,buildOwner.buildScope(renderViewElement)
方法中遍历脏element数组执行_dirtyElements[index].rebuild()
performRebuild
@override
void performRebuild() {
Widget built;
built = build();
_child = updateChild(_child, built, slot);
}
Element中performRebuild
是空,ComponentElement
重写了方法,同时创建了Element _child变量,并在updateChild
中inflateWidget
生成新的子element并赋值给_child
firstBuild
顾名思义第一次创建挂载element时触发
rebuild
顾名思义刷新界面时触发
在ComponentElement
中都是触发performRebuild
,并没什么不同,Stateless与Stateful
中会有不同的处理
StatelessElement
update
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
StatelessElement
中只有update
重写,调用了rebuild
,之后的流程就是performRebuild
等等
看到这里梳理一下思绪
假如StatelessWidget A child StatelessWidget B
那么StatelessElement Aelement,Belement
当第一次加载的时候调用顺序是
parent.inflateWidget => A.createElement => Aelement.mount => Aelement.firstBuild => Aelement.rebuild => Aelement.performRebuild => Aelement.updateChild =>
因为此时_child是空 所以Belement也走一遍重新创建的流程
当设置Aelement.markneedBuild
时 Aelement.rebuild
,
Belement已经存在,Aelement._child
不为空,那么Aelement.updateChild
比较,canUpdate==true
时 _child.update => _child.rebuild
下面这张图对照着看加深下印象
回答疑问
updateChild
什么时候调用呢
rebuild
的时候调用performRebuild
,接着调用updateChild
,rebuild
是什时候调用呢?是在firstBuild
和系统刷新回调脏数组遍历脏数组调用rebuild
Element
没有_child变量,updateChild
之后生成的element又给了谁呢
- 子类中定义
_child
,updateChild
赋值给_child
- 假如A的child是B,A通过updateChild生成B之后,B如何生成B自己的child
update
方法中rebuild
自己更新child
转载自:https://juejin.cn/post/6955734850833743880