Flutter:StatefulWidget 的状态管理与生命周期

原创:有趣知识点摸索型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、StatefulWidget 的状态管理
  • 二、Flutter 默认 Demo 对 StatefulWidget 的使用
  • 三、StatefulWidget 的生命周期

一、StatefulWidget 的状态管理

StatefulWidgetStatelessWidget 均继承自 Widget

abstract class StatefulWidget extends Widget
abstract class StatelessWidget extends Widget

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

@immutable
abstract class Widget extends DiagnosticableTree

StatefulWidget 是相对于 StatelessWidget 实现可变的 widget。由于 StatefulWidget 也是继承于 widget,所以自然在 widget 内是做不了变量更新的。既然 Widget 是不可变,那么 StatefulWidget 如何来存储可变的状态呢?

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

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

基本结构如下:

class MyStatefulWidget extends StatefulWidget {
  @override
  State< MyStatefulWidget > createState() {
    // 将创建的State返回
    return MyState();
  }
}
class MyState extends State<MyStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return <构建自己的Widget>;
  }
}

二、Flutter 默认 Demo 对 StatefulWidget 的使用

我们来看看系统为我们默认生成的页面中对 StatefulWidget 的使用。

这里有一个属性 title 表示的是页面的标题,其值由创建 MyHomePage 处赋予,在 Statebuild 方法中进行使用。像这样的属性,我们总是将其标记为 finalconst MyHomePage({ })表示其初始方法,需要传入父类的属性和当前类中自定义的属性。

class MyHomePage extends StatefulWidget {
  final String title;
  const MyHomePage({super.key, required this.title});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

而其对应的继承自 State,用于记录 StatefulWidget 变化状态的类如下:

class _MyHomePageState extends State<MyHomePage> {

}

在其中我们新增加了一个变量 _counter,其初值为0,每当点击 + 按钮调用一次 _incrementCounter 方法就会让其值增加1。由于其值的改变是在 setState 中进行的,而当在 setState 中的值发生改变的时候,就会告诉 Flutter 框架当前状态发生了改变,这将导致它重新运行下面的构建方法 build。如果我们改变 _counter 却不调用 setState(),那么构建方法 build 将不会被再次调用,看起来什么都不会发生。

int _counter = 0;

void _incrementCounter() {
  setState(() {
    _counter++;
  });
}

每次调用 setState 时都会重新运行下面这个方法。此处我们从 MyHomePage 对象中获取值,并使用它来设置我们的应用栏标题。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$_counter',
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: 'Increment',
      child: const Icon(Icons.add),
    ),
  );
}

MyHomePage 对象是在 MyAppbuild 方法中进行创建的。MyApp这个组件是我们应用程序的根组件。

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

在入口方法 main 方法中,系统调用了 runApp 方法,将 MyApp对象作为参数进入了传入。

void main() {
  runApp(const MyApp());
}

总结 Flutter 自定义 StatefulWidget 的套路:

  • 快捷代码模块:输入stlessstful 系统就会自动补全代码
  • 自定义 Widget:继承自 StatefulWidget,重载 createState 方法来加载自定义的 state
  • 自定义 State:继承自 State,实现你真正的 UI 部分和状态(Data)管理。自定义的 State 做状态管理,而 setState 做状态更新(更新完会触发 Widget 重新进行 build)。

三、StatefulWidget 的生命周期

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

1、StatefulWidget 类

class MyCounterWidget extends StatefulWidget {

}

执行 StatefulWidget 的构造函数(Constructor)来创建出 StatefulWidget

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

执行 StatefulWidgetcreateState 方法,来创建一个维护 StatefulWidgetState 对象

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

2、State 类

class MyCounterState extends State<MyCounterWidget> {

}

执行 State 类的构造方法(Constructor)来创建 State 对象。

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

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

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

注意这个方法是重写父类的方法,必须调用 super,因为父类中会进行一些其他操作。并且如果你阅读源码,你会发现这里有一个注解(annotation):@mustCallSuper

@protected
@mustCallSuper
void initState() {
  assert(_debugLifecycleState == _StateLifecycle.created);
}

执行 didChangeDependencies 方法,这个方法在两种情况下会调用。

  • 情况一:调用 initState 方法时会被调用
  • 情况二:从其他对象中依赖一些数据发生改变时,比如 InheritedWidget
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  print("MyCounterState:执行 didChangeDependencies 方法");
}

Flutter 执行 build 方法,来看一下我们当前的 Widget 需要渲染哪些 Widget

@override
Widget build(BuildContext context) {
    print("MyCounterState:执行 build 方法");
    return Center( )
}

当前的 Widget 不再使用时,会调用 dispose 进行销毁。

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

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

setState(() {
  _counter++;
});

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

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

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

flutter: MyApp:build
flutter: MyCounterWidget:执行了构造方法
flutter: MyCounterWidget:执行了 createState 方法

flutter: MyCounterState:执行构造方法
flutter: MyCounterState:执行 init 方法
flutter: MyCounterState:执行 didChangeDependencies 方法
flutter: MyCounterState:执行 build 方法

注意这里 Flutterbuild 所有的组件两次。如果你是用 Android Studio 开发的话,会有这个问题,但是如果使用 VSCode 进行开发则是正常的,即只会调用一遍的,所以不用太在意,可以理解为是 Android StudioBug,其运行到手机上肯定是正常的。

flutter: MyApp:build
flutter: MyCounterWidget:执行了构造方法
flutter: MyCounterState:执行 didUpdateWidget 方法
flutter: MyCounterState:执行 build 方法

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

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

推荐阅读更多精彩内容