Stateful组件生命周期
生命周期一:createState
当StatefulWidget
组件插入到组件树中时,createState
函数由Framework
调用,每一个组件都会有一个单独的State
,当createState
函数执行完毕后,有一个非常重要的属性mounted
被Framework
设置为true
。
class StatefulWidgetDemo extends StatefulWidget {
@override
_StatefulWidgetDemoState createState() => _StatefulWidgetDemoState();
}
生命周期二:initState
initState
函数在组件被插入树中时被Framework
调用(在createState
之后)。
此函数只会被调用一次,子类通常会重写此方法,在其中进行初始化操作,比如加载网络数据,重写此方法时一定要调用super.initState()
,如下:
@override
void initState() {
super.initState();
//初始化...
}
如果此组件需要订阅通知,比如ChangeNotifier
或者Stream
,则需要在不同的生命周期内正确处理订阅和取消订阅通知。
例如eventbus
:
- 在
initState
中订阅通知 - 在
didUpdateWidget
中,如果需要替换旧组件,则在旧对象中取消订阅,并在新对象中订阅通知 - 并在
dispose
中取消订阅
示例一种在initState中的错误写法:
@override
void initState() {
super.initState();
showDialog(context: context, builder: (context) {
return AlertDialog();
});
}
异常信息如下:
dependOnInheritedWidgetOfExactType<_LocalizationsScope>() or dependOnInheritedElement() was Called before _StatefulLifecycleState.initState() completed.
解决方案:
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showDialog(context: context, builder: (context) {
return AlertDialog(title: Text('AlertDialog'),);
});
});
}
生命周期三:didChangeDependencies
didChangeDependencies
方法在initState
之后由Framework
立即调用。
另外,当此State
的依赖项更改时被调用,比如其所依赖的InheritedWidget
发生变化时,Framework
会调用此方法通知组件发生变化。
此方法是生命周期中第一个可以使用BuildContext.dependOnInheritedWidgetOfExactType
的方法,此方法很少会被重写,因为 Framework
会在依赖发生变化时调用build
.需要重写此方法的场景是:依赖发生变化时需要做一些耗时任务,比如网络请求数据。
didChangeDependencies
方法调用后,组件的状态变为dirty
,立即调用build
方法。
生命周期四:build
此方法是我们最熟悉的,在方法中创建各种组件,绘制到屏幕上。 Framework会在多种情况下调用此方法:
- 调用
initState
方法后 - 调用
didUpdateWidget
方法后 - 收到对
setState
的调用后 - 此
State
的依存关系发生更改后(例如,依赖的InheritedWidget
发生了更改)。 - 调用
deactivate
之后,然后将State
重新插入树的另一个位置。
此方法可以在每一帧中调用,此方法中应该只包含构建组件的代码,不应该包含其他额外的功能,尤其是耗时任务。
生命周期五:didUpdateWidget
当组件的configuration
发生变化时调用此函数,当父组件使用相同的runtimeType
和Widget.key
重新构建一个新的组件时,Framework 将更新此State
的组件属性以引用新的组件,然后使用先前的组件作为参数调用此方法。
@override
void didUpdateWidget(covariant StatefulLifecycle oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget');
}
此方法中通常会用当前组件与前组件进行对比。Framework调用完此方法后,会将组件设置为dirty状态,然后调用build 方法,因此无需在此方法中调用setState方法。
生命周期六:deactivate
当框架从树中移除此State
时将会调用此方法,在某些情况下,框架将重新插入State
到树的其他位置(例如,如果包含该树的子树State
从树中的一个位置移植到另一个位置),框架将会调用build
方法来提供State
适应其在树中的新位置。
生命周期七:dispose
当框架从树中永久移除此State
时将会调用此方法,与deactivate
的区别是,deactivate
还可以重新插入到树中,而dispose
表示此State
对象永远不会在 build
。
其他生命周期
reassemble
当开发过程中进行热重载(hot reload)时,Flutter会调用reassemble
方法。通常用于开发调试场景下,刷新资源或重置某些状态。生产环境下不会被调用。dart@override void reassemble() { super.reassemble(); // 热重载时执行的逻辑 }
activate
当State
被插入到树中的某个位置时(包括初次插入和从其他位置重新插入),会调用activate
方法。通常很少需要重写,除非需要在State
激活时执行特定逻辑。dart@override void activate() { super.activate(); // State 激活时执行的逻辑 }
几个重要概念
mounted
是State
中的一个属性,此属性表示当前组件是否在树中,建议在setState
前对mounted
进行判断dirty
表示组件当前的状态为脏状态,下一帧时将会执行build
函数,调用setState
方法或者或执行didUpdateWidget
方法后,组件的状态为dirty
clean
与dirty
相对应,clean
表示组件当前的状态为干净状态,clean
状态下组件不会执行build
函数
执行次数
build 执行几次
- stl app启动时一次 热重启两次
- stf app启动时一次 热重启两次