Flutter 跟 iOS 的 ViewController、Android 的 Activity 一样拥有自己的生命周期, Flutter中一切都是 Widget,Widget 组件也有自己的生命周期。
生命周期流程图
页面的生命周期
在应用中有时候需要判断应用的前后台状态,比如播放视频、埋点统计等等,Flutter 可以通过WidgetsBindingObserver进行状态的判断。WidgetsBindingObserver 要写在被 MaterialApp 或者其他主题包裹的地方。
AppLifecycleState 对应状态说明
- inactive 应用程序处于非激活状态,无法响应用户输入
- pause 应用程序不可见,而且无法响应用户输入,运行在后台
- resumed 应用程序可见并能响应用户输入
生命周期是这样的
- 刚进入Widget,不会调用 AppLifecycleState
- 从前台切换到后台,调用顺序 AppLifecycleState inactive -> AppLifecycleState pause
- 从后台切换到前台,调用顺序 AppLifecycleState inactive -> ApplifecycleState resumed
- 从页面返回,会调用 dispose
实现代码,可以这样进行检测
class _AppLifecycleState extends State<AppLifecycle> with WidgetsBindingObserver {
void dispose() {
super.dispose();
print('页面销毁');
WidgetsBinding.instance?.removeObserver(this);
}
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.inactive:
//应用程序处于闲置状态并且没有收到用户的输入事件。
//注意这个状态,在切换到后台时候会触发,所以流程应该是先冻结窗口,然后停止UI
print('AppLifecycleState.inactive');
break;
case AppLifecycleState.paused:
//应用程序处于不可见状态
print('AppLifecycleState.paused');
break;
case AppLifecycleState.resumed:
//进入页面的时候不会触发该状态
//应用程序处于可见状态,并且可以响应用户的输入事件
print('YMAppLifecycleState.resumed');
break;
case AppLifecycleState.detached:
//从当前页面离开
print('AppLifecycleState.detached');
break;
}
}
其他一些状态的回调
- 底内存发生的回调,原生也有这个方法
@override
void didHaveMemoryPressure() {
super.didHaveMemoryPressure();
print('低内存发生的回调');
}
- 应用尺寸发生改变的回调
@override
void didChangeMetrics() {
super.didChangeMetrics();
print('应用尺寸改变时回调');
}
- 页面 pop 出去的回调
@override
Future<bool> didPopRoute() {
print('页面pop出去的回调')
return super.didPopRoute();
}
- 页面 push 的回调
@override
Future<bool> didPushRoute(String route) {
print('页面push的回调');
return super.didPushRoute(route);
}
- 切换主题的回调
@override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
print('切换主题的回调');
}
Widget组件的生命周期
在Flutter里面万物皆组件,而组件又分为无状态的组件StatelessWidget,和有状态的组件StatefulWidget,StatefulWidget 组件则可以直接改变当前组件的状态而无需重新创建新的实例,而StatelessWidget 就需要创建新的实例。
更个状态的解析
1. createState
createState 是 StatefulWidget 里创建 State 的方法。当要创建新的StatefulWidget的时候,就会立即执行createState。
2. initState
在 StatefulWidget 第一次创建的时候,initState 是 StatefulWidget 创建完后调用的第一个方法,而且只执行一次,类似于 IOS 的 viewDidLoad()、Android 的 onCreate,所以在这里 View 并没有渲染,但是这时 StatefulWidget 已经被加载到渲染树里了,这时 StatefulWidget 的 mount 的值会变为 true,直到 dispose 调用的时候才会变为 false。一些初始化的工作我们都会选择在这里处理。
3. didChangeDependencies
在 StatefulWidget 第一次创建的时候,didChangeDependencies 方法会在 initState 方法之后立即调用,之后当 StatefulWidget 刷新的时候,就不会调用了,除非你的 StatefulWidget 依赖的 InheritedWidget 发生变化之后,didChangeDependencies 才会调用,所以 didChangeDependencies 有可能会被调用多次。
4. build
在 StatefulWidget 第一次创建的时候,build 方法会在 didChangeDependencies 方法之后立即调用,另外一种会调用 build 方法的场景是,每当 UI 需要重新渲染的时候,build 都会被调用,所以 build 会被多次调用,然后返回要渲染的 Widget。
5. didUpdateWidget
didUpdateWidget 这个生命周期我们一般不会用到,只有在使用 key 对 Widget 进行复用的时候才会调用。
6. deactivate
当要将 State 对象从渲染树中移除的时候,就会调用 deactivate 生命周期,这标志着 StatefulWidget 将要销毁,但是有时候 State 不会被销毁,而是重新插入到渲染树种。
7. dispose
当 View 不需要再显示,从渲染树中移除的时候,State 就会永久的从渲染树中移除,就会调用 dispose 生命周期,这时候就可以在 dispose 里做一些取消监听、动画的操作,和 initState 是相反的。
StatelessWidget的生命周期
- 构造方法
- build方法
StatefulWidget的生命周期
- Widget构造方法
- Widget的CreateState
- State的构造方法
- State的initState方法
- didChangeDependencies方法(改变依赖关系),依赖的InheritedWidget发生变化之后,方法也会调用
- State的build 当调用
setState方法,会重新调用build进行渲染 - 当Widget销毁的时候,调用State的dispose
实现代码,可以这样进行检测
class StatefulLifeDemo extends StatefulWidget {
const StatefulLifeDemo({Key? key}) : super(key: key);
@override
_StatefulLifeDemoState createState(){
print('createState');
return _StatefulLifeDemoState();
}
}
class _StatefulLifeDemoState extends State<StatefulLifeDemo> {
@override
void initState() {
super.initState();
print('initState');
}
@override
void dispose() {
super.dispose();
print('dispose');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies');
}
@override
void didUpdateWidget(covariant StatefulLifeDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget');
}
@override
void setState(VoidCallback fn) {
super.setState(fn);
print('setState');
}
@override
Widget build(BuildContext context) {
return Container(
child: Text('111'),
);
}
}