我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

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生命周期的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

这篇文章主要为大家介绍了Flutter有状态组件StatefulWidget生命周期详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-13

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

这篇文章主要为大家介绍了Flutter组件生命周期和App生命周期示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-08

Flutter生命周期超详细讲解

这篇文章主要为大家介绍了Flutter生命周期和App生命周期示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-29

React组件的生命周期详解

React是用于构建用户界面的JavaScript库。本文详细讲解了React的生命周期,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-03-19

android生命周期的状态有哪几种

Android生命周期的状态有以下几种:1. 开始状态(Initial State):应用程序刚启动或重新启动,但没有创建任何活动(Activity)实例。2. 运行状态(Running State):活动实例已经被创建并正在运行。3. 暂
2023-10-20

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

Vue的生命周期就是vue实例从创建到销毁的全过程,也就是newVue()开始就是vue生命周期的开始。Vue实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom->渲染、更新->渲染、卸载等⼀系列过程,称这是Vue的⽣命周期
2022-11-13

Vue组件与生命周期详细讲解

Vue的生命周期就是vue实例从创建到销毁的全过程,也就是newVue()开始就是vue生命周期的开始。Vue实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom->渲染、更新->渲染、卸载等⼀系列过程,称这是Vue的⽣命周期
2022-11-13

java线程生命周期的状态有哪几种

Java线程的生命周期有以下几种状态:1. 新建(New):当线程对象被创建时,该线程处于新建状态。2. 就绪(Runnable):当线程对象调用 `start()` 方法后,该线程处于就绪状态,等待获取CPU时间片执行。3. 运行(Run
2023-09-11

ASP.NET组件的生命周期有哪些

本篇文章给大家分享的是有关ASP.NET组件的生命周期有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、 Instance 实例化通过控件的构造器所实例化。还可以通过被父
2023-06-18

Angular组件的生命周期有哪些

这期内容当中小编将会给大家带来有关Angular组件的生命周期有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。概述组件声明周期以及angular的变化发现机制红色方法只执行一次。变更检测执行的绿色方法
2023-06-15

bms裸金属服务器拥有的生命周期状态

裸金属服务器是一种使用裸金属(裸金属)材料制造的服务器,通常具有类似于金属的属性,如耐腐蚀性、防磁性和高强度等。这种服务器可以承受各种恶劣环境的考验,如高温、低温和潮湿等。以下是一些裸金属服务器所具备的生命周期状态:使用寿命:由于裸金属服务器不含金属材料,因此没有机械应力或磨损,因此可以持续使用很长时间。抗腐蚀性:裸金属不像金属那样容易被腐蚀,因此通常具有较低的腐蚀性。高强度和防磁性:
2023-10-26

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录