Flutter 源码阅读 - StatefulWidget 源码分析 & State 生命周期
一、StatefulWidget
StatefulWidget 也是继承自 Widget,重写了 createElement,并且添加了一个新的接口 createState,下面我们看一下它的源码:
看起来是不是很简单,代码不足十行。
createElement方法返回一个StatefulElement类型的Element。createState抽象方法返回一个State类型的实例对象。在给定的位置为StatefulWidget创建可变状态(state)。框架可以在StatefulWidget生命周期内多次调用此方法,比如:将StatefulWidget插入到Widget Tree中的多个位置时,会创建多个单独的State实例,如果将StatefulWidget从Widget Tree中删除,稍后再次将琦插入到Widget Tree中,框架将会再次调用createState创建一个新的State实例对象。
StatefulWidget 我们暂时就先讲到这里, 关于 State 和 StatefulElement 我们在下面会进行分析。
二、StatefulElement
上面讲到 StatefulWidget 中 createElement 会创建一个 StatefulElement 类型的 Element。下面我们就一起看下 StatefulElement 的源码。
在执行 StatefulWidget#createElement 时会把 this 传递进去,此时执行 StatefulElement 的构造方法中我们可以看出会做以下三件事情:
- 首先通过
_state = widget.createState()执行StatefulWidget中的createState进行闯将State实例; - 其次通过
state._element = this将当前对象赋值给State中的_element属性; - 最后通过
state._widget = widget,将StatefulWidget赋值给State中的_widget属性。
通过以上分析我们相应的可以得出以下结论:
StatefulElement持有State状态;State中又会反过来持有StatefulElement和StatefulWidget(当然,State的源码我们还没有看到);StatefulWidget只是负责创建StatefulElement和State,但是并不持有它们。
至此我们已经理清了 StatefulWidget、StatefulElement 和 State 三者之间的关系,关于 State 我们会在后面讲到。现在我们已经知道 StatefulWidget 中的 createState 在何时执行,那么 StatefulElement#createElement 又是在何时执行的呢?下面我们来看一个例子:
import 'package:flutter/material.dart';
void main() {
runApp(
const MyApp(),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return const ColoredBox(
color: Colors.red,
);
}
}通过断点调试可以看出在 Element#inflateWidget 中 通过 newWidget.createElement() 来进行触发 StatefulWidget#createElement 的执行,进而执行 StatefulElement 的构造函数。
关于更多 StatefulElement 内部方法,将在 State 源码以及相关案例中穿插进行。
三、State
State 是一个抽象类,它只定义了一个 build 抽象方法,由于构建 Widget 对象。它是通过StatefulElement#build 方法进行调用的。
如下是 State 源码的部分截图:
从源码中我们也可以对上面的结论得到验证,State 持有 StatefulElement 、StatefulWidget,这里的泛型 T必须是 StatefulWidget 类型,如下图所示:
除此之外 State 中还持有 BuildContext,通过源码我们可以看出 BuildContext 其实就是 StatefulElement 。
BuildContext get context {
return _element!;
}那么现在我们可以思考一下 State 中的生命周期方法在何时调用以及在哪里调用呢?从上面我们得出的结论:StatefulElement 持有 State 状态,State 中又会反过来持有 StatefulElement 和 StatefulWidget,StatefulWidget 只是负责创建 StatefulElement 和 State,但是并不持有它们。不难猜测出,应该是在 StatefulElement 中来触发的,下面我通过一个小的案例来进行研究一下:
void main() {
runApp(
const WrapWidget(),
);
}class WrapWidget extends StatelessWidget {
const WrapWidget({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("StatefulWidget Demo"),
),
body: MyApp(),
),
);
}
}class MyApp extends StatefulWidget {
const MyApp({
super.key,
});
@override
// ignore: no_logic_in_create_state
State<MyApp> createState() {
debugPrint("createState");
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
late int _count = 0;
@override
void initState() {
debugPrint("initState");
super.initState();
}
@override
void didChangeDependencies() {
debugPrint("didChangeDependencies");
super.didChangeDependencies();
}
@override
void didUpdateWidget(MyApp oldWidget) {
debugPrint("didUpdateWidget");
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
debugPrint("deactivate ");
super.deactivate();
}
@override
void dispose() {
debugPrint("dispose");
super.dispose();
}
@override
void reassemble() {
debugPrint("reassemble");
super.reassemble();
}
@override
Widget build(BuildContext context) {
debugPrint("build");
return Column(
children: [
Text('$_count'),
OutlinedButton(
onPressed: () {
setState(() {
_count++;
});
},
child: const Text('OnPress'),
),
],
);
}
}程序刚运行时打印日志如下:
然后我们点击⚡️按钮热重载,控制台输出日志如下:
我们再次点击 OnPress 按钮时,打印日志如下:
此时我们注释掉 WrapWidget 中的 body: MyApp() 这行代码,打印日志如下:
此时结合源码,我们来一起看下各个生命周期函数:
initState: 当Widget第一次插入到Widget Tree中,会执行一次,我们一般在这里可以做一些初始化状态的操作以及订阅通知事件等,通过源码我们可以看出它是在Statefulelement#_firstBuild中执行的;didChangeDependencies: 当State对象的依赖发生变化时会进行调用,例如:例如系统语言Locale或者应用主题等,通过源码我们可以看出它在Statefulelement#_firstBuild和Statefulelement#performRebuild中都会执行;build:在以下场景中都会调用:initState调用之后didUpdateWidget调用之后setState调用之后didChangeDependencies调用之后- 调用
deactivate之后,然后又重新插入到Widtget Tree中
通过源码可以看出它是在
Statefulelement#build中执行的;reassemble:专门为了开发调试而提供的,在hot reload时会被调用,在Release模式下永远不会被调用,通过源码可以看出它是在Statefulelement#reassemble中执行的;didUpdateWidget:在 Widget 重新构建时,Flutter 框架会在Element#updateChild中通过Widget.canUpdate判断是否需要进行更新,如果为 true 则进行更新;在
canUpdate源码中,新旧 widget 的key和runtimeType同时相等时会返回true,也就是说在在新旧widget的key和runtimeType同时相等时didUpdateWidget()就会被调用;deactivate:当 State 对象从树中被移除时将会调用,它将会在Statefulelement#deactivate中进行调用;dispose:当 State 对象从树中被永久移除时调用;通常在此回调中释放资源,它将会在Statefulelement#unmount中进行调用。
总结
至此,结合一些小的案例和源码阅读,我们大致明白了 StatefulWidget、State 以及 StatefulElement 他们三者之间的关系以及 State 的生命周期,相信在以后的实际应用中会更加得心应手。
链接:https://juejin.cn/post/7180626500951998520
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。