深入理解Flutter生命周期函数之StatefulWidget(一)

news2024/11/16 10:32:59

目录

前言

1.为什么需要生命周期函数

2.开发过程中常用的生命周期函数

1.initState() 

2.didChangeDependencies()

3.build()

4.didUpdateWidget()

5.setState() 

6.deactivate()

7.dispose()

3.Flutter生命周期总结

1.调用顺序

2.函数调用时机以及主要作用

4.生命周期函数的验证

1.创建和销毁时期函数的验证

2.didUpdateWidget函数验证

3.didChangeDependencies函数验证


前言

        在Flutter中,生命周期函数是管理StatefulWidget状态的关键机制。通过生命周期函数,我们可以控制Widget的初始化、更新和销毁过程,使得应用的状态管理和资源控制更加灵活。本文将详细介绍Flutter中的生命周期函数,帮助你更好地掌握Flutter应用的生命周期。

1.为什么需要生命周期函数

        生命周期函数允许我们在Widget的创建、更新和销毁过程中执行特定操作,比如数据的初始化、网络请求、资源的释放等。尤其是在StatefulWidget中,这些函数确保了应用在不同状态下的正确行为。

2.开发过程中常用的生命周期函数

1.initState() 

        这个方法在State对象被插入到树中时调用,仅调用一次。

        适合做初始化操作,例如初始化变量、加载数据或创建动画控制器。

@override
void initState() {
  super.initState();
  debugPrint("initState method is called!");
}

2.didChangeDependencies()

        这个方法在initState()调用之后,或者当依赖的InheritedWidget发生变化时调用。

        这个方法适用于需要访问依赖于上下文的情况,比如当Widget依赖于某个InheritedWidget的变化。

        下面的代用于打印"didChangeDependencies method is called!"。

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  debugPrint("didChangeDependencies method is called!");
}

3.build()

        build函数是构建UI的核心函数。

        build()在每次Widget需要重建时都会调用,包括在初次加载时、调用setState()之后。

        build方法用于描述Widget在屏幕上的展示方式。这个函数会频繁调用,因此确保代码尽量简洁高效。

        在这段代码中,build()函数返回一个带有计数器的页面,并包含一个按钮用于增加计数器。

@override
Widget build(BuildContext context) {
  debugPrint("build method is called!");
  return Scaffold(
    appBar: AppBar(
      backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      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.headlineMedium,
          ),
          ElevatedButton(onPressed: (){
            // 跳转逻辑
          }, child: const Text('跳转下一个页面')),
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: 'Increment',
      child: const Icon(Icons.add),
    ),
  );
}

4.didUpdateWidget()

        调用时机:当父Widget重新构建,并将新的Widget传递给子Widget时调用。

        作用:适合在父Widget属性变化时执行相应操作,比如更新状态或重新初始化数据。

        在下面代码中,它打印"didUpdateWidget method is called!",用于展示父级属性更新时的情况。

@override
void didUpdateWidget(covariant MyHomePage oldWidget) {
  super.didUpdateWidget(oldWidget);
  debugPrint("didUpdateWidget method is called!");
}

5.setState() 

        调用时机:在状态变化时,通过手动调用setState()来触发。

        作用:通知Flutter框架状态已改变,触发build()方法重新构建Widget。注意避免频繁调用setState()以减少性能开销。

@override
void didUpdateWidget(covariant MyHomePage oldWidget) {
  super.didUpdateWidget(oldWidget);
  debugPrint("didUpdateWidget method is called!");
}

6.deactivate()

        调用时机:当State对象被临时从树中移除时调用。

       作用:可以在这里执行一些临时清理工作。通常不需要在这里执行大量操作,使用场景不多。

        在这段代码中,deactivate()打印了"deactivate method is called!"。

@override
void deactivate() {
  super.deactivate();
  debugPrint("deactivate method is called!");
}

7.dispose()

        调用时机:当State对象永久性地从树中移除时调用。

        作用:适合释放资源,比如取消订阅、关闭控制器等。
        在代码中,dispose()打印了"dispose method is called!",用来展示页面被销毁时的情况。

@override
void dispose() {
  super.dispose();
  debugPrint("dispose method is called!");
}

3.Flutter生命周期总结

1.调用顺序

        通过上面的介绍,可以看到StatefulWidget的生命周期有以下顺序:

  1. 创建阶段:initState() → didChangeDependencies()
  2. 更新阶段:build() → didUpdateWidget() → setState()
  3. 销毁阶段:deactivate() → dispose()

       每个函数在Widget生命周期中扮演着不同的角色:

