flutter中state生命周期与app生命周期与路由监听
State生命周期
- 1、第一次展示到屏幕上时会依次调用当前element的构造函数,initState,didChangeDependencies,build
- 2、如果只是自己发生了更新,则只会回调build。如果当前对象的父节点发生更新,则会调用didUpdateWidget和build。如果依赖的InheritedWidget发生了改变,则还会先回调didChangeDependencies。
- 3、当widget被移除的的时候,会依次调用deactive和dispose
initState
initState 是 StatefulWidget 创建完后调用的第一个方法,而且只执行一次,类似于 Android 的 onCreate、iOS 的 viewDidLoad(),所以在这里 View 并没有渲染,但是这时 StatefulWidget 已经被加载到渲染树里了,这时 StatefulWidget 的 mounted 的值会变为 true,直到 dispose 调用的时候才会变为 false。可以在 initState 里做一些初始化的操作
initState()
表示当前 State
将和一个 BuildContext
产生关联,但是此时BuildContext
没有完全装载完成,如果你需要在该方法中获取 BuildContext
,可以使用 Future.delayed(const Duration(seconds: 0, (){//context});
didChangeDependencies
在 initState()
之后调用,当 State
对象的依赖的InheritedWidget 关系发生变化时,该方法被调用,初始化时也会调用。可能会被调用多次。
原理参考:Flutter原理篇:didChangeDependencies什么时候被调用
build
在 StatefulWidget 第一次创建的时候,build 方法会在 didChangeDependencies 方法之后立即调用,另外一种会调用 build 方法的场景是,每当 UI 需要重新渲染的时候,build 都会被调用,所以 build 会被多次调用,然后 返回要渲染的 Widget。千万不要在 build 里做除了创建 Widget 之外的操作,因为这个会影响 UI 的渲染效率。
didUpdateWidget
这个生命周期我们一般不会用到,只有在使用 key 对 Widget 进行复用的时候,当 widget
状态发生变化时,会调用。
deactivate
当 State
被暂时从视图树中移除时,会调用这个方法,同时页面切换时,也会调用。
dispose
当 View 不需要再显示,从渲染树中移除的时候,State 就会永久的从渲染树中移除,就会调用 dispose 生命周期,这时候就可以在 dispose 里做一些取消监听、动画的操作,和 initState 是相反的。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
///createState 是 StatefulWidget 里创建 State 的方法,当要创建新的 StatefulWidget 的时候,会立即执行 createState,而且只执行一次,createState 必须要实现
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isShowChild;
///当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调
@override
void initState() {
super.initState();
isShowChild = true;
debugPrint("parent initState......");
}
///初始化时,在initState()之后立刻调用
///当依赖的InheritedWidget rebuild,会触发此接口被调用
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("parent didChangeDependencies......");
}
///绘制界面,当setState触发的时候会再次被调用
@override
Widget build(BuildContext context) {
debugPrint("parent build......");
return MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
setState(() {
isShowChild = !isShowChild;
});
},
child: isShowChild ? Child() : Text("演示移除Child"),
)),
),
);
}
///状态改变的时候会调用该方法,比如调用了setState
@override
void didUpdateWidget(MyApp oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint("parent didUpdateWidget......");
}
///当State对象从树中被移除时,会调用此回调
@override
void deactivate() {
super.deactivate();
debugPrint('parent deactivate......');
}
///当State对象从树中被永久移除时调用;通常在此回调中释放资源
@override
void dispose() {
super.dispose();
debugPrint('parent dispose......');
}
}
class Child extends StatefulWidget {
@override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
@override
Widget build(BuildContext context) {
debugPrint("child build......");
return Text('lifeCycle');
}
@override
void initState() {
super.initState();
debugPrint("child initState......");
}
///初始化时,在initState()之后立刻调用
///当依赖的InheritedWidget rebuild,会触发此接口被调用
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("child didChangeDependencies......");
}
///父widget状态改变的时候会调用该方法,比如父节点调用了setState
@override
void didUpdateWidget(Child oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint("child didUpdateWidget......");
}
///当State对象从树中被移除时,会调用此回调
@override
void deactivate() {
super.deactivate();
debugPrint('child deactivate......');
}
///当State对象从树中被永久移除时调用;通常在此回调中释放资源
@override
void dispose() {
super.dispose();
debugPrint('child dispose......');
}
}
执行的输出结果显示为:
-
运行到显示
I/flutter (22218): parent initState...... I/flutter (22218): parent didChangeDependencies...... I/flutter (22218): parent build...... I/flutter (22218): child initState...... I/flutter (22218): child didChangeDependencies...... I/flutter (22218): child build......
-
点击按钮会移除Child
I/flutter (22218): parent build...... I/flutter (22218): child deactivate...... I/flutter (22218): child dispose......
-
将MyApp的代码由
child: isShowChild ? Child() : Text("演示移除Child")
,改为child: Child()
,点击按钮时
I/flutter (22765): parent build...... I/flutter (22765): child didUpdateWidget...... I/flutter (22765): child build......
从这些实验中能够得出State的生命周期为:
flutter页面路由监听
1.创建一个全局变量
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
2.在路由配置里面添加监听器
MaterialApp(
// ...
navigatorObservers: <NavigatorObserver>[routeObserver],
),
3.在你需要用到监听的页面使用就好了
/// 路由跳转监控对象
/// 1.state 继承 [RouteAware]
/// 2 didChangeDependencies 添加 routeObserver.subscribe(this, ModalRoute.of(context));
/// 3 在dispose 添加 routeObserver.unSubscribe(this);
/// 4 重写路由事件 [didPopNext], [didPush], [didPop], [didPushNext]
class RouterListenerPage extends StatefulWidget {
const RouterListenerPage({Key? key}) : super(key: key);
@override
State<RouterListenerPage> createState() => _RouterListenerPageState();
}
class _RouterListenerPageState extends State<RouterListenerPage>
with RouteAware {
@override
Widget build(BuildContext context) {
return Container();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute<dynamic>);
}
/// Called when the current route has been pushed.
/// 当前的页面被push显示到用户面前 viewWillAppear.
@override
void didPush() {}
/// Called when the current route has been popped off.
/// 当前的页面被pop viewWillDisappear.
@override
void didPop() {}
/// Called when the top route has been popped off, and the current route
/// shows up.
/// 上面的页面被pop后当前页面被显示时 viewWillAppear.
@override
void didPopNext() {}
/// Called when a new route has been pushed, and the current route is no
/// longer visible.
/// 从当前页面push到另一个页面 viewWillDisappear.
@override
void didPushNext() {}
@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
}
app生命周期
如果想要知道 Flutter App 的生命周期,例如 Flutter 是在前台还是在后台,就需要使用到 WidgetsBindingObserver 了
abstract class WidgetsBindingObserver {
//页面pop
Future didPopRoute() => Future.value(false);
//页面push
Future didPushRoute(String route) => Future.value(false);
//系统窗口相关改变回调,如旋转
void didChangeMetrics() {}
// 文本缩放系数变化
void didChangeTextScaleFactor() {}
// 系统亮度变化
void didChangePlatformBrightness() {}
// 本地化语言变化
void didChangeLocales(List locale) {}
// App生命周期变化
void didChangeAppLifecycleState(AppLifecycleState state) {}
// 内存警告回调
void didHaveMemoryPressure() {}
// Accessibility相关特性回调
void didChangeAccessibilityFeatures() {}
}
可以看到,WidgetsBindingObserver 这个类提供的回调函数非常丰富,常见的屏幕旋转、屏幕亮度、语言变化、内存警告都可以通过这个实现进行回调。我们通过给 WidgetsBinding 的单例对象设置监听器,就可以监听对应的回调方法。监听app生命周期使用方法如下:
///使用
/// 1. State 的类 mixin WidgetsBindingObserver
/// 2. 在 State 的initState()里添加监听 WidgetsBinding.instance.addObserver(this);
/// 3. 在dispose中移除监听 WidgetsBinding.instance.removeObserver(this);
/// 4. 重写方法didChangeAppLifecycleState,该回调函数中,有一个参数类型为 AppLifecycleState 的枚举类,这个枚举类是 Flutter 对 App 生命周期状态的封装。它的常用状态包括 resumed、inactive、paused 这三个。
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
//因为该方法是监听整个app的生命周期的,想监听单页面生命周期,要判断监听的是否是当前页面
if (!ModalRoute.of(context)!.isCurrent) return;
switch (state) {
//可见的,并能响应用户的输入 类似于Android的onResume
case AppLifecycleState.resumed:
break;
//处在不活动状态,无法处理用户响应。界面退到后台或弹出对话框情况下, 即失去了焦点但仍可以执行drawframe回调 类似于Android的onPause
case AppLifecycleState.inactive:
break;
//不可见并不能响应用户的输入,但是在后台继续活动中。应用挂起,比如退到后台,失去了焦点且不会收到drawframe回调. 类似于Android的onStop
case AppLifecycleState.paused:
break;
//APP结束时调用. 类似于Android的onDestroy
case AppLifecycleState.detached:
break;
}
}
封装工具类:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
///监听app 页面生命周期
class PageLifecycleEventHandler extends WidgetsBindingObserver {
final AsyncCallback? resumeCallBack; //前台
final AsyncCallback? suspendingCallBack; //后台
PageLifecycleEventHandler({this.resumeCallBack, this.suspendingCallBack});
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
if (resumeCallBack != null) {
await resumeCallBack!();
}
break;
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
if (suspendingCallBack != null) {
await suspendingCallBack!();
}
break;
}
}
}
State就不需要再混入WidgetsBindingObserver ,使用
单页面生命周期监听:
class _AuctionPageState extends State<AuctionItemDetailPage> {
PageLifecycleEventHandler _lifecycleEventHandler = PageLifecycleEventHandler(
resumeCallBack: () async {}, suspendingCallBack: () async {});
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(_lifecycleEventHandler);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(_lifecycleEventHandler);
super.dispose();
}
}
整个app声明周期监听:
void main() {
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance.addObserver(PageLifecycleEventHandler(
resumeCallBack: () async {}, suspendingCallBack: () async {}));
runApp(MyApp());
}
另一种方式监听生命周期:SystemChannels.lifecycle
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
SystemChannels.lifecycle.setMessageHandler((msg) {
switch (msg) {
case "AppLifecycleState.paused":
print(msg);
break;
case "AppLifecycleState.inactive":
print(msg);
break;
case "AppLifecycleState.resumed":
print(msg);
break;
default:
break;
}
});
}
@override
void dispose() {
super.dispose();
}
}
封装工具类:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class LifecycleEventHandler {
final AsyncCallback resumeCallBack;
final AsyncCallback suspendingCallBack;
LifecycleEventHandler({
this.resumeCallBack,
this.suspendingCallBack,
}) {
SystemChannels.lifecycle.setMessageHandler((msg) async {
switch (msg) {
case "AppLifecycleState.resumed":
if (resumeCallBack != null) {
await resumeCallBack();
}
break;
case "AppLifecycleState.paused":
case "AppLifecycleState.detached":
if (suspendingCallBack != null) {
await suspendingCallBack();
}
break;
default:
break;
}
});
}
}
使用:
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
handleAppLifecycleState(resumeCallBack:()async{
print('resumeCallBack');
},suspendingCallBack:()async{
print('suspendingCallBack');
});
}
}
View 绘制完成
在原生中,还有一个常用操作是监听 View 绘制完成,防止空指针。
//view重绘时回调
view.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
@Override
public void onDraw() {
// TODO Auto-generated method stub
}
Flutter 同样给我们有两个回调函数:
- addPostFrameCallback 只有首次绘制完才回调,是 StatefulWidge 渲染结束的回调,只会被调用一次,之后 StatefulWidget 需要刷新 UI 也不会被调用
- addPersistentFrameCallback 每次重绘都回调
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print("单次Frame绘制回调"); //只回调一次
});
WidgetsBinding.instance.addPersistentFrameCallback((_) {
print("实时Frame绘制回调"); //每帧都回调
});
}
参考:Flutter的生命周期