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 的渲染效率。

原理参考:Flutter运行原理篇之Build构建的过程

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 同样给我们有两个回调函数:

  1. addPostFrameCallback 只有首次绘制完才回调,是 StatefulWidge 渲染结束的回调,只会被调用一次,之后 StatefulWidget 需要刷新 UI 也不会被调用
  2. addPersistentFrameCallback 每次重绘都回调
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      print("单次Frame绘制回调"); //只回调一次
    });
    WidgetsBinding.instance.addPersistentFrameCallback((_) {
      print("实时Frame绘制回调"); //每帧都回调
    });
  }
 


 

参考:Flutter的生命周期

http://t.csdn.cn/lF6ok

Flutter 进阶攻略:Widget Lifecycle 全面解析!