2.函数调用时机以及主要作用

函数

调用时机主要作用

initState()

Widget被插入树中时调用一次

初始化操作,仅调用一次

didChangeDependencies()

initState()后及依赖变化时调用

处理依赖项

build()

每次Widget需要重建时

构建UI并返回Widget

didUpdateWidget()

父Widget重新构建并传入新参数时调用

处理父组件属性变化

setState()

状态变化时手动调用

更新Widget并触发重建

deactivate()

State从树中暂时移除时

清理临时状态(不常用)

dispose()

State永久移除时

释放资源,避免内存泄漏

4.生命周期函数的验证

1.创建和销毁时期函数的验证

        我们以下面的代码为例,当app启动的之后:

        图1.示例demo

     控制台打印信息如下:

图2.进入页面控制台打印日志

        当我们点击remove按钮之后,移除子控件,控制台的打印信息如下:

图3.点击Remove之后控制台打印日志

2.didUpdateWidget函数验证

        要确保 didUpdateWidget 函数被调用,需要使 StatefulWidget 的 父级组件对其属性进行更新,而不是使用 UniqueKey 强制组件重新创建。我们修改下代码,点击按钮之后,修改子组件的颜色,通过更新 ChildWidget 的 color 属性触发 didUpdateWidget 的调用。     

        效果图如下:   

       图4.didUpdateWidge函数验证demo
        然后我们在didUpdateWidge中打印该函数,点击按钮之后,控制台打印信息如下:

图5.控制台打印日志

        完整代码如下:

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const ParentPage(),
    );
  }
}

class ParentPage extends StatefulWidget {
  const ParentPage({super.key});

  @override
  State<ParentPage> createState() => _ParentPageState();
}

class _ParentPageState extends State<ParentPage> {
  Color childColor = Colors.blue;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Lifecycle Demo')),
      body: Center(child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              setState(() {
                // Toggle color between blue and green
                childColor = childColor == Colors.blue ? Colors.green : Colors.blue;
              });
            },
            child: const Text('Change Child Widget Color'),
          ),
          const SizedBox(height: 20),
          ChildWidget(
            color: childColor, // Pass updated color
          ),
        ],
      ),)
    );
  }
}

class ChildWidget extends StatefulWidget {
  final Color color;

  const ChildWidget({super.key, required this.color});

  @override
  State<ChildWidget> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildWidget> {
  @override
  void initState() {
    super.initState();
    debugPrint("ChildWidget: initState");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    debugPrint("ChildWidget: didChangeDependencies");
  }

  @override
  void didUpdateWidget(covariant ChildWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    debugPrint("ChildWidget: didUpdateWidget - Old color: ${oldWidget.color}, New color: ${widget.color}");
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("ChildWidget: build");
    return Container(
      height: 100,
      width: 100,
      color: widget.color,
    );
  }

  @override
  void deactivate() {
    super.deactivate();
    debugPrint("ChildWidget: deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    debugPrint("ChildWidget: dispose");
  }
}

3.didChangeDependencies函数验证

        要验证 didChangeDependencies 的调用时机,我们需要引入一个可以触发依赖改变的机制,比如 InheritedWidget 或 MediaQuery 的更新。以下是代码的更新版本,通过使用 InheritedWidget 来测试 didChangeDependencies 的调用。

        我们修改下代码:

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return InheritedColorProvider(
      color: Colors.blue,
      child: MaterialApp(
        home: const ParentPage(),
      ),
    );
  }
}

class InheritedColorProvider extends InheritedWidget {
  final Color color;

  const InheritedColorProvider({
    super.key,
    required this.color,
    required Widget child,
  }) : super(child: child);

  static InheritedColorProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<InheritedColorProvider>();
  }

  @override
  bool updateShouldNotify(InheritedColorProvider oldWidget) {
    return color != oldWidget.color;
  }
}

class ParentPage extends StatefulWidget {
  const ParentPage({super.key});

  @override
  State<ParentPage> createState() => _ParentPageState();
}

class _ParentPageState extends State<ParentPage> {
  Color appColor = Colors.blue;

  void _changeColor() {
    setState(() {
      appColor = appColor == Colors.blue ? Colors.green : Colors.blue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return InheritedColorProvider(
      color: appColor,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Lifecycle Demo'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _changeColor,
              child: const Text('Change Inherited Color'),
            ),
            const SizedBox(height: 20),
            const ChildWidget(),
          ],
        ),
      ),
    );
  }
}

