如何知道State的生命周期?
网上搜索❎
打开源码✅
引言
重写的各个生命周期阶段的方法都是State类的,StatefulElement在初始化的时候会调用Widget.createState方法实例化State,并且持有State(_state)。所以各个周期都在StatefulElement类里面
initState
那么,在StatefulElement类里面搜索「state.initState(」就能看到init方法被调用的地方。
@override
void _firstBuild() {
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
state.didChangeDependencies();
super._firstBuild();
}
可以看到,是在Widget第一次build之前,调用了initState,然后接着调用didChangeDependencies。
didChangeDependencies
接着搜索「state.didChangeDependencies(」,看到didChangeDependencies除了_firstBuild方法中被调用之外,还有一次在performRebuild调用之前被调用。而setState的流程中,节点被标脏,就会执行Element.rebuild→Element.performRebuild。所以可以理解,为什么大伙都说在更改依赖之后,会执行build方法。(详情查看provider实现和setState原理,大致链路为:InheritedElement.notifyClients→Element.didChangeDependencies→markNeedsBuild→performRebuild→build)
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
didUpdateWidget
搜索「state.didUpdateWidget(」,只有一处调用,在父节点更新的时候,会调用updateChild,这时候会调用子组件的update方法(update方法只有这一种调用途径💃),这里面会调用didUpdateWidget,接着是rebuild→performRebuild→build。
注意⚠️:
这里可以看到,didUpdateWidget和didChangeDependencies是有重合的路径的。但是,这段重合,并不会发生在同一个节点。「在父节点的dependency改变之后,Inherited组件发出notify,让父组件调用自己的didChangeDependencies方法,接着父组件的state进入didChangeDependencies状态,然后更新自身并且更新child(通过child.update)方法,让子节点的state进入didUpdateWidget状态。」这段过程中,将「父节点」替换成「consumer」或者「selector」等组件,就完全理解了。详见Provider原理和setState原理
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = state._widget!;
state._widget = widget as StatefulWidget;
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
rebuild(force: true);
}
deactivate
在「framework.dart」文件里面搜索「state.deactivate(」,仅StatefulElement.deactivate方法出现一次。
当节点从树中被移除时调用。节点从树中移除有两种情况,一种是使用了GlobalKey复用组件,即改变使用了GlobalKey的组件的父节点时,先从当前位置移除,然后再插入另一位置;另一种是永久移除节点。
个人感觉,这是一个没那么重要的方法,不展开写了,如何仅调用deactivate,但不调用dispose见下面问题和链接👇
如何通过GlobalKey复用组件(不推荐)?
How to use GlobalKey to maintain widgets' states when changing parents?
dispose
在「framework.dart」文件里面搜索「state.dispose(」,只有在StatefulElement.unmount方法中出现一次。
当节点从树中永久移除时调用。一般在这里释放资源
@override
void unmount() {
super.unmount();
state.dispose();
state._element = null;
// Release resources to reduce the severity of memory leaks caused by
// defunct, but accidentally retained Elements.
_state = null;
}
demo:
上面涉及的didChangeDependencies和didUpdateWidget两个方法的注意处的例子见下面demo:
///数据类
class Count extends ChangeNotifier {
Count();
int count = 0;
void add() {
count++;
notifyListeners();
}
}
///用于触发数据类变更
class DemoPage extends StatefulWidget {
@override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
@override
initState() {
super.initState();
}
@override
dispose() {
super.dispose();
}
@override
void didUpdateWidget(covariant DemoPage oldWidget) {
super.didUpdateWidget(oldWidget);
print("demo page didUpdateWidget");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("demo page didChangeDependencies");
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("DemoPage"),
),
body: Center(
child: DemoWidget(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
//注意这里listen为false,所以不会触发didChangeDependencies
Count count = Provider.of<Count>(context, listen: false);
count.add();
print("count.count = ${count.count}");
},
child: const Icon(Icons.add),
));
}
}
///用于验证didChangeDependencies
class DemoWidget extends StatefulWidget {
@override
_DemoWidgetState createState() => _DemoWidgetState();
}
class _DemoWidgetState extends State<DemoWidget> {
Count? count;
@override
initState() {
super.initState();
}
@override
didChangeDependencies() {
super.didChangeDependencies();
//这里的listen为true,所以会触发didChangeDependencies
count = Provider.of<Count>(context, listen: true);
count?.addListener(() {
setState(() {});
});
print("demo widget didChangeDependencies");
}
@override
void didUpdateWidget(covariant DemoWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("demo widget didUpdateWidget");
}
@override
dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return UpdateText(text:
count == null ? "null" : count!.count.toString(),
);
}
}
///用于验证didUpdateWidget
class UpdateText extends StatefulWidget {
const UpdateText({Key? key, required this.text}) : super(key: key);
final String text;
@override
_UpdateTextState createState() => _UpdateTextState();
}
class _UpdateTextState extends State<UpdateText> {
@override
initState() {
super.initState();
}
@override
dispose() {
super.dispose();
}
@override
void didUpdateWidget(covariant UpdateText oldWidget) {
super.didUpdateWidget(oldWidget);
print("UpdateText didUpdateWidget");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("UpdateText didChangeDependencies");
}
@override
Widget build(BuildContext context) {
return Text(widget.text);
}
}
///入口类
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider<Count>(
create: (_) => Count(),
child: DemoPage(),
),
);
}
}
/*
点击之后输出:
flutter: count.count = 1
flutter: demo widget didChangeDependencies
flutter: UpdateText didUpdateWidget
*/
附上周期图
最后有个注意点
在initState里面无法使用Provider.of<>(context, listen: true)。
如果需要依赖某个ChangeNotifier类型并且每次改变都刷新数据的话,需要在didUpdateWidget方法中使用Provider.of<>(context, listen: true);
如果仅仅是获取ChangeNotifier类型对象,使用数据,不依赖改变的话,可以直接在initState里面Provider.of<>(context, listen: false)获取对象,这样当前的context不会被注册进刷新列表,也就不会出现下面的报错。