美文网首页
Flutter中State的生命周期

Flutter中State的生命周期

作者: BeeCaffe | 来源:发表于2021-04-30 15:34 被阅读0次

    Flutter中State的生命周期

    Flutter中State类(状态)一般与StatefulWidget配合使用,来实现Widget的更新。State负责维护StatefulWidget的状态,并保存其状态信息。当Widget的状态发生变化时,用户只需要调用setState()方法,Fultter引擎便会重新构建Widget树来更新UI。其中setState()方法更类似于一个消息机制,用于通知Flutter引擎Widget状态发生了变化,Flutter引擎收到通知后便会更新Widget树。

    由于State管理的StatefulWidget的状态信息,并负责了StateWidget控件的更新,因此了解其的生命周期对于Flutter开发具有重要意义。在某些应用场景下我们需要知道State和Widget何时被构建,何时被销毁以及何时被更新等。

    1. State简介

    State类的接口如以下代码所示,其中我们开发中最常用的为build()和setState()方法,分别用于构建Widget和通知Flutter引擎更新Widget树。State类持有了一个Widget对象,通过该对象来重构Widget树,同时持有一个Widget的一个上下文,来获取该Widget在Widget树中的位置。

    //State为一个模板虚类,必须继承才能够使用
    @optionalTypeArgs
    abstract class State<T extends StatefulWidget> with Diagnosticable {
      T? _widget;  //State的实例中会持有一个Widget对象
      StatefulElement? _element;  //持有StatefulElement对象,构建Element树
      BuildContext get context {  //持有context对象,实际上为StatefulElement
        return _element!;
      }
      
        Widget build(BuildContext context);  //构建Widget树
        
        void initState(){}  //初始化State
        
        void didUpdateWidget(covariant T oldWidget) { }  //更新Widget
        
        void reassemble() {}  //重新装配Widget,一般在热重载时调用
        
        void setState(VoidCallback fn) {}  //更新State,后面详细解释
        
        void deactivate() {}  //注销Widget,将Widget从树中移除,不释放资源。
        
        void dispose() {}  //销毁Widget,将Widget从树中真正移除,并释放资源
          
        void didChangeDependencies() {}  //更改依赖,State依赖发生变化
    }
    

    在开发中,我们一般只需要重载build()方法来定义子Widget,同时使用setState()方法来通知Flutter引擎更新UI。其中build()方法完全由用户定义的,这里我们给出setState()的主要代码来探究setState()方法的运行机制,代码如下:

      //setState()方法的主要代码,略去了调试信息
      @protected
      void setState(VoidCallback fn) {  //传入一个回调函数fn
        final dynamic result = fn() as dynamic;
        _element!.markNeedsBuild();    //将Element标记为需要重新build()
      }
      
      //owner为BuildOwner类型,同样略去调试信息。
      void markNeedsBuild() {
        owner!.scheduleBuildFor(this);  //传入该Element,并进入Flutter引擎的调度序列,等待重构
      }
    

    从setState()中的源码可以看出,传入的回调函数会被运行,但是不影响Flutter引擎的build()过程,换句话说,即使回调函数fn()为空,Flutter引擎也会去根据现有的值去更新UI。在代码层面以下两种书写方式是等价的。

    //书写方式1:先更新UI值,再调用setState()方法
    TextButton(
      child: Text('$_counter'),
      onPressed:(){
        ++_counter;  //先更新需要变动的UI值
        setState(() {});  //再调用setState()方法
      },
    );
    
    //书写方式2:在setState()方法体中更新UI值
    TextButton(
      child: Text('$_counter'),
      onPressed:(){
        setState(() {
            ++_counter;  //在setState()方法中更新UI值
        });  
      },
    );
    

    但是为了代码的可读性,即让程序员知道我们在什么地方刷新了UI,还是会将需要更新UI的值放在setState()的回调函数体中。

    2.State()的生命周期

    清楚地知道State()的生命周期对使用Flutter开发是很有意义的。举例来说,如果在某些情况下我们需要用到Widget中的参数来完成对State<Widget>的初始化操作,我们就可以在initState()方法中通过value=widget.initValue的方式来进行初始化。如果采用State<Widget>的构造函数来进行初始化的话,就会造成代码的冗余和可读性变差的问题。另外,了解State何时被构建何时被销毁,对程序开发也具有重要的意义。

    在上一节,我们以及较为详细的介绍了State类中的成员,这里给出一个表格来详细介绍State中方法的作用。

    方法 作用
    initState() 初始化State时调用,将State插入渲染树,只会调用一次
    didChangeDependencies() State依赖对象变化时调用,如改变语言和主题时会调用该方法。
    didUpdateWidget() 组件状态改变时调用,可能调用多次
    build() 构建Widget
    deactivate() 注销,将State移除渲染树,但是暂时不销毁
    dispose() 销毁,将State从内存中移除
    reassemble() 重组,当热面热加载时会被调用

    下面我们将以一个简单的页面跳转的代码来探究Flutter的生命周期。代码由两个页面构成,分别为StateDemoPage1(页面1)和StateDemoPage2(页面2),他们都只有一个TextButton控件,StateDemoPage1实现了到StateDemoPage2的一个简单跳转,StateDemoPage2实现了一个点击更换Button颜色的逻辑,通过这个简单跳转我们来观察State()的生命周期,代码如下:

      //页面1代码
      class StateDemoPage1 extends StatefulWidget{
    
      const StateDemoPage1({
        Key key
      });
    
      @override
      State<StatefulWidget> createState() {
        return new _StateDemoPage1();
      }
    }
    
    class _StateDemoPage1 extends State<StateDemoPage1>{
      @override
      Widget build(BuildContext context) {
        print("_StateDemoPage1: build(创建Widget,构建Widget树)");
        return Scaffold(
          body: Center(
            child: TextButton(
              child: Text('跳转到下个页面'),
              //点击后计数器自增
              onPressed:(){
                Navigator.push(context, MaterialPageRoute(builder: (context){
                  StateDemoPage2();
                }));
              },
              style: ButtonStyle(
                backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.blue)
              )
            ),
          ),
        );
      }
    
      @override
      void initState() {
        super.initState();
        print("_StateDemoPage1: initState(初始化State)");
      }
    
      @override
      void didUpdateWidget(covariant StateDemoPage1 oldWidget) {
        super.didUpdateWidget(oldWidget);
        print("_StateDemoPage1: didUpdateWidget(更新Widget)");
      }
    
      @override
      void deactivate() {
        super.deactivate();
        print("_StateDemoPage1: deactive(注销Widget,将Widget从树中移除,不释放资源。)");
      }
    
      @override
      void dispose() {
        super.dispose();
        print("_StateDemoPage1: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)");
      }
    
      @override
      void reassemble() {
        super.reassemble();
        print("_StateDemoPage1: reassemble(重组Widget)");
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("_StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)");
      }
    
      _StateDemoPage1(){
        print("_StateDemoPage1: constructor(更改依赖,State依赖发生变化)");
      }
    }
      
    //页面2代码
      class StateDemoPage2 extends StatefulWidget{
      @override
      State<StatefulWidget> createState() {
        return new _StateDemoPage2();
      }
    }
    
    class _StateDemoPage2 extends State<StateDemoPage2>{
      Color mBackGroundColor = Colors.redAccent;
      @override
      Widget build(BuildContext context) {
        print("_StateDemoPage2: build(创建Widget,构建Widget树)");
        return Scaffold(
          body: Center(
            child: TextButton(
                child: Text('这里是第二个页面',
                style: TextStyle(
                  color: Colors.white
                ),),
                onPressed: (){
                  setState(() {
                    mBackGroundColor=Colors.green;
                  });
                },
                style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.resolveWith((states) => mBackGroundColor)
                )
            ),
          ),
        );
      }
    
      @override
      void initState() {
        super.initState();
        print("_StateDemoPage2: initState(初始化State)");
      }
    
      @override
      void didUpdateWidget(covariant StateDemoPage2 oldWidget) {
        super.didUpdateWidget(oldWidget);
        print("_StateDemoPage2: didUpdateWidget(更新Widget)");
      }
    
      @override
      void deactivate() {
        super.deactivate();
        print("_StateDemoPage2: deactive(注销Widget,将Widget从树中移除,不释放资源。)");
      }
    
      @override
      void dispose() {
        super.dispose();
        print("_StateDemoPage2: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)");
      }
    
      @override
      void reassemble() {
        super.reassemble();
        print("_StateDemoPage2: reassemble(重组Widget)");
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("_StateDemoPage2: didChangeDependencies(更改依赖,State依赖发生变化)");
      }
    
      _StateDemoPage2(){
        print("_StateDemoPage2: constructor(构造函数)");
      }
    }
    

    step1: 点击开始运行

    页面1展示

    页面1中State的初始化过程: 当运行页面1时,依次运行了_StateDemoPage1的:构造函数->initState()->didChangeDependencies()->build()。

    _StateDemoPage1: constructor(构造函数)
    _StateDemoPage1: initState(初始化State)
    _StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)
    _StateDemoPage1: build(创建Widget,构建Widget树)
    

    页面1中State的热重载过程: 当我们对页面1进行热重载的时候,运行了_StateDemoPage1的:reassemble()->didUpdateWidget()->build()。

    _StateDemoPage1: constructor(构造函数)
    _StateDemoPage1: initState(初始化State)
    _StateDemoPage1: didChangeDependencies(更改依赖,State依赖发生变化)
    _StateDemoPage1: build(创建Widget,构建Widget树)
    _StateDemoPage1: reassemble(重组Widget)
    _StateDemoPage1: didUpdateWidget(更新Widget)
    _StateDemoPage1: build(创建Widget,构建Widget树)
    

    step2:在点击页面1中的跳转按钮后,跳转到页面2

    页面2展示

    页面2初始化过程:此时,页面2进入了初始化过程,其构建过程与页面1相同。而页面1为不可见状态,但是被没有进入销毁过程。终端输出如下:

    _StateDemoPage2: constructor(构造函数)
    _StateDemoPage2: initState(初始化State)
    _StateDemoPage2: didChangeDependencies(更改依赖,State依赖发生变化)
    _StateDemoPage2: build(创建Widget,构建Widget树)
    

    step3:此时点击页面2,更换到绿色按钮

    image

    页面2的更新过程: 此时,调用了build()方法来更新了UI。

    _StateDemoPage2: build(创建Widget,构建Widget树)
    

    step4: TopBar上的点击返回

    页面2的销毁过程: 此时,我们进入了销毁过程,依次调用了_StateDemoPage2的deactive()->dispose()方法来完成页面2的销毁。

    _StateDemoPage2: deactive(注销Widget,将Widget从树中移除,不释放资源。)
    _StateDemoPage2: dispose(销毁Widget,将Widget从树中真正移除,并释放资源)
    

    StatefulWidget的生命周期如下图所示,我们可以大致分为3个阶段:

    • 初始化过程: constructor->initState()->didChangeDependencies()->build()
    • 更新过程: reassemble()->didUpdateWidget()
    • 销毁过程: deactive()->dispose()
    State的生命周期

    欢迎关注

    猫枣编程

    参考文献

    [1] https://book.flutterchina.club/chapter3/

    [2] https://blog.csdn.net/u011272795/article/details/82695920

    相关文章

      网友评论

          本文标题:Flutter中State的生命周期

          本文链接:https://www.haomeiwen.com/subject/zdwnrltx.html