当前位置:   article > 正文

Flutter 中的组件绘制完成监听、组件生命周期和APP生命周期_flutter didchangeapplifecyclestate

flutter didchangeapplifecyclestate

Flutter 的 生命周期

说到 Flutter 的生命周期,其实就是说 StatefulWidget 的生命周期,因为 StatelessWidget 是静态控件。

StatefulWidget,通过借助于 State 对象,处理状态变化,并体现在 UI 上。这些阶段,就涵盖了一个组件从加载到卸载的全过程,即生命周期。

而一个应用的生命周期,包括了页面组件的生命周期和整个 app 的生命周期。我们分别了解下。

State 生命周期

首先我们看下 State 里面的这几个方法:

  1. @override
  2. void initState() {
  3. super.initState();
  4. print('MyHomePage==initState');
  5. }
  6. @override
  7. void didChangeDependencies() {
  8. super.didChangeDependencies();
  9. print('MyHomePage==didChangeDependencies');
  10. }
  11. @override
  12. void didUpdateWidget(MyHomePage oldWidget) {
  13. super.didUpdateWidget(oldWidget);
  14. print('MyHomePage==didUpdateWidget');
  15. }
  16. @override
  17. void reassemble() {
  18. super.reassemble();
  19. print('MyHomePage==reassemble');
  20. }
  21. @override
  22. void deactivate() {
  23. super.deactivate();
  24. print('MyHomePage==deactivate');
  25. }
  26. @override
  27. void dispose() {
  28. print('MyHomePage==dispose');
  29. super.dispose();
  30. }
  31. @override
  32. Widget build(BuildContext context) {
  33. print('MyHomePage==build');
  34. 。。。
  35. }

接下来我们运行应用,看下打印日志:

  1. flutter: MyHomePage==initState
  2. flutter: MyHomePage==didChangeDependencies
  3. flutter: MyHomePage==build

然后路由Navigator.push到新页面:

  1. flutter: SecondPage==initState
  2. flutter: SecondPage==didChangeDependencies
  3. flutter: SecondPage==build

然后路由Navigator.pop到回到首页:

  1. flutter: SecondPage==deactivate
  2. flutter: SecondPage==dispose

State 初始化时会依次执行 :构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染。
State 销毁时会依次执行 :deactivate -> dispose 随后完成页面释放。

接下来我们看下这几个方法。

构造方法

构造方法是 State 生命周期的起点,Flutter 会通过调用 StatefulWidget.createState() 来创建一个 State。我们可以通过构造方法,来接收父 Widget 传递的初始化 UI 配置数据。这些配置数据,决定了 Widget 最初的呈现效果。

initState

