Flutter学习-有状态的StatefulWidget_GY-93的博客-程序员秘密

技术标签: flutter  Flutter  

1. StatefulWidget

在开发中,某些Widget情况下我们展示的数据并不是一层不变的:
比如Flutter默认程序中的计数器案例,点击了+号按钮后,显示的数字需要+1;
比如在开发中,我们会进行下拉刷新、上拉加载更多,这时数据也会发生变化;
而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget;

1.1 StatefulWidget的介绍

如果你有阅读过默认我们创建Flutter的示例程序,那么你会发现它创建的是一个StatefulWidget

  • 为什么选择StatefulWidget呢?

    • 因为在示例代码中,当我们点击按钮时,界面上显示的数据会发生改变;
    • 这时,我们需要一个变量来记录当前的状态,再把这个变量显示到某个Text Widget上
    • 并且每次变量发生改变时,我们对应的Text上显示的内容也要发生改变;
  • 但是有一个问题,我之前说过定义到Widget中的数据都是不可变的,必须定义为final,为什么呢?

    • 这次因为Flutter在设计的时候就决定了一旦Widget中展示的数据发生变化,就重新构建整个Widget;
    • 下一个章节我会讲解Flutter的渲染原理,Flutter通过一些机制来限定定义到Widget中的成员变量必须是final的;

Flutter如何做到我们在开发中定义到Widget中的数据一定是final的呢?
我们来看下源码:

@immutable
abstract class Widget extends DiagnosticableTree {
    
	// ...省略代码
}

在这里插入图片描述

  • 结论定义到Widget中的数据一定是不可变的,需要使用final来修饰

1.2 如何存储Widget状态?

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

  • StatelessWidget无所谓,因为它里面的数据通常是直接定义玩后就不修改的。
  • 但StatefulWidget需要有状态(可以理解成变量)的改变,这如何做到呢?
  • Flutter将StatefulWidget设计成了两个类:
    • 也就是你创建StatefulWidget时必须创建两个类
    • 一个类继承自StatefulWidget,作为Widget树的一部分;
    • 一个类继承自State,用于记录StatefulWidget会变化的状态,并且根据状态的变化,构建出新的Widget;

创建一个StatefulWidget,我们通常会按照如下格式来做:

  • 当Flutter在构建Widget Tree时,会获取State的实例,并且它调用build方法去获取StatefulWidget希望构建的Widget;
  • 那么,我们就可以将需要保存的状态保存在MyState中,因为它是可变的;
class MyTestfulWidget extends StatefulWidget {
    
  const MyTestfulWidget({
    Key? key}) : super(key: key);

  @override
  _MyTestfulWidgetState createState() => _MyTestfulWidgetState();
}

class _MyTestfulWidgetState extends State<MyTestfulWidget> {
    
  @override
  Widget build(BuildContext context) {
    
    return Container();
  }
}

思考:为什么Flutter要这样设计呢?
这是因为在Flutter中,只要数据改变了Widget就需要重新构建(rebuild)

2. StatefulWidget案例

我们通过一个案例来练习一下StatefulWidget,还是之前的计数器案例,但是我们按照自己的方式进行一些改进。
在这里插入图片描述

  • 因为当点击按钮时,数字会发生变化,所以我们需要使用一个StatefulWidget,所以我们需要创建两个类;
  • GYHomeContentBody继承自StatefulWidget,里面需要实现createState方法;
  • _GYHomeContentBodyState继承自State,里面实现build方法,并且可以定义一些成员变量
  • 示例代码:
import 'package:flutter/material.dart';//导入对应的库

//main函数作为程序的入口
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: Text(
                "计数器"
            ),
          ),
          body: GYHomeContent()
        )
    );
  }
}

class GYHomeContent extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    
    // TODO: implement build
    return GYHomeContentBody();
  }
}
// Widget是不加_: 暴露给别人使用
class GYHomeContentBody extends StatefulWidget {
    
  const GYHomeContentBody({
    Key? key}) : super(key: key);

  @override
  _GYHomeContentBodyState createState() => _GYHomeContentBodyState();
}

