Flutter中State的生命周期

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的初始化操作,我们就可以在initState()方法中通过value=widget.initValue的方式来进行初始化。如果采用State的构造函数来进行初始化的话,就会造成代码的冗余和可读性变差的问题。另外,了解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,更换到绿色按钮

页面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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值