flutter中state生命周期与app生命周期与路由监听

State生命周期

  • 1、第一次展示到屏幕上时会依次调用当前element的构造函数,initState,didChangeDependencies,build
  • 2、如果只是自己发生了更新,则只会回调build。如果当前对象的父节点发生更新,则会调用didUpdateWidget和build。如果依赖的InheritedWidget发生了改变,则还会先回调didChangeDependencies。
  • 3、当widget被移除的的时候,会依次调用deactive和dispose

Flutter之所以渲染效率高,是因为Flutter采用的是增量渲染的机制,判断一个Widget应该被更新重新渲染,是调用的

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

inheritedWidget源码:

abstract class InheritedWidget extends ProxyWidget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const InheritedWidget({ Key? key, required Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  /// Whether the framework should notify widgets that inherit from this widget.
  ///
  /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
  /// inherit from this widget but sometimes we do not. For example, if the data
  /// held by this widget is the same as the data held by `oldWidget`, then we
  /// do not need to rebuild the widgets that inherited the data held by
  /// `oldWidget`.
  ///
  /// The framework distinguishes these cases by calling this function with the
  /// widget that previously occupied this location in the tree as an argument.
  /// The given widget is guaranteed to have the same [runtimeType] as this
  /// object.
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

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

  1.  初始化时也会调用。
  2. 在 initState() 之后调用,当 State 对象的依赖的InheritedWidget 关系发生变化时,该方法被调用。而这个“依赖”指的就是子 widget 是否使用了父 widget 中InheritedWidget的数据!如果使用了,则代表子 widget 有依赖;如果没有使用则代表没有依赖。
  3. key发生变化时会被调用 

总结:在它的 State 对象对应的 Element 被 createElement() 的时候并且被Mount的时候就会被调用,简单就是说当这个对应的 Element 被重新创建的时候就会被调用

原理参考:Flutter原理篇:didChangeDependencies什么时候被调用

build

在 StatefulWidget 第一次创建的时候,build 方法会在 didChangeDependencies 方法之后立即调用,另外一种会调用 build 方法的场景是,每当 UI 需要重新渲染的时候,build 都会被调用,所以 build 会被多次调用,然后 返回要渲染的 Widget。千万不要在 build 里做除了创建 Widget 之外的操作,因为这个会影响 UI 的渲染效率。

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

didUpdateWidget

这个生命周期我们一般不会用到,只有在使用 key 对 Widget 进行复用的时候,当  widget 状态发生变化时,会调用。

deactivate

当 State 被暂时从视图树中移除时,会调用这个方法,同时页面切换时,也会调用。

当元素从 Widget 树中被移除或移动时被调用。如果一个元素在同一帧期间被移动了且它有 GlobalKey,那么它仍然能够被激活

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页面路由监听

RouteAware 类似App原生页面生命周期,有四大回调方法:didPopNext,didPush,didPop,didPushNext。

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 生命周期官方组件3.13版本之前使用 WidgetsBindingObserver,3.13版本后使用 AppLifecycleListener,两者处理使用方式不同外,后者增加了 onStateChange 、onExitRequested 等多个状态。

WidgetsBindingObserver

如果想要知道 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');
    });
  }
  }

3.13版本后使用 AppLifecycleListener

官方文档:AppLifecycleListener class - widgets library - Dart API

其中有几个回调方法,不是所有平台都支持,我测试了 IOS 和 Android,其中:

onDetach:IOS支持,Android不支持;
onExitRequested:都不支持,也有可能是我测试手法有问题,我是直接退出App;
官方文档:exitApplication method - ServicesBinding mixin - services library - Dart API
 

AppLifecycleListener 能够有效地监视和响应各个应用程序生命周期状态以及它们之间的转换。此外, onExitRequested 回调简化了退出请求的管理,允许根据应用的特定要求更顺利地取消或执行退出过程。

如果想 不通过观察方法,直接拿到App状态,使用 SchedulerBinding.instance.lifecycleState 获取到状态值;

class AppLifecyclePage extends StatefulWidget {
  const AppLifecyclePage({super.key});

  @override
  State<AppLifecyclePage> createState() => _AppLifecyclePageState();
}

class _AppLifecyclePageState extends State<AppLifecyclePage> {
  late final AppLifecycleListener _listener;

  @override
  void initState() {
    
 var lifecycleState = SchedulerBinding.instance.lifecycleState;
    debugPrint('lifecycleState:$lifecycleState'); // lifecycleState:AppLifecycleState.resumed

 

    _listener = AppLifecycleListener(
      onDetach: _onDetach,
      onHide: _onHide,
      onInactive: _onInactive,
      onPause: _onPause,
      onRestart: _onRestart,
      onResume: _onResume,
      onShow: _onShow,
      onStateChange: _onStateChanged,
      onExitRequested: _onExitRequested,
    );
super.initState();
  }

  @override
  void dispose() {
    _listener.dispose();
    super.dispose();
  }

  void _onDetach() {
    print('onDetach');
  }

  void _onHide() {
    print('onHide');
  }

  void _onInactive() {
    print('onInactive');
  }

  void _onPause() {
    print('onPause');
  }

  void _onRestart() {
    print('onRestart');
  }

  void _onResume() {
    print('onResume');
  }

  void _onStateChanged(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.detached:
        print('detached');
      case AppLifecycleState.resumed:
        print('resumed'); 
      case AppLifecycleState.inactive:
        print('inactive');
      case AppLifecycleState.hidden:
        print('hidden');
      case AppLifecycleState.paused:
        print('paused');
    }
  }

  // 询问用户是否要退出应用程序。如果用户
  // 取消退出,返回AppExitResponse.cancel。否则,
  // 返回 AppExitResponse.exit。
  Future<AppExitResponse> _onExitRequested() async {
    final response = await showDialog<AppExitResponse>(
      context: context,
      barrierDismissible: false,
      builder: (context) => AlertDialog.adaptive(
        title: const Text('您确定要退出这个应用程序吗?'),
        content: const Text('所有未保存的进度都将丢失。'),
        actions: [
          TextButton(
            child: const Text('取消'),
            onPressed: () {
              Navigator.of(context).pop(AppExitResponse.cancel);
            },
          ),
          TextButton(
            child: const Text('确定'),
            onPressed: () {
              Navigator.of(context).pop(AppExitResponse.exit);
            },
          ),
        ],
      ),
    );
    return response ?? AppExitResponse.exit;
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Placeholder(),
    );
  }
}

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 全面解析!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值