/**
 * 为什么Flutter在设计的时候StatefulWidget的build方法放在State中
 *  1.build出来的Widget是需要依赖State中的变量(状态/数据)
 *  2.在Flutter的运行过程中:
 *    Widget是不断的销毁和创建的
 *    当我们自己的状态发生改变时, 并不希望重新状态一个新的State
 */

// State是加_: 状态这个类只是给Widget使用
class _GYHomeContentBodyState extends State<GYHomeContentBody> {
    
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RaisedButton(
                  onPressed: () {
    
                    setState(() {
    
                      _count++;
                    });
                  },
                  child: Text("加一"),
                  color: Colors.pink),
              RaisedButton(
                  onPressed: () {
    
                    setState(() {
    
                      _count--;
                    });
                  },
                  child: Text("减一"),
                  color: Colors.orange)
            ],
          ),
          Text("当前计数: $_count", style: TextStyle(fontSize: 24))
        ],
      ),
    );
  }
}

  • 按钮点击状态改变:
    我们现在要监听状态的改变,当状态改变时要修改_count变量:
    • 但是,直接修改变量可以改变界面吗?不可以。
    • 这是因为Flutter并不知道我们的数据发生了改变,需要来重新构建我们界面中的Widget

如何可以让Flutter知道我们的状态发生改变了,重新构建我们的Widget呢?

  • 我们需要调用一个State中默认给我们提供的setState方法
  • 可以在其中的回调函数中修改我们的变量;
onPressed: () {
    
  setState(() {
    
      _count++;
  });
}
  • StatefulWidget的传递值
    • 如果我们传递一个数据,到StatefulWidget对象,那我们在State类中如何获取这个值
      • 第一种:把值在传递一次
      • 第二种:State类中有一个属性widget表示是和当前状态类绑定的Widget对象,这是Flutter帮我们绑定的 ,我们可以通过这个对象拿到当前Widget中的值
class GYHomeContent extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    
    // TODO: implement build
    return GYHomeContentBody("您好");
  }
}
// Widget是不加_: 暴露给别人使用
class GYHomeContentBody extends StatefulWidget {
    
  final String message;
  GYHomeContentBody(this.message);

  @override
  _GYHomeContentBodyState createState() => _GYHomeContentBodyState();
}
// State是加_: 状态这个类只是给Widget使用
class _GYHomeContentBodyState extends State<GYHomeContentBody> {
    
  
  @override
  Widget build(BuildContext context) {
    
    print("message=======${this.widget.message}");
    return Column();
  }
}

3. StatefulWidget生命周期

3.1. 生命周期的理解

什么是生命周期呢?

  • 客户端开发:iOS开发中我们需要知道UIViewController从创建到销毁的整个过程,Android开发中我们需要知道Activity从创建到销毁的整个过程。以便在不同的生命周期方法中完成不同的操作;
  • 前端开发中:Vue、React开发中组件也都有自己的生命周期,在不同的生命周期中我们可以做不同的操作

Flutter小部件的生命周期:

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

3.2 生命周期简单版

那么StatefulWidget有哪些生命周期的回调呢?它们分别在什么情况下执行呢?

  • 在下图中,灰色部分的内容是Flutter内部操作的,我们并不需要手动去设置它们
  • 白色部分表示我们可以去监听到或者可以手动调用的方法;

我们知道StatefulWidget本身由两个类组成的:StatefulWidgetState,我们分开进行分析

在这里插入图片描述

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

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

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

  1. 执行State类的构造方法(Constructor)来创建State对象;
  2. 执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求;
    • 注意:这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
    • 并且如果你阅读源码,你会发现这里有一个注解(annotation):@mustCallSuper
@protected
  @mustCallSuper
  void initState() {
    
    assert(_debugLifecycleState == _StateLifecycle.created);
  }
  1. 执行didChangeDependencies方法,这个方法在两种情况下会调用
    • 情况一:调用initState会调用;
    • 情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
  2. Flutter执行build方法,来看一下我们当前的Widget需要渲染哪些Widget;
  3. 当前的Widget不再使用时,会调用dispose进行销毁
  4. 手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets;
  5. 执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会调用didUpdateWidget方法;
