StatefulWidget 创建流程详解
首先需要说明的是,在阅读下面内容之前,需要有一定的widget,element,renderObjetc树的理解
StatefulWidget
其实就widget本身而言,StatefulWidget
和StatelessWidget
并没有太大的区别,都是描述文件,不存在状态变不变的概念,或者说都是一次性的状态,内部的属性定义最好都是用final
来修饰,但是我们在开发中常常用来区分有状态和无状态,实际本质依赖的是widget后面的element,比如StatefulWidget
之所以可以保留状态,是因为他的elementStatefulElement
没有改变,导致element的一个属性state
没有改变,间接的可以理解为element本身绑定了状态,StatelessWidget
没有状态是因为他的elementStatelessElement
没有绑定状态
简单的理解过后,我们来梳理一下StatefulWidget
完整的初始化流程(包括他的element以及state)
flutter页面的刷新依赖于系统的vsync
回调,在这个回调中,会进行rebuild,下面是rebuild中的重要的函数
1. updateChild
这个方法中做了很多事情,针对这里说的问题,我只拿出一个点就是,通过widget创建element
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
final Element newChild;
newChild = inflateWidget(newWidget, newSlot);
return newChild;
}
2. inflateWidget
还是拿重点,这一步创建了Element,并且调用了element 的 mount方法
Element inflateWidget(Widget newWidget, Object? newSlot) {
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
3. createElement
StatefulWidget
中创建的是StatefulElement
,在StatefulElement
构造函数中,又通过widget的createState
方法创建了state
,并且将widget赋值了给state._widget
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
state._element = this;
state._widget = widget;
}
4. mount
StatefulElement
没有实现,所以走的他的父类ComponentElement
,里边调用了_firstBuild
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
5. _firstBuild
可以看到面试中长问的一个问题didChangeDependencies
什么时候会调用,这里可以明确的知道在element被创建的时候,或者说是state初始化后
void _firstBuild() {
state.didChangeDependencies();
super._firstBuild();
}
这里会调用父类的_firstBuild
,而父类只是调用了一下rebuild
,而rebuild
也仅是做了一件事,调用performRebuild
,因为rebuild
是定义在Element
中的,他只是用来做一些断言判断的,所以具体的如何rebuild是下放到子类去完成的
6. performRebuild(StatefulElement)
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
这里再次出现didChangeDependencies
,但是在初始化的时候_didChangeDependencies
是false
,所以state.didChangeDependencies()
不会调用重复,这里又引出了didChangeDependencies
被调用的另一种情况,就是在rebuild的时候,发现_didChangeDependencies
为true,就调用,而_didChangeDependencies
的修改是在InheritedWidget
通知监听组件时候设置的(更具体的内容请看我之前的文章InheritedWidget 详细理解)
7. performRebuild(ComponentElement)
void performRebuild() {
Widget? built;
built = build();
super.performRebuild();
_child = updateChild(_child, built, slot);
}
这里边执行了build()
,在StatefulElement
的实现是Widget build() => state.build(this);
,所以最终调用的就是我们在state中经常要写的build
函数,super.performRebuild()
只是将自己设置为_dirty = false
,表示下一帧不需要rebuild我
_child = updateChild(_child, built, slot)
就是在build执行完之后,有了新的子widget,再根据子widget重复上述步骤来创建子Element