Flutter中State的生命周期
Flutter中State类(状态)一般与StatefulWidget配合使用,来实现Widget的更新。State负责维护StatefulWidget的状态,并保存其状态信息。当Widget的状态发生变化时,用户只需要调用setState()方法,Fultter引擎便会重新构建Widget树来更新UI。其中setState()方法更类似于一个消息机制,用于通知Flutter引擎Widget状态发生了变化,Flutter引擎收到通知后便会更新Widget树。
由于State管理的StatefulWidget的状态信息,并负责了StateWidget控件的更新,因此了解其的生命周期对于Flutter开发具有重要意义。在某些应用场景下我们需要知道State和Widget何时被构建,何时被销毁以及何时被更新等。
1. State简介
State类的接口如以下代码所示,其中我们开发中最常用的为build()和setState()方法,分别用于构建Widget和通知Flutter引擎更新Widget树。State类持有了一个Widget对象,通过该对象来重构Widget树,同时持有一个Widget的一个上下文,来获取该Widget在Widget树中的位置。
//State为一个模板虚类,必须继承才能够使用
@optionalTypeArgs
abstract class State<T extends StatefulWidget> with Diagnosticable {
T? _widget; //State的实例中会持有一个Widget对象
StatefulElement? _element; //持有StatefulElement对象,构建Element树
BuildContext get context { //持有context对象,实际上为StatefulElement
return _element!;
}
Widget build(BuildContext context); //构建Widget树
void initState(){} //初始化State
void didUpdateWidget(covariant T oldWidget) { } //更新Widget
void reassemble() {} //重新装配Widget,一般在热重载时调用
void setState(VoidCallback fn) {} //更新State,后面详细解释
void deactivate() {} //注销Widget,将Widget从树中移除,不释放资源。
void dispose() {} //销毁Widget,将Widget从树中真正移除,并释放资源
void didChangeDependencies() {} //更改依赖,State依赖发生变化
}
在开发中,我们一般只需要重载build()方法来定义子Widget,同时使用setState()方法来通知Flutter引擎更新UI。其中build()方法完全由用户定义的,这里我们给出setState()的主要代码来探究setState()方法的运行机制,代码如下:
//setState()方法的主要代码,略去了调试信息
@protected
void setState(VoidCallback fn) { //传入一个回调函数fn
final dynamic result = fn() as dynamic;
_element!.markNeedsBuild(); //将Element标记为需要重新build()
}
//owner为BuildOwner类型,同样略去调试信息。
void markNeedsBuild() {
owner!.scheduleBuildFor(this); //传入该Element,并进入Flutter引擎的调度序列,等待重构
}
从setState()中的源码可以看出,传入的回调函数会被运行,但是不影响Flutter引擎的build()过程,换句话说,即使回调函数fn()为空,Flutter引擎也会去根据现有的值去更新UI。在代码层面以下两种书写方式是等价的。
//书写方式1:先更新UI值,再调用setState()方法
TextButton(
child: Text('$_counter'),
onPressed:(){
++_counter; //先更新需要变动的UI值
setState(() {}); //再调用setState()方法
},
);
//书写方式2:在setState()方法体中更新UI值
TextButton(
child: Text('$_counter'),
onPressed:(){
setState(() {
++_counter; //在setState()方法中更新UI值
});
},
);
但是为了代码的可读性,即让程序员知道我们在什么地方刷新了UI,还是会将需要更新UI的值放在setState()的回调函数体中。
2.State()的生命周期
清楚地知道State()的生命周期对使用Flutter开发是很有意义的。举例来说,如果在某些情况下我们需要用到Widget中的参数来完成对State<Widget>的初始化操作,我们就可以在initState()方法中通过value=widget.initValue的方式来进行初始化。如果采用State<Widget>的构造函数来进行初始化的话,就会造成代码的冗余和可读性变差的问题。另外,了解State何时被构建何时被销毁,对程序开发也具有重要的意义。
在上一节,我们以及较为详细的介绍了State类中的成员,这里给出一个表格来详细介绍State中方法的作用。
方法 | 作用 |
---|---|
initState() | 初始化State时调用,将State插入渲染树,只会调用一次 |
didChangeDependencies() | State依赖对象变化时调用,如改变语言和主题时会调用该方法。 |
didUpdateWidget() | 组件状态改变时调用,可能调用多次 |
build() | 构建Widget |
deactivate() | 注销,将State移除渲染树,但是暂时不销毁 |
dispose() | 销毁,将State从内存中移除 |
reassemble() | 重组,当热面热加载时会被调用 |
下面我们将以一个简单的页面跳转的代码来探究Flutter的生命周期。代码由两个页面构成,分别为StateDemoPage1(页面1)和StateDemoPage2(页面2),他们都只有一个TextButton控件,StateDemoPage1实现了到StateDemoPage2的一个简单跳转,StateDemoPage2实现了一个点击更换Button颜色的逻辑,通过这个简单跳转我们来观察State()的生命周期,代码如下:
//页面1代码
class StateDemoPage1 extends StatefulWidget{
const StateDemoPage1({
Key key
});
@override
State<StatefulWidget> createState() {
return new _StateDemoPage1();
}
}
class _StateDemoPage1 extends State<StateDemoPage1>{
@override
Widget build(BuildContext context) {
print("_StateDemoPage1: build(创建Widget,构建Widget树)");
return Scaffold(
body: Center(
child: TextButton(
child: Text('跳转到下个页面'),
//点击后计数器自增
onPressed:(){
Navigator.push(context, MaterialPageRoute(builder: (context){
StateDemoPage2();
}));
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.blue)
)
),
),
);
}
@override
void initState() {
super.initState();
print("_StateDemoPage1: initState(初始化State)");
}
@override
void didUpdateWidget(covariant StateDemoPage1 oldWidget) {
super.didUpdateWidget(oldWidget);
print("_StateDemoPage1: didUpdateWidget(更新Widget)");
}
@override
void deactivate() {
super.deactivate();
print("_StateDemoPage1: deactive(注销Widget,将Widget从树中移除,不释放资源。)");
}
@override
void dispose() {
super.dispose();
print("_StateDemoPage1: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)");
}
@override
void reassemble() {
super.reassemble();
print("_StateDemoPage1: reassemble(重组Widget)");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("_StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)");
}
_StateDemoPage1(){
print("_StateDemoPage1: constructor(更改依赖,State依赖发生变化)");
}
}
//页面2代码
class StateDemoPage2 extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return new _StateDemoPage2();
}
}
class _StateDemoPage2 extends State<StateDemoPage2>{
Color mBackGroundColor = Colors.redAccent;
@override
Widget build(BuildContext context) {
print("_StateDemoPage2: build(创建Widget,构建Widget树)");
return Scaffold(
body: Center(
child: TextButton(
child: Text('这里是第二个页面',
style: TextStyle(
color: Colors.white
),),
onPressed: (){
setState(() {
mBackGroundColor=Colors.green;
});
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) => mBackGroundColor)
)
),
),
);
}
@override
void initState() {
super.initState();
print("_StateDemoPage2: initState(初始化State)");
}
@override
void didUpdateWidget(covariant StateDemoPage2 oldWidget) {
super.didUpdateWidget(oldWidget);
print("_StateDemoPage2: didUpdateWidget(更新Widget)");
}
@override
void deactivate() {
super.deactivate();
print("_StateDemoPage2: deactive(注销Widget,将Widget从树中移除,不释放资源。)");
}
@override
void dispose() {
super.dispose();
print("_StateDemoPage2: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)");
}
@override
void reassemble() {
super.reassemble();
print("_StateDemoPage2: reassemble(重组Widget)");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("_StateDemoPage2: didChangeDependencies(更改依赖,State依赖发生变化)");
}
_StateDemoPage2(){
print("_StateDemoPage2: constructor(构造函数)");
}
}
step1: 点击开始运行
页面1中State的初始化过程: 当运行页面1时,依次运行了_StateDemoPage1的:构造函数->initState()->didChangeDependencies()->build()。
_StateDemoPage1: constructor(构造函数)
_StateDemoPage1: initState(初始化State)
_StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)
_StateDemoPage1: build(创建Widget,构建Widget树)
页面1中State的热重载过程: 当我们对页面1进行热重载的时候,运行了_StateDemoPage1的:reassemble()->didUpdateWidget()->build()。
_StateDemoPage1: constructor(构造函数)
_StateDemoPage1: initState(初始化State)
_StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)
_StateDemoPage1: build(创建Widget,构建Widget树)
_StateDemoPage1: reassemble(重组Widget)
_StateDemoPage1: didUpdateWidget(更新Widget)
_StateDemoPage1: build(创建Widget,构建Widget树)
step2:在点击页面1中的跳转按钮后,跳转到页面2
页面2初始化过程:此时,页面2进入了初始化过程,其构建过程与页面1相同。而页面1为不可见状态,但是被没有进入销毁过程。终端输出如下:
_StateDemoPage2: constructor(构造函数)
_StateDemoPage2: initState(初始化State)
_StateDemoPage2: didChangeDependencies(更改依赖,State依赖发生变化)
_StateDemoPage2: build(创建Widget,构建Widget树)
step3:此时点击页面2,更换到绿色按钮
页面2的更新过程: 此时,调用了build()方法来更新了UI。
_StateDemoPage2: build(创建Widget,构建Widget树)
step4: TopBar上的点击返回
页面2的销毁过程: 此时,我们进入了销毁过程,依次调用了_StateDemoPage2的deactive()->dispose()方法来完成页面2的销毁。
_StateDemoPage2: deactive(注销Widget,将Widget从树中移除,不释放资源。)
_StateDemoPage2: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)
StatefulWidget的生命周期如下图所示,我们可以大致分为3个阶段:
- 初始化过程: constructor->initState()->didChangeDependencies()->build()
- 更新过程: reassemble()->didUpdateWidget()
- 销毁过程: deactive()->dispose()
欢迎关注
参考文献
[1] https://book.flutterchina.club/chapter3/
[2] https://blog.csdn.net/u011272795/article/details/82695920