import 'package:flutter/material.dart';//导入对应的库

//main函数作为程序的入口
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text(
                  "计数器"
              ),
            ),
            body: GYHomeContent()
        )
    );
  }
}

class GYHomeContent extends StatelessWidget {
    
  @override
  Widget build(BuildContext context) {
    
    // TODO: implement build
    return GYHomeContentBody();
  }
}
// Widget是不加_: 暴露给别人使用
class GYHomeContentBody extends StatefulWidget {
    
  GYHomeContentBody() {
    
    print("GYHomeContentBody=======初始化方法");
  }

  @override
  _GYHomeContentBodyState createState() {
    
    print("GYHomeContentBody=======createState");
     return _GYHomeContentBodyState();
  }
}
// State是加_: 状态这个类只是给Widget使用
class _GYHomeContentBodyState extends State<GYHomeContentBody> {
    
  _GYHomeContentBodyState() {
    
    print("_GYHomeContentBodyState======初始化方法");
  }

  @override
  void initState() {
    
    print("_GYHomeContentBodyState========initState");
    super.initState();
  }

  @override
  void didUpdateWidget(GYHomeContentBody oldWidget) {
    
    super.didUpdateWidget(oldWidget);
    print("_GYHomeContentBodyState=========didUpdateWidget");
  }

  @override
  void didChangeDependencies() {
    
    super.didChangeDependencies();
    print("HYHomeContentState=========didChangeDependencies方法");
  }


  int _count = 0;

  @override
  Widget build(BuildContext context) {
    
    print("_GYHomeContentBodyState========build方法");
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          RaisedButton(
              onPressed: () {
    
                print("点击按钮事件=========");
                setState(() {
    
                  _count++;
                });
              },
              child: Text("加一"),
              color: Colors.pink),
          Text("当前计数: $_count", style: TextStyle(fontSize: 24))
        ],
      ),
    );
  }
}

运行后的结果:
在这里插入图片描述

3.3 生命周期复杂版

我们来学习几个前面生命周期图中提到的属性,但是没有详细讲解的

  • mountedState内部设置的一个属性,事实上我们不了解它也可以,但是如果你想深入了解它,会对State的机制理解更加清晰;
    • 很多资料没有提到这个属性,但是我这里把它列出来,是内部设置的,不需要我们手动进行修改;

在这里插入图片描述

  • dirty state的含义是脏的State

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

    • 它表示当前build出来的Widget,下一次重绘检查时不需要重新build
  • setState()方法的作用是,判断dirty标记,如果该标记是true则直接返回,如果标记是false,则把标记设置为true,dirty=true
    在这里插入图片描述
    在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yong_19930826/article/details/118708378

智能推荐

OpenJDK 正式宣布AWT、2D、Swing等项目解散_程序猿DD_的博客-程序员秘密

7月15日OpenJDK管理委员会全票通过批准成立由Phil Race担任初始负责人的Client Libraries Group(客户端类库工作组)。新的工作组将继续赞助OpenJFX...

android studio导入support -v4 包的方法_android导入supper_瑺圊樹的博客-程序员秘密

1.进入 file-project structure2.左边选择app3.右边选择dependencies4.左下角可以看到一个加号,点击选择Library dependency就可以看到可以导入的jar包,选择support-v4导入即可

[Android]只显示月和日的DatePickerDialog_weixin_34364071的博客-程序员秘密

 前言  需求要只显示月和日的日历控件,又不想自定义控件,最简单的办法就是隐藏显示年的这个框了,但DatePickerDialog并没有直接提供方法来操作,这里分享一个笨办法:) 声明  欢迎转载,但请保留文章原始出处:)     博客园:http://www.cnblogs.com    农民伯伯: http://over140.cnblogs.com    正文  一、效果图    1.1 默...

利用Oracle物化视图日志订阅增量_不会画画的画师的博客-程序员秘密