class ChildWidget extends StatefulWidget {
  const ChildWidget({super.key});

  @override
  State<ChildWidget> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildWidget> {
  @override
  void initState() {
    super.initState();
    debugPrint("ChildWidget: initState");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    debugPrint("ChildWidget: didChangeDependencies - Color: ${InheritedColorProvider.of(context)?.color}");
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("ChildWidget: build");
    final color = InheritedColorProvider.of(context)?.color ?? Colors.transparent;

    return Container(
      height: 100,
      width: 100,
      color: color,
    );
  }

  @override
  void didUpdateWidget(covariant ChildWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    debugPrint("ChildWidget: didUpdateWidget");
  }

  @override
  void deactivate() {
    super.deactivate();
    debugPrint("ChildWidget: deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    debugPrint("ChildWidget: dispose");
  }
}

        在修改之后的代码中,我们做了以下工作:

1.引入了InheritedWidget

        InheritedColorProvider 是一个简单的 InheritedWidget,用来共享 color 属性。

 2.修改了更新逻辑

        在 ParentPage 中,通过修改 InheritedColorProvider 的 color 属性触发 didChangeDependencies。

3.验证依赖关系

        子组件 ChildWidget 会依赖 InheritedColorProvider,当 color 发生变化时,didChangeDependencies 会被调用。

        当我们点击修改按钮之后,控制台打印日志如下:

ChildWidget: initState
ChildWidget: didChangeDependencies - Color: Color(0xff0000ff)
ChildWidget: build
ChildWidget: didChangeDependencies - Color: Color(0xff00ff00)
ChildWidget: build

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2241443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

llama factory lora 微调 qwen2.5 7B Instruct模型

项目背景 甲方提供一台三卡4080显卡 需要进行qwen2.5 7b Instruct模型进行微调。以下为整体设计。 要使用 LLaMA-Factory 对 Qwen2.5 7B Instruct模型 进行 LoRA&#xff08;Low-Rank Adapters&#xff09;微调&#xff0c;流程与之前提到的 Qwen2 7B Instruct 模型类似。LoRA …

机器学习day2-特征工程

四.特征工程 1.概念 一般使用pandas来进行数据清洗和数据处理、使用sklearn来进行特征工程 将任意数据&#xff08;文本或图像等&#xff09;转换为数字特征&#xff0c;对特征进行相关的处理 步骤&#xff1a;1.特征提取&#xff1b;2.无量纲化&#xff08;预处理&#xf…

Llama架构及代码详解

Llama的框架图如图&#xff1a; 源码中含有大量分布式训练相关的代码&#xff0c;读起来比较晦涩难懂&#xff0c;所以我们对llama自顶向下进行了解析及复现&#xff0c;我们对其划分成三层&#xff0c;分别是顶层、中层、和底层&#xff0c;如下&#xff1a; Llama的整体组成…

stm32在linux环境下的开发与调试

环境安装 注&#xff1a;文末提供一键脚本 下载安装stm32cubeclt 下载地址为&#xff1a;https://www.st.com/en/development-tools/stm32cubeclt.html 选择 linux版本下载安装 安装好后默认在家目录st下 > $ ls ~/st/stm32cubeclt_1.16.0 …

第T7周:Tensorflow实现咖啡豆识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: &#xff08;二&#xff09;具体步骤 1. 使…

亲测有效:Maven3.8.1使用Tomcat8插件启动项目

我本地maven的settings.xml文件中的配置&#xff1a; <mirror><id>aliyunmaven</id><mirrorOf>central</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url> </mirror>…

LLM - 使用 LLaMA-Factory 微调大模型 Qwen2-VL SFT(LoRA) 图像数据集 教程 (2)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/143725947 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 LLaMA-…

神经网络与Transformer详解

一、模型就是一个数学公式 模型可以描述为:给定一组输入数据,经过一系列数学公式计算后,输出n个概率,分别代表该用户对话属于某分类的概率。 图中 a, b 就是模型的参数,a决定斜率,b决定截距。 二、神经网络的公式结构 举例:MNIST包含了70,000张手写数字的图像,其中…

鲸鱼机器人和乐高机器人的比较

鲸鱼机器人和乐高机器人各有其独特的优势和特点&#xff0c;家长在选择时可以根据孩子的年龄、兴趣、经济能力等因素进行综合考虑&#xff0c;选择最适合孩子的教育机器人产品。 优势 鲸鱼机器人 1&#xff09;价格亲民&#xff1a;鲸鱼机器人的产品价格相对乐高更为亲民&…

Flink Source 详解

Flink Source 详解 原文 flip-27 FLIP-27 介绍了新版本Source 接口定义及架构 相比于SourceFunction&#xff0c;新版本的Source更具灵活性&#xff0c;原因是将“splits数据获取”与真“正数据获取”逻辑进行了分离 重要部件 Source 作为工厂类&#xff0c;会创建以下两…

路漫漫其修远兮,吾将上下而求索---第一次使用github的过程记录和个人感受

文章目录 1.仓库位置2.新建仓库3.配置仓库4.克隆和上传5.推荐文章和我的感受 1.仓库位置 这个仓库的位置就是在我们的这个个人主页的右上角&#xff1b;如果是第一次注册账号的话&#xff0c;这个主页里面肯定是不存在仓库的&#xff0c;需要我们自己手动的进行创建&#xff1…

npm list -g --depth=0(用来列出全局安装的所有 npm 软件包而不显示它们的依赖项)

您提供的命令 npm list -g --depth0 是在 Node Package Manager (npm) 的上下文中使用的&#xff0c;用来列出全局安装的所有 npm 软件包而不显示它们的依赖项。 这是它的运作方式&#xff1a; npm list -g --depth0-g: 指定列表应包括全局安装的软件包。--depth0: 限制树形结…

tdengine学习笔记

官方文档&#xff1a;用 Docker 快速体验 TDengine | TDengine 文档 | 涛思数据 整体架构 TDENGINE是分布式&#xff0c;高可靠&#xff0c;支持水平扩展的架构设计 TDengine分布式架构的逻辑结构图如下 一个完整的 TDengine 系统是运行在一到多个物理节点上的&#xff0c;包含…

K8S单节点部署及集群部署

1.Minikube搭建单节点K8S 前置条件&#xff1a;安装docker&#xff0c;注意版本兼容问题 # 配置docker源 wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo# 安装docker环境依赖 yum install -y yum-utils device-m…

以往运维岗本人面试真题分享

以下是本人面试运维岗的一些面试经历&#xff0c;在此做个记录分享 目录 TCP/IP三次握手 IPtables IPtables四表五链都是什么&#xff1f; nat端口如何做&#xff1f; 开放本机的80端口该如何做&#xff1f; 如何在单用户模式下引导Centos&#xff1f; nginx轮询模式都有…

STM32 串口输出调试信息

软硬件信息 CubeMX version 6.12.1Keil uVision V5.41.0.0 注意 串口有多种&#xff1a; TTL232485 串口的相关知识&#xff1a; 01-【HAL库】STM32实现串口打印&#xff08;printf方式) &#xff0c; 内含 TTL 和 232 区别。 我把 232 串口连进 STM32 串口助手收到的信息…

Python 三种方式实现自动化任务

在这篇文章中&#xff0c;我们将介绍一些用Python实现机器人过程自动化的包。机器人流程自动化&#xff08;Robotic process automation&#xff0c;简称RPA&#xff09;是指将鼠标点击和键盘按压自动化的过程&#xff0c;即模拟人类用户的操作。RPA用于各种应用程序&#xff0…

Android ART知多少?

Android 虚拟机 ART&#xff08;Android Runtime&#xff09;是 Android 平台上的应用程序运行时环境&#xff0c;用于执行应用程序的字节码。ART 自 Android 5.0&#xff08;Lollipop&#xff09;开始取代了 Dalvik&#xff0c;成为 Android 的默认运行时环境。本文将从以下几…

Vulnhub靶场 Billu_b0x 练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 文件包含2. SQL注入3. 文件上传4. 反弹shell5. 提权&#xff08;思路1&#xff1a;ssh&#xff09;6. 提权&#xff08;思路2&#xff1a;内核&#xff09;7. 补充 0x04 总结 0x00 准备 下载链接&#…

软间隔支持向量机支持向量的情况以及点的各种情况

软间隔支持向量 ​ 这一节我们要回答的问题是&#xff1f;如何判断一个点是软间隔支持向量机中的支持向量&#xff0c;在硬间隔支持向量机中&#xff0c;支持向量只需要满足一个等式&#xff1a; y i ( w T x i b ) − 1 0 y_i(w^Tx_i b) -1 0 yi​(wTxi​b)−10 ​ 在软间…