Flutter有状态组件StatefulWidget生命周期详解

目录
  • 1、StatefulWidget的背后
  • 2、StatefulWidget的生命周期
    • 2.1创建阶段
    • 2.2更新阶段
    • 2.3销毁阶段
  • 总结:

1、StatefulWidget的背后

flutter开发过程中,我们经常会用到两个组件StatelessWidget和StatefulWidget。前者为无状组件,后者为有状态组件,无状态组件通常在创建后内部的数据无法改变,而有状态组件可以维持内部的数据状态,适合动态组件使用。

/// 无状态组件的定义
class MyApp extends StatelessWidget {}
/// 有状态状态组件的定义
class MyApp extends StatefullWidget {
  @override
  State<StatefulWidget> createState() => _MyApp();
}
class _MyApp extends State<MyApp> {}

定义无状态组件相对简单,只需要继承StatelessWidget即可,而有状态组件需要两个类来实现,首先是继承StatefullWidget,然后重写createState方法,返回State的实现类。

刚始我不明白StatefullWidget为何要通过重写createState的方式来实现,后来通过对StatelessWidget的深入,我渐渐的理解了其中的用意。

首先,StatelessWidget和StatefulWidget都的父类来自Widget,而Widget在定义过程中使用了dart的注解@immutable。

@immutable的作用根据官方的解释:被@immutable注解标明的类或者子类都必须是不可变的。

也就是说,继承了StatelessWidget和StatefullWidget的组件都必须为常量组件,可以使用const修饰,而它的构造函数和成员属性需要是常量,不可变的。

class MyApp extends StatelessWidget {
  /// 常量属性,不加final会警告
  final String? title;
  const MyApp({Key? key, this.title}) : super(key: key);
}
/// or
class MyApp extends StatefulWidget {
  /// 常量属性,不加final会警告
  final String? title;
  const MyApp({Key? key, this.title}) : super(key: key);
  @override
  State<StatefulWidget> createState() => _MyApp();
}
/// 调用时可以加const
const MyApp()

StatelessWidget自然没什么问题,本身它就是无状态组件,创建出来内部数据不会改变,符合@immutable的定义。而StatefullWidget不同,作为有状态组件,需要维持自身的成员属性可变,不能是一个常量。那如何解决呢?就是通过State来保持状态,因为它并不继承Widget,不受@immutable的影响,内部成员可以定义成变量。

还有个问题,为什么Widget非要使用@immutable来注释?

这一切的出发点都是为了减少性能的损耗,提高Widget构建效率。因为常量定义的类,不会重复构建,可以大大提升运行速度,只要明白这一点,就能解了StatefulWidget这样设计的意义。

2、StatefulWidget的生命周期

StatefulWidget通过State来管理状态,同时也提供了相应的生命周期函数,如initState、didUpdateWidget、dispose等,我们只需要重写这些函数,StatefulWidget在执行过程中会在合适的时机调用。

StatefulWidget的生命周期可以分为创建阶段、更新阶段和销毁阶段,下面我们结合一个示例来看下它的执行过程。

