StatefulWidget 基础知识

本篇主要认识StatefulWidget、了解其状态管理,简单使用,生命周期等

1. 初始StatefulWidget

@immutable

看源码,StatefulWidget也是继承自Widget,其上有个注解:@immutable,该注解的意思就是不可变的意思,作用就是widget及其子类是不可变的,定义的变量必须是final的,所以,这就无法在StatelessWidget内实现数据更新操作。

官方@immutable解释

1.1. StatefulWidget 状态管理

StatefulWidget是相对于StatelessWidget实现可变的widget,StatefulWidget也是继承于widget,所以,自然在widget内是做不了变量更新的

既然Widget是不可变,那么StatefulWidget如何来存储可变的状态呢?

  • StatelessWidget无所谓,因为它里面的数据通常是直接定义完后就不修改的。
  • 但StatefulWidget需要有状态(可以理解成变量)的改变,这如何做到呢?

Flutter将StatefulWidget设计成了两个类:

  • 也就是你创建StatefulWidget时必须创建两个类:
  • 一个类继承自StatefulWidget,作为Widget树的一部分;
  • 一个类继承自State,用于记录StatefulWidget会变化的状态,并且根据状态的变化,构建出新的Widget;

基本结构如下:

class MyStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // 将创建的State返回
    return MyState();
  }
}

class MyState extends State<MyStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return <构建自己的Widget>;
  }
}

2. StatefulWidget简单使用

实现效果:

StatefulWidget_demo1.png

代码:

class HomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("HomeBody build");
    return MyCounterWidget();
  }
}

class MyCounterWidget extends StatefulWidget {

  MyCounterWidget() {
    print("执行了MyCounterWidget的构造方法");
  }

  @override
  State<StatefulWidget> createState() {
    print("执行了MyCounterWidget的createState方法");
    // 将创建的State返回
    return MyCounterState();
  }
}

class MyCounterState extends State<MyCounterWidget> {
  int counter = 0;

  MyCounterState() {
    print("执行MyCounterState的构造方法");
  }

  @override
  void initState() {
    super.initState();
    print("执行MyCounterState的init方法");
  }

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print("执行MyCounterState的didChangeDependencies方法");
  }

  @override
  Widget build(BuildContext context) {
    print("执行执行MyCounterState的build方法");
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                color: Colors.redAccent,
                child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {
                  setState(() {
                    counter++;
                  });
                },
              ),
              RaisedButton(
                color: Colors.orangeAccent,
                child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {
                  setState(() {
                    counter--;
                  });
                },
              )
            ],
          ),
          Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
        ],
      ),
    );
  }

  @override
  void didUpdateWidget(MyCounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("执行MyCounterState的didUpdateWidget方法");
  }

  @override
  void dispose() {
    super.dispose();
    print("执行MyCounterState的dispose方法");
  }
}

分析

  • 自定义的State做状态管理,关于state不解释,前端coder肯定懂,我主程是客户端开发
  • setState做状态更新:更新完会触发widget重新build
  • 记住flutter自定义可变widget的套路:
    • 自定义widget:继承StatefulWidget,重载build加载自定义的state
    • 自定义state:继承State,实现你真正的ui部分和状态(data)管理
    • 快捷代码模块:stless、stful 就会自动不全代码

3. 生命周期

不多解释,既然是个对象,就有生命流程,生命周期也是ui开发数据管理的基本着力点。

  • StatelessWidget可以由父Widget直接传入值,调用build方法来构建,整个过程非常简单;
  • 而StatefulWidget需要通过State来管理其数据,并且还要监控状态的改变决定是否重新build整个Widget;
  • 所以,我们主要讨论StatefulWidget的生命周期,也就是它从创建到销毁的整个过程;

过程

首先,执行StatefulWidget中相关的方法:

  • 1、执行StatefulWidget的构造函数(Constructor)来创建出StatefulWidget;
  • 2、执行StatefulWidget的createState方法,来创建一个维护StatefulWidget的State对象;

其次,调用createState创建State对象时,执行State类的相关方法:

  • 1、执行State类的构造方法(Constructor)来创建State对象;

  • 2、执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求;

    • 注意:这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
    • 并且如果你阅读源码,你会发现这里有一个注解(annotation):@mustCallSuper
  • 3、执行didChangeDependencies方法,这个方法在两种情况下会调用

    • 情况一:调用initState会调用;
    • 情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
  • 4、Flutter执行build方法,来看一下我们当前的Widget需要渲染哪些Widget;

  • 5、当前的Widget不再使用时,会调用dispose进行销毁;

  • 6、手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets;

  • 7、执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会调用didUpdateWidget方法;

上面的代码直接运行,打印如下:

flutter: HomeBottomBar 构造函数
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行了MyCounterWidget的createState方法
flutter: 执行MyCounterState的构造方法
flutter: 执行MyCounterState的init方法
flutter: 执行MyCounterState的didChangeDependencies方法
flutter: 执行执行MyCounterState的build方法
flutter: HomeBottomBar 构造函数

// 注意:Flutter会build所有的组件两次,如果你是用as开发的话,会有这个问题,但是vscode是正常的,或者flutter run 方法启动,也是只调用一遍的,所以,不用太在意,可以理解为是as的bug,运行到手机上肯定是正常的
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行MyCounterState的didUpdateWidget方法
flutter: 执行执行MyCounterState的build方法

//当我们改变状态,手动执行setState方法后会打印如下结果:
flutter: 执行执行MyCounterState的build方法

3.1. 深入分析

mounted

/// Whether this [State] object is currently in a tree.
  ///
  /// After creating a [State] object and before calling [initState], the
  /// framework "mounts" the [State] object by associating it with a
  /// [BuildContext]. The [State] object remains mounted until the framework
  /// calls [dispose], after which time the framework will never ask the [State]
  /// object to [build] again.
  ///
  /// It is an error to call [setState] unless [mounted] is true.
  bool get mounted => _element != null;

1、第一句:用于判断state是否在widget树中,也就是判断widget是否加载

2、最后一句:widget加载之前,是不能调用setState的。

3、其他,表示state挂载原理和生命周期

所以,开发中,一般开发中,更新状态的时候,会有widget是否加载的判断,以免报错:

if(mounted){
    setState((){   
       
    }) 
}

dirty state & clean state

dirty state

  • 它实际是通过一个Element的东西的属性来标记的;
  • 将它标记为dirty会等待下一次的重绘检查,强制调用build方法来构建我们的Widget;

clean state的含义是干净的State

  • 它表示当前build出来的Widget,下一次重绘检查时不需要重新build;

所以,流程就是,当setState调用的时候,_element!.markNeedsBuild();将widget标记为需要build将dirty=true,然后调用scheduleBuildFor将widget加入dirty的widget列表等待rebuild,然后触发更新去rebuild,完后,将widget的dirty=false,这时候state就是clean干净的了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容