当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。
但是使用 InheritFromWidget 的时候,不能在该回调中调用BuildContext.dependOnInheritedWidgetOfExactType(该方法用于在Widget树上获取离当前widget最近的一个父级InheritFromWidget,原因是在初始化完成后,Widget树中的InheritFromWidget也可能会发生变化,所以正确的做法应该在在build()方法或didChangeDependencies()中调用它。

didChangeDependencies

didChangeDependencies 则用来专门处理 State 对象依赖关系变化,会在 initState() 调用结束后,被 Flutter 调用。
哪些情况下 State 对象的依赖关系会发生变化呢?比如使用 InheritedWidget 作数据共享的时候,InheritedWidget 的发生了变化,子widget的didChangeDependencies()回调都会被调用。典型的场景是,系统语言 Locale 或应用主题改变时,系统会通知 State 执行 didChangeDependencies 回调方法。

build

它主要是用于构建Widget子树的,会在如下场景被调用:

  • 在调用initState()之后。
  • 在调用didUpdateWidget()之后。
  • 在调用setState()之后。
  • 在调用didChangeDependencies()之后。
  • 在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其它位置之后。

deactivate

deactivate():当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

dispose

dispose():当State对象从树中被永久移除时调用;通常在此回调中释放资源。

didUpdateWidget

下面我们写个嵌套布局,在 MyHomePage 里嵌套 ChildView,然后在 MyHomePage 里调用 setState:

  1. body: Center(
  2. child: Column(
  3. mainAxisAlignment: MainAxisAlignment.center,
  4. children: <Widget>[
  5. FlatButton(
  6. onPressed: () {
  7. setState(() {
  8. _count++;
  9. });
  10. },
  11. child: Text('点击父控件'),
  12. ),
  13. ChildView(),
  14. ],
  15. ),
  16. ),

看下打印信息:

  1. flutter: MyHomePage==build
  2. flutter: ChildView==didUpdateWidget
  3. flutter: ChildView==build

didUpdateWidget:当 Widget 的配置发生变化时,比如,父 Widget 触发重建(即父 Widget 的状态发生变化时),热重载时,系统会调用这个函数。

reassemble

上面我们说热重载的时候,didUpdateWidget会被调用,其实 reassemble 也会被调用,reassemble 是一个 debug 方法,在热重载的时候调用,我们点 IDE 的热重载按钮:

  1. Performing hot reload...
  2. Syncing files to device iPhone 11 Pro...
  3. flutter: MyHomePage==reassemble
  4. flutter: ChildView==reassemble
  5. flutter: MyHomePage==didUpdateWidget
  6. flutter: MyHomePage==build
  7. flutter: ChildView==didUpdateWidget
  8. flutter: ChildView==build
  9. Reloaded 1 of 513 libraries in 286ms.

会先深度调用 reassemble ,然后再 调用 didUpdateWidget 和 build。

生命周期.jpg

APP 的生命周期

WidgetsBindingObserver

在原生开发中,我们都会获取应用是否在前台的状态,在 Flutter 中同样需要。我们可以利用 WidgetsBindingObserver 实现。

  1. abstract class WidgetsBindingObserver {
  2. //页面pop
  3. Future didPopRoute() => Future.value(false);
  4. //页面push
  5. Future didPushRoute(String route) => Future.value(false);
  6. //系统窗口相关改变回调,如旋转
  7. void didChangeMetrics() {}
  8. // 文本缩放系数变化
  9. void didChangeTextScaleFactor() {}
  10. // 系统亮度变化
  11. void didChangePlatformBrightness() {}
  12. // 本地化语言变化
  13. void didChangeLocales(List locale) {}
  14. // App生命周期变化
  15. void didChangeAppLifecycleState(AppLifecycleState state) {}
  16. // 内存警告回调
  17. void didHaveMemoryPressure() {}
  18. // Accessibility相关特性回调
  19. void didChangeAccessibilityFeatures() {}
  20. }

可以看到,WidgetsBindingObserver 这个类提供的回调函数非常丰富,常见的屏幕旋转、屏幕亮度、语言变化、内存警告都可以通过这个实现进行回调。我们通过给 WidgetsBinding 的单例对象设置监听器,就可以监听对应的回调方法。

下面我们写个demo:

  1. class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  2. @override
  3. void initState() {
  4. super.initState();
  5. WidgetsBinding.instance.addObserver(this);
  6. }
  7. @override
  8. void dispose() {
  9. WidgetsBinding.instance.removeObserver(this);
  10. super.dispose();
  11. }
  12. @override
  13. void didChangeAppLifecycleState(AppLifecycleState state) async {
  14. print("$state");
  15. if (state == AppLifecycleState.resumed) {
  16. }
  17. }
  18. }

从前台切到后台:

  1. flutter: AppLifecycleState.inactive
  2. flutter: AppLifecycleState.paused

再从后台切刀前台:

  1. flutter: AppLifecycleState.inactive
  2. flutter: AppLifecycleState.resumed

didChangeAppLifecycleState

didChangeAppLifecycleState 回调函数中,有一个参数类型为 AppLifecycleState 的枚举类,这个枚举类是 Flutter 对 App 生命周期状态的封装。它的常用状态包括 resumed、inactive、paused 这三个。

  • resumed:可见的,并能响应用户的输入。
  • inactive:处在不活动状态,无法处理用户响应。
  • paused:不可见并不能响应用户的输入,但是在后台继续活动中。

前后台.001.jpeg

封装一个工具类:

  1. class LifecycleEventHandler extends WidgetsBindingObserver{
  2. static const String TAG = '==lifecycle_event_handler==';
  3. final AsyncCallback resumeCallBack;
  4. final AsyncCallback suspendingCallBack;
  5. LifecycleEventHandler({
  6. this.resumeCallBack,
  7. this.suspendingCallBack,
  8. });
  9. @override
  10. Future<Null> didChangeAppLifecycleState(AppLifecycleState state) async {
  11. switch (state) {
  12. case AppLifecycleState.resumed:
  13. if (resumeCallBack != null) {
  14. await resumeCallBack();
  15. }
  16. break;
  17. case AppLifecycleState.inactive:
  18. case AppLifecycleState.paused:
  19. case AppLifecycleState.detached:
  20. if (suspendingCallBack != null) {
  21. await suspendingCallBack();
  22. }
  23. break;
  24. }
  25. }
  26. }

State就不需要再混入WidgetsBindingObserver ,使用起来就简单多了:

  1. class _MyHomePageState extends State<MyHomePage> {
  2. LifecycleEventHandler _lifecycleEventHandler = LifecycleEventHandler(
  3. resumeCallBack: () async {}, suspendingCallBack: () async {});
  4. @override
  5. void initState() {
  6. super.initState();
  7. WidgetsBinding.instance.addObserver(_lifecycleEventHandler);
  8. }
  9. @override
  10. void dispose() {
  11. WidgetsBinding.instance.removeObserver(_lifecycleEventHandler);
  12. super.dispose();
  13. }
  14. }