/// StatefulWidget demo
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class ColorInheritedWidget extends InheritedWidget {
  final Color color;
  const ColorInheritedWidget({
    Key? key,
    required this.color,
    required Widget child,
  }) : super(key: key, child: child);
  @override
  bool updateShouldNotify(covariant ColorInheritedWidget oldWidget) {
    return oldWidget.color != color;
  }
  static ColorInheritedWidget? of(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<ColorInheritedWidget>();
}
class MyApp extends StatefulWidget {
  final String? title;
  const MyApp({Key? key, this.title}) : super(key: key);
  @override
  State<StatefulWidget> createState() {
    print('createState');
    return _MyApp();
  }
}
class _MyApp extends State<MyApp> {
  int value = 0;
  bool isDetach = false;
  Color color = Colors.red;
  @override
  void initState() {
    super.initState();
    print('initState');
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies');
  }
  @override
  void didUpdateWidget(covariant MyApp oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget');
  }
  @override
  void deactivate() {
    super.deactivate();
    print('deactivate');
  }
  @override
  void dispose() {
    super.dispose();
    print('deactivate');
  }
  @override
  Widget build(BuildContext context) {
    print('build');
    return ColorInheritedWidget(
      color: color,
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('StatefulWidget demo'),
          ),
          body: Column(
            children: [
              if (!isDetach) MyAppChild(value: value),
              MaterialButton(
                color: Colors.blue,
                onPressed: () {
                  value++;
                  setState(() {});
                },
                child: const Text('更新value'),
              ),
              MaterialButton(
                color: Colors.blue,
                onPressed: () {
                  color = color == Colors.red ? Colors.yellow : Colors.red;
                  setState(() {});
                },
                child: const Text('更新color'),
              ),
              MaterialButton(
                color: Colors.blue,
                onPressed: () {
                  isDetach = !isDetach;
                  setState(() {});
                },
                child: Text(isDetach ? '添加' : '移除'),
              )
            ],
          ),
        ),
      ),
    );
  }
}
class MyAppChild extends StatefulWidget {
  final int value;
  const MyAppChild({Key? key, required this.value}) : super(key: key);
  @override
  State<StatefulWidget> createState() {
    print('child-createState');
    return _MyAppChild();
  }
}
class _MyAppChild extends State<MyAppChild> {
  @override
  void initState() {
    super.initState();
    print('child-initState');
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('child-didChangeDependencies');
  }
  @override
  void didUpdateWidget(covariant MyAppChild oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('child-didUpdateWidget');
  }
  @override
  void deactivate() {
    super.deactivate();
    print('child-deactivate');
  }
  @override
  void dispose() {
    super.dispose();
    print('child-dispose');
  }
  @override
  Widget build(BuildContext context) {
    print('child-build');
    return Container(
      width: 100,
      height: 100,
      color: ColorInheritedWidget.of(context)?.color,
      child: Text('${widget.value}'),
    );
  }
}

2.1创建阶段

运行demo,首次渲染会打印出:

flutter: createState
flutter: initState
flutter: didChangeDependencies
flutter: build
flutter: child-createState
flutter: child-initState
flutter: child-didChangeDependencies
flutter: child-build

从这里我们可以看出,StatefulWidget组件执行的顺序createState -> initState -> didChangeDependencies -> build。

  • createState

Widget只是对组件信息的描述,而Element才是最终对Widget的实现。通过Element可以进行Widget的挂载和RenderObject的创建,最终实现UI的渲染。

flutter首帧渲染的过程中,会不断向下遍历Widget树,通过cteateElememt创建Element实例,每创建一个Widget都会对应创建一个Element。例如创建StatefulWidget时会对应创建StatefulElement。

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key? key }) : super(key: key);
  @override
  StatefulElement createElement() => StatefulElement(this);
  /// ...
}

StatefulElement的构造函数中,执行了StatefulWidget的createState方法,完成对State的调用。

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
            state._element = this;
            state._widget = widget;
        }
  /// ...
}
  • initState

createState通过StatefulElement构造函数来创建,而initState在firstBuild方法中定义,firstBuild是首帧渲染的时候,通过StatefulElement实例的mount方法进行调用,firstBuild只会执行一次,也就是说initState只会在Widget首次创建次调用。

通常我们会使用initState进行数据的初始化,也可以进行网络请求操作。

/// StatefulElement
  @override
  void _firstBuild() {
    try {
      /// 调用initState
      final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
    } finally {
      /// ...
    }
    /// 调用didChangeDependencies
    state.didChangeDependencies();
    /// 调用父级ComponentElement类的_firstBuild,再往上到Element的rebuild方法,最后触发的是StatefulElement中的performRebuild方法。
    super._firstBuild();
  }
  @override
  void performRebuild() {
    /// _didChangeDependencies标记Element的依赖节点发生改变,didChangeDependencies会再次调用。
    if (_didChangeDependencies) {
      state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    /// 调用ComponentElement中的performRebuild,最终触发StatefulElement的build方法
    super.performRebuild();
  }
  @override
  Widget build() => state.build(this);

2.2更新阶段

在State类中调用了setState方法,会解发组件update流程,它会对比新旧Element,将修改过的组件标记为脏元素。如果Widget依赖InheritedWidget的数据发现变化,会触发didChangeDependencies函数,接着会调用子组件的update方法,在StatefulElement的update方法中执行了didUpdateWidget,最后才执行rebuild进行build的调用,完成UI的更新。

/// StatefulElement
  @protected
  @pragma('vm:prefer-inline')
  Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    /// ...
    final Element newChild;
    if (child != null) {
      bool hasSameSuperclass = true;
      if (hasSameSuperclass && child.widget == newWidget) {
        newChild = child;
      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        /// 更新逻辑会调用child的update方法
        child.update(newWidget);
        newChild = child;
      }
    }
    return newChild;
  }
