在Flutter组件构建浅析中我们为了简便起见,以StatelessWidget为例梳理了其构建的过程,如果用StatefulWidget替换,其流程不会有大的变化,只是多了一些细节。
StatefulWidget类图
下面这张图方便查看对象之间的关系和其中的方法
StatefulWidget示例代码
使用StatefulWidget的示例代码,在屏幕上点一次更换一个颜色
class Start extends StatefulWidget {
const Start({Key? key}) : super(key: key);
@override
State<Start> createState() => _StartState();
}
class _StartState extends State<Start> {
@override
void initState() {
super.initState();
debugPrint("Start didChangeDependencies");
}
Color _color() {
return Color.fromARGB(
255,
random.nextInt(255),
random.nextInt(255),
random.nextInt(255),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("Start didChangeDependencies");
}
@override
void didUpdateWidget(covariant Start oldWidget) {
super.didUpdateWidget(oldWidget);
// 本示例不会打印
debugPrint("Start didUpdateWidget");
}
@override
Widget build(BuildContext context) {
debugPrint("Start build");
// 在屏幕上点一次更换一个颜色
return GestureDetector(
child: ColoredBox(color: _color()),
onTap: () {
setState(() {
});
},
);
}
}
流程相似之处不再重述了,细节可查看Flutter组件构建浅析,我们通过下面这张图从初始化和setState更新两个方面来说一下不同之处
初始化
- createElement的时候实例化了State,并使其持用了Element和Widget引用
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
state._element = this;
state._widget = widget;
}
- _firstBuild时调用了initState和didChangeDependencies方法。initState只在此处调用一次,是经常要复写的方法,而didChangeDependencies方法很少复写
void _firstBuild() {
try {
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
}
state.didChangeDependencies();
super._firstBuild();
}
- performRebuild先判断了_didChangeDependencies是否为true,初始化是false,InheritedElement会涉及到这个值,这个先按下不表。
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
- build方法调用的是State中的build方法,也是我们经常复写的方法
@override
Widget build() => state.build(this);
setState更新
就上面的示例代码来说,当点击屏幕调用到setstate方法,会触发当前StatefulElement重新build,和初始化的区别在于此时childElement不为空了,更新操作走的是childElement的update方法
if (child != null) {
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
newChild = child;
// 新建的GestureDetector和之前的runtimeType、key一样,可以更新
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
// 走的是update
child.update(newWidget);
newChild = child;
} else {
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
}
不同的子类update方法的实现不一样,StatelessElement比较简单直接重建走performRebuild,示例代码中的GestureDetector就是这种。如果是StatefulElement,会先调用state.didUpdateWidget方法,这个方法是经常需要复写的,主要有两个操作
- 移除oldWidget上面的一些监听之类的
- 使用newWidget的配置更新State
StatefulElement
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
_dirty = true;
// 指向了新widget
state._widget = widget as StatefulWidget;
try {
...
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
}
rebuild();
}
为什么流程图上这个state.didUpdateWidget的颜色和上面state方法的颜色标记不一样,因为它们是属于不同StatefulElement。还有一些点打算放在InheritedWidget中分析