另一种用法:
我们可以在入口函数里直接调用,这样就不用侵入widget了:

  1. void main() {
  2. WidgetsFlutterBinding.ensureInitialized();
  3. WidgetsBinding.instance.addObserver(LifecycleEventHandler(
  4. resumeCallBack: () async {}, suspendingCallBack: () async {}));
  5. runApp(MyApp());
  6. }

SystemChannels.lifecycle

除了上面的方法,Flutter 还为我们提供了一种方法, SystemChannels.lifecycle,同样可以监听到 APP 的生命周期:

  1. class _MyHomePageState extends State<MyHomePage> {
  2. @override
  3. void initState() {
  4. super.initState();
  5. SystemChannels.lifecycle.setMessageHandler((msg) {
  6. switch (msg) {
  7. case "AppLifecycleState.paused":
  8. print(msg);
  9. break;
  10. case "AppLifecycleState.inactive":
  11. print(msg);
  12. break;
  13. case "AppLifecycleState.resumed":
  14. print(msg);
  15. break;
  16. default:
  17. break;
  18. }
  19. });
  20. }
  21. @override
  22. void dispose() {
  23. super.dispose();
  24. }
  25. }

前后台切换:

  1. Syncing files to device iPhone 11 Pro...
  2. flutter: MyHomePage==build
  3. flutter: AppLifecycleState.inactive
  4. flutter: AppLifecycleState.paused
  5. flutter: AppLifecycleState.inactive
  6. flutter: AppLifecycleState.resumed

同样我们可以封装个工具类:

  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. class LifecycleEventHandler {
  5. final AsyncCallback resumeCallBack;
  6. final AsyncCallback suspendingCallBack;
  7. LifecycleEventHandler({
  8. this.resumeCallBack,
  9. this.suspendingCallBack,
  10. }) {
  11. SystemChannels.lifecycle.setMessageHandler((msg) async {
  12. switch (msg) {
  13. case "AppLifecycleState.resumed":
  14. if (resumeCallBack != null) {
  15. await resumeCallBack();
  16. }
  17. break;
  18. case "AppLifecycleState.paused":
  19. case "AppLifecycleState.detached":
  20. if (suspendingCallBack != null) {
  21. await suspendingCallBack();
  22. }
  23. break;
  24. default:
  25. break;
  26. }
  27. });
  28. }
  29. }

然后使用:

  1. class _MyHomePageState extends State<MyHomePage> {
  2. @override
  3. void initState() {
  4. super.initState();
  5. handleAppLifecycleState(resumeCallBack:()async{
  6. print('resumeCallBack');
  7. },suspendingCallBack:()async{
  8. print('suspendingCallBack');
  9. });
  10. }
  11. }

切换前后台:

  1. flutter: MyHomePage==build
  2. flutter: suspendingCallBack
  3. flutter: resumeCallBack

为了不入侵 Widget ,同样可以在入口函数处调用:

  1. void main() {
  2. WidgetsFlutterBinding.ensureInitialized();
  3. handleAppLifecycleState(resumeCallBack: () async {
  4. print('resumeCallBack');
  5. }, suspendingCallBack: () async {
  6. print('suspendingCallBack');
  7. });
  8. runApp(MyApp());
  9. }

View 绘制完成

在原生中,还有一个常用操作是监听 View 绘制完成,防止空指针。

  1. //view重绘时回调
  2. view.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
  3. @Override
  4. public void onDraw() {
  5. // TODO Auto-generated method stub
  6. }

Flutter 同样给我们有两个回调函数:

  1. addPostFrameCallback 只有首次绘制完才回调
  2. addPersistentFrameCallback 每次重绘都回调
  1. @override
  2. void initState() {
  3. super.initState();
  4. WidgetsBinding.instance.addObserver(this);
  5. WidgetsBinding.instance.addPostFrameCallback((_) {
  6. print("单次Frame绘制回调"); //只回调一次
  7. });
  8. WidgetsBinding.instance.addPersistentFrameCallback((_) {
  9. print("实时Frame绘制回调"); //每帧都回调
  10. });
  11. }
  1. flutter: MyHomePage==build
  2. flutter: 实时Frame绘制回调
  3. flutter: 单次Frame绘制回调
  4. flutter: 实时Frame绘制回调

点击 setState:

  1. flutter: MyHomePage==build
  2. flutter: 实时Frame绘制回调
  3. flutter: 实时Frame绘制回调
  4. flutter: 实时Frame绘制回调
  5. flutter: 实时Frame绘制回调
  6. flutter: 实时Frame绘制回调
  7. flutter: 实时Frame绘制回调
  8. flutter: 实时Frame绘制回调
  9. flutter: 实时Frame绘制回调

因为是递归回调的,所以会调用多次。
有了这两个函数,我们可以实现原生一样的功能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/308682
推荐阅读
相关标签
  

闽ICP备14008679号