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