物化视图的快速刷新需要先构造物化视图日志,而物化视图日志中会记录表的dml操作,因此可以通过物化视图日志订阅Oracle增量。1.物化视图日志名物化视图日志名为MLOG$_表名。当表名长度超过20时,只取前20位;当出现截短后名称重复时,会自动在物化视图日志名后面添加数字。2.物化视图日志结构基本字段:SNAPTIME$$:数据刷新的时间;当该物化视图日志仅供一张物化视图使用时,为400...

eps倾斜摄影矢量化采集毕业设计_eps倾斜摄影矢量化dlg采集可打印.doc_weixin_39890452的博客-程序员秘密

一、背景1、倾斜摄影测量倾斜摄影测量和我们传统影像的区别:从数据采集的方式来看,传统影像是通过飞机上搭载的航摄仪对地面连续摄取相片,而后经过一系列的内业处理得到的影像数据,获取的成果只有地物俯视角度信息,也就是视角垂直于地面。而倾斜摄影测量测试通过飞机或无人机搭载5个相机从前、后、左、右、垂直五个方向对地物进行拍摄,再通过内业的几何校正、平差、多视影像匹配等一系列的处理得到的具有地物全方位信息的数...

open, read, write/fopen,fread,fwrite区别_有比fwrite速度快的函数吗_麦子_hsx的博客-程序员秘密

UNIX环境下的C 对二进制流文件的读写有两套班子:1) fopen,fread,fwrite ; 2) open, read, write在介绍之前先简单的说一下缓冲区和非缓冲区:1.缓冲文件系统缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执

随便推点

Android应用开发-小巫程序员秘密client之显示博文具体内容_weixin_33691817的博客-程序员秘密

Android应用开发-小巫程序员秘密客户端之显示博文具体内容 上篇博文给大家介绍的是怎样嵌入有米广告而且获取收益,本篇博客打算讲讲关于怎样在一个ListView里显示博文的具体信息。这个可能是童鞋们比較困惑的,由于一篇博客可能有标题、摘要、图片、代码等等元素组成,我们要怎么在一个界面中显示这些内容而且依照自己的指定的方式显示呢,别急,以下会告诉大家。   又一次整理一下一篇博文可能有以下元...

java项目tkmybatis整合_spring-boot如何最精简整合tkmybatis_海v尔v前v员工的博客-程序员秘密

tkmybatis最精简整合,最精简整合指的是手动导入最精简的自动配置类ImportTkMybatis.java代码如下package com.litong.spring.boot.mybatis.tk;import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;import org.springfr...

正则 RegExp_曹政鑫婕_曹政鑫婕的博客-程序员秘密

正则 RegExp1. 创建正则表达式1.1 通过构造方法创建var reg = new RegExp(/today/);1.2 字面量的形式创建var reg = /tommorw/;2. 字符类2.1 边界符正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符–精确匹配边界符说明^表示匹配行首的文本(以谁开始)$表示匹配行尾的文本(以谁结束)2.2 括号元字符说明{}大括号量词符. 里面表示重复次数

java.lang.UnsatisfiedLinkError: /mnt/jdk1.8/jre/lib/amd64/libawt_xawt.so: libXrender.so.1_能量守恒洛的博客-程序员秘密

AWT is not properly configured on this server. Perhaps you need to run your container with "-Djava.awt.headless=true"? See also: https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+got+java.awt.headl...

StaggeredGridLayoutManager瀑布流错乱和顶部空白问题解决_staggeredgridlayoutmanager 上面空_春暖花开_love的博客-程序员秘密

 参考: https://blog.csdn.net/maplelove1211/article/details/78394852 https://blog.csdn.net/chen_lian_/article/details/80239028  闪烁问题https://blog.csdn.net/lf0814/article/details/52884569...

房地产库存两极分化态势明显_Shawn.Hu的博客-程序员秘密

http://news.ifeng.com/a/20160810/49750364_0.shtml3日,发改委官网刊文称更好发挥投资对经济增长的作用,建议未来因城施策加大调控力度,进一步去库存;鼓励先租后售、与政府共有产权等方式促进居民购房,鼓励品牌房地产开发企业收购现有地产项目。上海易居房地产研究院最新报告显示,上半年库存削减态势明显,保持了连续10个月库存同比下滑的态势。但值得注意

推荐文章

热门文章

相关标签