zoukankan      html  css  js  c++  java
  • flutter 生命周期

    前言:生命周期是一个组件加载到卸载的整个周期,熟悉生命周期可以让我们在合适的时机做该做的事情,
    flutter中的State生命周期和android以及React Native的生命周期类似。

    先看一张生命周期的流程图:


    大致可以分为3个阶段:

    初始化
    状态变化
    组件移除
    初始化
    State初始化时会依次执行 : 构造函数 > initState > didChangeDependencies > Widget build , 此时页面加载完成。

    然后我们看一下每个函数的意义:

    构造函数
    调用次数:1次

    这个函数严格意义上来讲不属于生命周期的一部分,因为这个时候State的widget属性为空,无法在构造函数中访问widget的属性 。但是构造函数必然是要第一个调用的。可以在这一部分接收前一个页面传递过来的数据。

    initState
    Called when this object is inserted into the tree.

    调用次数:1次

    当插入渲染树的时候调用,这个函数在生命周期中只调用一次。这里可以做一些初始化工作,比如初始化State的变量。

    didChangeDependencies
    Called when a dependency of this [State] object changes.

    初始化时,在initState()之后立刻调用
    当依赖的InheritedWidget rebuild,会触发此接口被调用
    这个函数会紧跟在initState之后调用,并且可以调用BuildContext.inheritFromWidgetOfExactType,那么BuildContext.inheritFromWidgetOfExactType的使用场景是什么呢?最经典的应用场景是

    new DefaultTabController(length: 3, child: new TabBar(
          tabs: [ "主页","订单","我的" ]
          .map( (data)=>new Text(data) ).toList(),

     TabBar本来需要定义一个TabController,但是在外面套一层DefaultTabController就不需要定义TabContrller了,看下源码:

    @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        _updateTabController();
        _initIndicatorPainter();
      }
    
    void _updateTabController() {
        final TabController newController = widget.controller ?? DefaultTabController.of(context);
        ...
        }

    注意到这里DefaultTabController.of(context)

    static TabController of(BuildContext context) {
        final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope);
        return scope?.controller;
      }

    实际上就是调用BuildContext.inheritFromWidgetOfExactType,也就说在didChangeDependencies中,可以跨组件拿到数据。

    运行时
    build
    调用次数:多次

    初始化之后开始绘制界面,当setState触发的时候会再次被调用

    didUpdateWidget
    Called whenever the widget configuration changes.

    祖先节点rebuild widget时调用 .当组件的状态改变的时候就会调用didUpdateWidget.

    理论上setState的时候会调用,但我实际操作的时候发现只是做setState的操作的时候没有调用这个方法。而在我改变代码hot reload时候会调用 didUpdateWidget 并执行 build…

    实际上这里flutter框架会创建一个新的Widget,绑定本State,并在这个函数中传递老的Widget。
    这个函数一般用于比较新、老Widget,看看哪些属性改变了,并对State做一些调整。

    需要注意的是,涉及到controller的变更,需要在这个函数中移除老的controller的监听,并创建新controller的监听。

    组件移除
    组件移除,例如页面销毁的时候会依次执行:deactivate > dispose

    deactivate
    Called when this object is removed from the tree.

    在dispose之前,会调用这个函数。实测在组件课件状态变化的时候会调用,当组件卸载时也会先一步dispose调用。

    dispose
    Called when this object is removed from the tree permanently.

    调用次数:1次

    一旦到这个阶段,组件就要被销毁了,这个函数一般会移除监听,清理环境。

    reassemble
    hot reload调用

    名称 状态
    initState 插入渲染树时调用,只调用一次
    didChangeDependencies state依赖的对象发生变化时调用
    didUpdateWidget 组件状态改变时候调用,可能会调用多次
    build 构建Widget时调用
    deactivate 当移除渲染树的时候调用
    dispose 组件即将销毁时调用
    实际场景
    假设我们从A页面跳转到B页面, 那么A,B页面的生命周期会是怎样的呢?

    B页面进入初始化状态,依次执行4个函数:构造函数 > initState > didChangeDependencies > Widget build , 此时页面加载完成,进入运行态。
    此时A页面依次执行deactivate > build函数。注意 此时A页面并未卸载。

    然后我们假设B页面只有一个按钮,点击B页面中的按钮,改变按钮的文字,会执行widget的build方法 ,(理论上也应该执行didUpdateWidget,但我这里没有)。

    这时,我们点击返回键从B页面返回到A页面。
    A页面重新显示,B页面开始卸载。
    那么A先执行deactivate > build , 然后B页面依次执行:deactivate > dispose 。
    此时A页面进入运行态,B页面移除。

    本次示例B页面代码:

    /*
     * Created by 李卓原 on 2018/9/13.
     * email: zhuoyuan93@gmail.com
     *
     */
    
    import 'package:flutter/material.dart';
    
    class NewsDetailPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => NewsDetailState();
    }
    
    class NewsDetailState extends State<NewsDetailPage> {
      int text = 1;
    
      NewsDetailState() {
        print('构造函数');
      }
    
      @override
      void initState() {
        print('init state');
        super.initState();
      }
    
      @override
      void didChangeDependencies() {
        print('didChangeDependencies');
        super.didChangeDependencies();
      }
    
      @override
      Widget build(BuildContext context) {
        print('widget build');
    
        return Scaffold(
          body: Center(
            child: _loading(),
          ),
          appBar: AppBar(
            title: Text('咨询详情'),
          ),
        );
      }
    
      @override
      void didUpdateWidget(NewsDetailPage oldWidget) {
        print('组件状态改变:didUpdateWidget');
        super.didUpdateWidget(oldWidget);
      }
    
      @override
      void deactivate() {
        print('移除时:deactivate');
        super.deactivate();
      }
    
      @override
      void dispose() {
        print('移除时:dispose');
        super.dispose();
      }
    
      //预加载布局
      Widget _loading() {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CircularProgressIndicator(
              strokeWidth: 1.0,
            ),
            Container(
              child: Text("正在加载"),
              margin: EdgeInsets.only(top: 10.0),
            )
          ],
        );
      }
    }

    Tips:
    下面内容来自咸鱼技术团队.

    当ListView中的item滚动出可显示区域的时候,item会被从树中remove掉,此item子树中所有的state都会被dispose,state记录的数据都会销毁,item滚动回可显示区域时,会重新创建全新的state、element、renderobject

    使用hot reload功能时,要特别注意state实例是没有重新创建的,如果该state中存在一下复杂的资源更新需要重新加载才能生效,那么需要在reassemble()添加处理,不然当你使用hot reload时候可能会出现一些意想不到的结果,例如,要将显示本地文件的内容到屏幕上,当你开发过程中,替换了文件中的内容,但是hot reload没有触发重新读取文件内容,页面显示还是原来的旧内容.

    idChangeDependencies有两种情况会被调用。
    创建时候在initState 之后被调用
    在依赖的InheritedWidget发生变化的时候会被调用
    正常的退出流程中会执行deactivate然后执行dispose。但是也会出现deactivate以后不执行dispose,直接加入树中的另一个节点的情况。
    这里的状态改变包括两种可能:1.通过setState内容改变 2.父节点的state状态改变,导致孩子节点的同步变化。
    App生命周期
    需要指出的是如果想要知道App的生命周期,那么需要通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。常用状态包含如下几个:

    一个实际场景中的例子:

    在不考虑suspending的情况下:从后台切入前台生命周期变化如下: AppLifecycleState.inactive->AppLifecycleState.resumed;

    从前台压后台生命周期变化如下: AppLifecycleState.inactive->AppLifecycleState.paused;

  • 相关阅读:
    HDU 3586 二分答案+树形DP判定
    POJ 3140 树形DP
    POJ 1741 树的点分治
    POJ 1655 求树的重心
    CF 219D 树形DP
    HDU 2196树形DP(2个方向)
    HDU 1520 树形DP入门
    POJ 1159 Palindrome(最长公共子序列)
    树状数组 区间更新 区间查询
    HDU 1556 BIT区间修改+单点查询(fread读入优化)
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/10130469.html
Copyright © 2011-2022 走看看