/// StatefulElement
  @override
  void update(StatefulWidget newWidget) {
    super.update(newWidget);
    try {
      /// 调用state.didUpdateWidget
      final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
    }
    /// 重新rebuild深入遍历子组件
    rebuild();
  }

在demo中,点击“更新value”按钮,会打印出:

flutter: build
flutter: child-didUpdateWidget
flutter: child-build

从这里可以看出父组件setState时,会先走自身的build再触发子组件的didUpdateWidget和build。

2.3销毁阶段

在demo中,点击“移除”按钮,会打印出:

flutter: build
flutter: child-deactivate
flutter: child-dispose

组件移除节点后会调用deactivate,如果该组件被移除节点,然后未被 插入到其他节点时,则会继续调用 dispose 永久移除,并释放组件资源。

总结:

1、StatefulWidget通过State来管理状态数据,目的是为了保持StatefulWidget可常量创建,减少性能的损耗,提高Widget构建效率。

2、StatefulWidget创建阶段生命周期先执行顺序createState -> initState -> didChangeDependencies -> build。可以在initState进行数据初始化、网络请求操作。

3、StatefulWidget更新阶段:build -> child didUpdateWidget -> child build。

4、StatefulWidget销毁阶段:build -> child deactivate -> child dispose。

以上就是Flutter有状态组件StatefulWidget生命周期详解的详细内容,更多关于Flutter StatefulWidget生命周期的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter Widget开发Shortcuts快捷键实例

    目录 正文 ShortcutActivators到Intents的映射 想要捕获Control + C ? 正文 Flutter所提供的键盘快捷键系统直接用就很棒了,而且还提供了大量的空间可根据自己的喜好配置操作,之前那一篇博客介绍了小部件Focus 它会指示Flutter以你的应用来包裹键盘事件,以寻找匹配的Shortcuts小部件,这便会带入Shortcuts小部件. ShortcutActivators到Intents的映射 上一篇博客,我们以Accordion属性所假想的小部件树,挑个你

  • Flutter组件生命周期和App生命周期示例解析

    目录 引言 无状态组件(StatelessWidget) 有状态组件(StatefulWidget) StatefulWidget生命周期详细分析 1. createState 2. initState 3. didChangeDependencies 4. build 5. didUpdateWidget 6. deactivate 7. dispose 8. reassemble App生命周期 结束语 引言 在Flutter开发中,所有的组件和页面都继承自Widget,所以探索页面的生命周

  • Flutter SizedBox布局组件Widget使用示例详解

    目录 正文 child 的 constrains 确定自己的大小 SizedBox 的命名构造函数们 SizedBox.expand SizedBox.shrink SizedBox.fromSize SizedBox.square 应用场景 为 child 提供 tight 约束. 为 children 之间提供空白. 占位 正文 Flutter Sizedbox 是一个 布局组件,用来给 child 添加 tight 约束的,也可以用来添加空白. width,height是 Sizedbox

  • Flutter Widget之NavigationBar使用详解

    目录 正文 background 正文 这是一个和时间一样古老的故事.您的应用程序有三到五个主要内容区域,您的用户应该能够在任何屏幕之间切换. 那么,在这种情况下,请查看NavigationBar. 现在,您可能会想,“底部们有导航栏吗?,这个新的导航栏小部件有什么特别之处?“ 不同之处在于BoottomNavigationBar使用Material 2设计系统,而NavigationBar具有新的Material 3外观和感觉. 例如,药丸形状,它以对比色指示活动的目的地. 要启动并运行,为N

  • vue的生命周期钩子与父子组件的生命周期详解

    目录 vue的生命周期钩子的介绍 父子组件的生命周期 加载渲染过程 父组件更新过程 子组件更新过程 父子组件更新过程 销毁过程 代码示例 created和mounted的区别 vue的生命周期钩子的介绍 vue官网中提供的vue的生命周期钩子图 vue的生命周期可以分为8个阶段: 1.创建vue实例涉及的两个钩子 (1)beforeCreate:创建前,vue实例初始化之前调用. 此时的数据观察和事件配置都还没有准备好,而此时的实例中的data和el还是underfined状态,不可用的.Dom

  • Android编程中的四大基本组件与生命周期详解

    本文实例讲述了Android编程中的四大基本组件与生命周期.分享给大家供大家参考,具体如下: Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity : 应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应. Activity之间通过Intent进行通信.在Intent 的描述结构中,有两个最

  • Android入门教程之组件Activity的生命周期详解

    目录 返回栈 Activity 状态 1. 运行状态 2. 暂停状态 3. 停止状态 4. 销毁状态 Activity 的生存期 onCreate() onStart() onResume() onPause() onStop() onDestroy() onRestart() 完整生存期 可见生存期 前台生存期 Activity 回收处理 返回栈 Android 中的 Activity 是可以层叠的,我们每启动一个新的 Activity,就会覆盖在原有的 Activity 之上,然后点击 Ba

  • 基于asp.net MVC 应用程序的生命周期(详解)

    首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的生命周期,从一个控件到另一个控件是怎样被处理的.我们还会详细介绍一下整个请求的生命周期中,用到的相关组件.因为在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller和action方法之间做相关的处理,对于真正内在的运行机制可能不是很了解.其实

  • Tomcat生命周期详解

    目录 引言 1.LifeCycle接口设计 1.1 生命周期的方法 1.2 相关的状态处理 2.监听器和事件的设计 3.LifecycleBase 3.1 事件处理 3.2 生命周期方法 引言 在上篇文章中我们看到了Tomcat架构中的核心组件,而且各个组件都有各自的作用,各司其职,而且相互之间也有对应的父子关系,那么这些对象的创建,调用,销毁等操作是怎么处理呢? 也就是在Tomcat中的组件的对象生命周期是怎么管理的呢?针对这个问题,在Tomcat中设计了Lifecycle接口来统一管理Tom

  • React中的生命周期详解

    目录 react生命周期 常用的生命周期 不常用的生命周 完整的生命周期图 react生命周期 函数组件无生命周期,生命周期只有类组件才拥有 生命周期函数指在某一时刻组件会自动调用并执行的函数. React每个类组件都包含生命周期方法,以便于在运行过程中特定的阶段执行这些方法. 例如:我们希望在第一次将其呈现到DOM时设置一个计时器Clock.这在React中称为“安装”.我们也想在删除由产生 的DOM时清除该计时器Clock.这在React中称为“卸载”. 一般分为:挂载.更新.卸载 常用的生

  • Android开发Activity的生命周期详解

    目录 前言 典型情况下的生命周期分析 前言 Android生命周期分为两部分: (1)典型情况下的生命周期. (2)异常情况下的生命周期. 典型情况下的生命周期分析 图1 Activity的生命周期图解 图2 Activity生命周期的金字塔图 (1)典型情况下的生命周期指在有用户参与的情况下,Activity所经过的生命周期的改变,正常情况下,Activity的常用生命周期有以下几种情况: onCreate():Activity启动后第一个被调用的函数,常用来进行Activity的初始化,如创

  • Spring配置使用之Bean生命周期详解

    基本概念 Spring 中的 Bean 的生命周期,指的是 Bean 从创建到销毁的过程. 下面来探究下几个有关 Bean 生命周期配置的属性. lazy-init lazy-init 表示延迟加载 Bean,默认在 Spring IoC 容器初始化时会实例化所有在配置文件定义的 Bean,若启用了 lazy-init 则在调用 Bean 时才会去创建 Bean. 定义 Bean: public class Animals { public Animals(){ System.out.print

  • 基于Listener监听器生命周期(详解)

    一.Listener生命周期 listener是web三大组件之一,是servlet监听器,用来监听请求,监听服务端的操作. listener分为:(都是接口类,必须实现相应方法) 1.生命周期监听器(3个) ServletContextListener requestDestroyed 在容器启动时被调用(在servlet被实例化前执行) requestInitialized 在容器销毁时调用(在servlet被销毁后执行) HttpSessionListener sessionCreated

  • Java开发学习之Bean的作用域和生命周期详解

    目录 一.Bean 的作用域 二.Spring 的执行流程 三.Bean 的生命周期 一.Bean 的作用域 在之前学习Java基础的时候,有接触到作用域这样的概念.一个变量并不一定在任何区域都是有效的,限定这个变量的可用性的代码范围就是该变量的作用域. 但是在这里 Bean 的作用域的概念和以前所认为的作用域有所不同. Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式. 接下来,将会举一个案例来讲讲什么是作用域,什么是行为模式 案例概要: 创建一个共有的 Bean

随机推荐