Flutter 中 State
state生命周期
示例代码如下:
class _CounterWidgetState extends State<CounterWidget> {
int _counter;
@override
void initState() {
super.initState();
//初始化状态
_counter=widget.initValue;
print("initState");
}
@override
Widget build(BuildContext context) {
print("build");
return Scaffold(
body: Center(
child: FlatButton(
child: Text('$_counter'),
//点击后计数器自增
onPressed:()=>setState(()=> ++_counter,
),
),
),
);
}
@override
void didUpdateWidget(CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget");
}
@override
void deactivate() {
super.deactivate();
print("deactive");
}
@override
void dispose() {
super.dispose();
print("dispose");
}
@override
void reassemble() {
super.reassemble();
print("reassemble");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies");
}
}
当我们创建一个路由,该路由中只有CounterWidget
wiget
Widget build(BuildContext context) {
return CounterWidget();
}
此时我们运行应用打开路由界面,会log
I/flutter ( 5436): initState
I/flutter ( 5436): didChangeDependencies
I/flutter ( 5436): build
将statefulWeight插入widget树时,首先会调用initState
方法
当点击热重载按钮时
I/flutter ( 5436): reassemble
I/flutter ( 5436): didUpdateWidget
I/flutter ( 5436): build
热重载时,didUpdateWidget
被调用
当将CounnterWidget
移除,将build方法更改
Widget build(BuildContext context) {
//移除计数器
//return CounterWidget();
//随便返回一个Text()
return Text("xxx");
}
此时热重载
I/flutter ( 5436): reassemble
I/flutter ( 5436): deactive
I/flutter ( 5436): dispose
当移除时,调用deactive
和dispose
方法
initState():
当Widget
第一次插入到树中会被调用,对于每个State对象只会调用一次该回调,因此通常在其中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。不能在该方法中调用BuildContext.inheritFromWidgetOfExactType
(该方法用于在Widget树中获取离当前widget最近的父级InheritFromWidget
),原因是在初始化完成后,widget树中的InheritFromWidget可能也会发生变化,所以应该在build()方法或者didChangeDependencies()
中调用它didChangeDependencies()
:当State对象的依赖发生变化时被调用;例如,之前build()
中包含一个InheritedWidget
,之后build()中InheritdWidget
发生变化,此时InheritedWidget
的子widget的didChangeDependencies()
回调都会被调用。典型场景是当系统语言Locale或应用主题改变时,Flutter framework
会通知widget调用此回调build()
:主要用于构建Widget子树,在一下场景被调用- 在
initState()
之后 - 在
didUpdateWidget()
之后 - 调用
setState()
后 - 调用
didChangeDependencies()
后 - 在State对象从树中的一个位置移除后(此时调用
deactive
),又重新插入到其它位置之后
- 在
reassemble()
:回调专门为开发调试提供,在热重载时被调用,在release模式下永远不会被调用didUpdateWidget()
:在widget重新构建时,调用Widget.canUpdate
检测Widget树中同一位置的新旧节点,决定是否需要更新,如果返回true
调用此回调。其Widget.canUpdate
会在新旧widget的key和runtimeType同时相等时返回true,deactivate()
:当State从树中移除,会调用此回调。在一些场景下,Flutter framework
会将State
对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。dispose()
:当State
对象从树中永久移除时调用;通常在此回调中释放资源
注意:在继承StatefulWidget重写其方法时,对于包含
@mustCallSuper
标注的父类方法,都要在子类方法中先调用父类方法。
在Widget树中获取State对象
有时,需要获取StatefulWidget对应的State对象来调用一些方法,例如通过Scaffold
组件对象的状态类ScaffoldState
可以打开SnackBar
。存在两种可以在子widget中获取父StatefuleWidget
的State对象
通过Context获取
context
对象有一个ancestorStateOfType(TypeMatcher)
方法,可以再当前节点沿着widget树向上查找指定类型的StateWidget对应的State对象
Scaffold(
appBar: AppBar(
title: Text("子树中获取State对象"),
),
body: Center(
child: Builder(builder: (context) {
return RaisedButton(
onPressed: () {
// 查找父级最近的Scaffold对应的ScaffoldState对象
ScaffoldState _state = context.ancestorStateOfType(
TypeMatcher<ScaffoldState>());
//调用ScaffoldState的showSnackBar来弹出SnackBar
_state.showSnackBar(
SnackBar(
content: Text("我是SnackBar"),
),
);
},
child: Text("显示SnackBar"),
);
}),
),
);
一般来说,如果StatefulWidget
状态是私有的,不应该直接去获取其State对象。但是通过context.ancestorStateOfType
获取获取statefulWidget 的状态方法是通用的,并不能在语法层面指定其状态是否私有
因此,在Flutter中有一个默认的约定:
- 如果其状态是希望暴露的,则应该在
statefulWidget
中提供一个of
静态方法来获取其state对象,可以直接通过此方法来获取.(这个约定在Flutter的SDK中也是随处可见的)
ScaffoldState _state=Scaffold.of(context);
_state.showSnackBar(
SnackBar(
content: Text("我是SnackBar"),
),
);
GlobalKey
给目标
StatefulWidget
添加GlobalKey
//定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储 static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
...
Scaffold(
key: _globalKey , //设置key
...
)通过
GlobalKey
获取_globalKey.currentState.openDrawer()
globalKey
是在整个APP引用element的机制,如果widget设置了GlobalKey
,可以通过glovalKey.currentWidget
获取该widget对象,globalKey.currentElement
获得widget对应element对象,如果widget是StatefulWidget
,可以通过globalKey.currentState
获得widget对应的state对象
使用
GlobalKey
开销就很大,应该尽量避免使用
同一和GlobalKey
在整个widget树中必须是唯一的不能重复