本篇主要认识StatefulWidget、了解其状态管理,简单使用,生命周期等
1. 初始StatefulWidget
@immutable
看源码,StatefulWidget也是继承自Widget,其上有个注解:@immutable
,该注解的意思就是不可变的意思,作用就是widget及其子类是不可变的,定义的变量必须是final的,所以,这就无法在StatelessWidget内实现数据更新操作。
官方@immutable解释
1.1. StatefulWidget 状态管理
StatefulWidget是相对于StatelessWidget实现可变的widget,StatefulWidget也是继承于widget,所以,自然在widget内是做不了变量更新的
既然Widget是不可变,那么StatefulWidget如何来存储可变的状态呢?
- StatelessWidget无所谓,因为它里面的数据通常是直接定义完后就不修改的。
- 但StatefulWidget需要有状态(可以理解成变量)的改变,这如何做到呢?
Flutter将StatefulWidget设计成了两个类:
- 也就是你创建StatefulWidget时必须创建两个类:
- 一个类继承自StatefulWidget,作为Widget树的一部分;
- 一个类继承自State,用于记录StatefulWidget会变化的状态,并且根据状态的变化,构建出新的Widget;
基本结构如下:
class MyStatefulWidget extends StatefulWidget {
@override
State createState() {
// 将创建的State返回
return MyState();
}
}
class MyState extends State {
@override
Widget build(BuildContext context) {
return <构建自己的Widget>;
}
}
2. StatefulWidget简单使用
实现效果:
代码:
class HomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("HomeBody build");
return MyCounterWidget();
}
}
class MyCounterWidget extends StatefulWidget {
MyCounterWidget() {
print("执行了MyCounterWidget的构造方法");
}
@override
State createState() {
print("执行了MyCounterWidget的createState方法");
// 将创建的State返回
return MyCounterState();
}
}
class MyCounterState extends State {
int counter = 0;
MyCounterState() {
print("执行MyCounterState的构造方法");
}
@override
void initState() {
super.initState();
print("执行MyCounterState的init方法");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("执行MyCounterState的didChangeDependencies方法");
}
@override
Widget build(BuildContext context) {
print("执行执行MyCounterState的build方法");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
color: Colors.redAccent,
child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter++;
});
},
),
RaisedButton(
color: Colors.orangeAccent,
child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter--;
});
},
)
],
),
Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
],
),
);
}
@override
void didUpdateWidget(MyCounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("执行MyCounterState的didUpdateWidget方法");
}
@override
void dispose() {
super.dispose();
print("执行MyCounterState的dispose方法");
}
}
分析
- 自定义的State做状态管理,关于state不解释,前端coder肯定懂,我主程是客户端开发
- setState做状态更新:更新完会触发widget重新build
- 记住flutter自定义可变widget的套路:
- 自定义widget:继承StatefulWidget,重载build加载自定义的state
- 自定义state:继承State,实现你真正的ui部分和状态(data)管理
- 快捷代码模块:stless、stful 就会自动不全代码
3. 生命周期
不多解释,既然是个对象,就有生命流程,生命周期也是ui开发数据管理的基本着力点。
- StatelessWidget可以由父Widget直接传入值,调用build方法来构建,整个过程非常简单;
- 而StatefulWidget需要通过State来管理其数据,并且还要监控状态的改变决定是否重新build整个Widget;
- 所以,我们主要讨论StatefulWidget的生命周期,也就是它从创建到销毁的整个过程;
过程
首先,执行StatefulWidget中相关的方法:
- 1、执行StatefulWidget的构造函数(Constructor)来创建出StatefulWidget;
- 2、执行StatefulWidget的createState方法,来创建一个维护StatefulWidget的State对象;
其次,调用createState创建State对象时,执行State类的相关方法:
1、执行State类的构造方法(Constructor)来创建State对象;
-
2、执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求;
- 注意:这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
- 并且如果你阅读源码,你会发现这里有一个注解(annotation):@mustCallSuper
-
3、执行didChangeDependencies方法,这个方法在两种情况下会调用
- 情况一:调用initState会调用;
- 情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
4、Flutter执行build方法,来看一下我们当前的Widget需要渲染哪些Widget;
5、当前的Widget不再使用时,会调用dispose进行销毁;
6、手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets;
7、执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会调用didUpdateWidget方法;
上面的代码直接运行,打印如下:
flutter: HomeBottomBar 构造函数
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行了MyCounterWidget的createState方法
flutter: 执行MyCounterState的构造方法
flutter: 执行MyCounterState的init方法
flutter: 执行MyCounterState的didChangeDependencies方法
flutter: 执行执行MyCounterState的build方法
flutter: HomeBottomBar 构造函数
// 注意:Flutter会build所有的组件两次,如果你是用as开发的话,会有这个问题,但是vscode是正常的,或者flutter run 方法启动,也是只调用一遍的,所以,不用太在意,可以理解为是as的bug,运行到手机上肯定是正常的
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行MyCounterState的didUpdateWidget方法
flutter: 执行执行MyCounterState的build方法
//当我们改变状态,手动执行setState方法后会打印如下结果:
flutter: 执行执行MyCounterState的build方法
3.1. 深入分析
mounted
/// Whether this [State] object is currently in a tree.
///
/// After creating a [State] object and before calling [initState], the
/// framework "mounts" the [State] object by associating it with a
/// [BuildContext]. The [State] object remains mounted until the framework
/// calls [dispose], after which time the framework will never ask the [State]
/// object to [build] again.
///
/// It is an error to call [setState] unless [mounted] is true.
bool get mounted => _element != null;
1、第一句:用于判断state是否在widget树中,也就是判断widget是否加载
2、最后一句:widget加载之前,是不能调用setState的。
3、其他,表示state挂载原理和生命周期
所以,开发中,一般开发中,更新状态的时候,会有widget是否加载的判断,以免报错:
if(mounted){
setState((){
})
}
dirty state & clean state
dirty state
- 它实际是通过一个Element的东西的属性来标记的;
- 将它标记为dirty会等待下一次的重绘检查,强制调用build方法来构建我们的Widget;
clean state的含义是干净的State
- 它表示当前build出来的Widget,下一次重绘检查时不需要重新build;
所以,流程就是,当setState调用的时候,_element!.markNeedsBuild();
将widget标记为需要build将dirty=true
,然后调用scheduleBuildFor将widget加入dirty的widget列表等待rebuild,然后触发更新去rebuild,完后,将widget的dirty=false
,这时候state就是clean干净的了