在 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
的生命周期,相信在以后的实际应用中会更加得心应手。
作者:feelingHy
链接:https://juejin.cn/post/7180626500951998520
网友评论