setsate()使用详解原理及注意事项

📚 Flutter 状态管理系列文章目录
  1. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  2. setState() 使用详解:原理及注意事项

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解

  8. Flutter Riverpod 使用详细解析

  9. Riverpod原理解析(实现一个自己的Riverpod

源码链接
在 Flutter 中使用 setState 是最原始也最简单的状态管理方法,但也有很多容易踩坑的地方。以下是使用 setState 时需要注意的一些关键点,结合实例和解释,帮你避免常见问题。


✅ 一、只能在 StatefulWidget 中使用

setState() 只能在 StatefulWidgetState 类中使用。

错误示例

class MyWidget extends StatelessWidget {int count = 0;Widget build(BuildContext context) {return ElevatedButton(onPressed: () {count++;         // 无法更新 UIsetState(() {}); // ❌ StatelessWidget 中没有 setState},child: Text('$count'),);}
}

✅ 二、避免在 build() 中调用 setState()

build() 是用来渲染 UI 的,不应该触发状态更新,否则可能导致死循环或性能问题。

错误示例


Widget build(BuildContext context) {setState(() {}); // ❌ 不要在 build 中调用 setStatereturn Text("Hello");
}

✅ 三、更新局部状态,避免重建整个树

使用 setState 会触发当前 widget 树下的 整个 build 函数重建。如果状态变化只影响一部分 UI,应尽可能拆分组件,减少无谓重建。

示例优化

// 父组件
class CounterPage extends StatefulWidget {_CounterPageState createState() => _CounterPageState();
}class _CounterPageState extends State<CounterPage> {int count = 0;Widget build(BuildContext context) {return Column(children: [ElevatedButton(onPressed: () => setState(() => count++),child: Text("Increment"),),CounterDisplay(count: count), // 👇 子组件,不会重建自身],);}
}// 子组件
class CounterDisplay extends StatelessWidget {final int count;const CounterDisplay({required this.count});Widget build(BuildContext context) {return Text("Count: $count");}
}

✅ 四、setState() 不是真的“修改值”,而是“触发重建”

setState 不会修改状态本身,而是通知 Flutter 状态已变,你要手动修改变量值

示例

setState(() {counter++; // 你必须手动更新变量
});

✅ 五、避免在 dispose() 后调用 setState()

异步操作结束后调用 setState(),要确保当前 widget 仍然挂载(mounted == true),否则会报错:

setState() called after dispose()

正确写法

Future<void> loadData() async {final data = await fetchSomething();if (!mounted) return;setState(() {this.data = data;});
}

✅ 六、避免频繁调用 setState

高频调用(如在动画帧、滚动、秒表中)会导致性能问题。建议使用 Ticker, AnimationController, 或 StreamBuilder 等方式替代。


✅ 七、异步操作时正确使用 setState

不要把 async 加到 setState() 上!

错误写法

setState(() async {data = await fetchData();  // ❌ 异步不应该在 setState 中执行
});

正确写法

fetchData() async {final result = await getData();setState(() {data = result;});
}

✅ 八、使用 setState 时注意 UI 闪烁问题

setState 每次都会刷新 UI,如果不加判断,可能导致不必要的刷新。

优化写法

setState(() {if (count != newCount) {count = newCount;}
});

🔚 总结:setState 使用注意事项

事项建议
位置仅用于 StatefulWidgetState
调用时机不要在 build()dispose() 后调用
控制刷新最小化 setState 范围,避免不必要的重建
性能高频更新避免使用,改用 AnimatedBuilderStreamBuilder
异步处理await,后 setState,并加上 if (mounted)

setState 的底层原理

setState() 是 Flutter 中最基础的状态管理方法,它的底层原理其实并不复杂,但理解清楚它的 触发机制、刷新原理与生命周期关系 对写出高质量 Flutter 代码非常关键。


🧠 一句话原理总结

setState() 的本质是:通知 Flutter 框架当前 Widget 的状态发生了改变,应该重新调用 build() 方法进行 UI 重建。


🧩 底层原理详细解析

1️⃣ StatefulWidgetState 是如何工作的?

当你创建一个 StatefulWidget 时:

class MyWidget extends StatefulWidget {_MyWidgetState createState() => _MyWidgetState();
}

Flutter 会:

  • 创建 MyWidget 实例(Widget 是不可变的)
  • 调用 createState(),创建一个可变的 _MyWidgetState
  • Flutter 框架持有的是 State 实例,它是真正有生命周期的部分

2️⃣ 调用 setState() 会发生什么?

setState(() {// 修改本地状态变量
});

底层流程如下:

✅ 第一步:标记该组件为 “dirty”
void setState(VoidCallback fn) {assert(_element != null);fn();  // 执行你的状态更新逻辑_element.markNeedsBuild();  // 标记需要重建
}

这里的 _elementStatefulElement,是 Flutter 框架中的一个核心类(继承自 Element)。

markNeedsBuild() 会:

  • 把当前组件加入 Flutter 的 “dirty list”
  • 等待下一帧进行批量更新(UI 重建)

3️⃣ 下一帧触发重建

Flutter 每帧都会检查是否有 “dirty” 组件:

  • 如果有,就调用对应 Statebuild() 方法
  • 用新的 Widget Tree 替换旧的 Widget Tree(差异对比后才真正重绘)

这是一种惰性+批量优化机制


⚙️ Flutter 渲染管线与 setState

完整的渲染管线:

setState() → markNeedsBuild() → scheduleBuildFor() →
build() → element tree diff → renderObject 更新 → 屏幕刷新

这说明:

  • Widget 是临时的、不可变的
  • Element 是桥梁,负责 diff 和 build
  • RenderObject 是最终绘制层,才真正操作屏幕像素

🧪 举例对比流程

class CounterWidget extends StatefulWidget {_CounterWidgetState createState() => _CounterWidgetState();
}class _CounterWidgetState extends State<CounterWidget> {int count = 0;Widget build(BuildContext context) {print('build called');return Text('$count');}void increment() {setState(() {count++; // 改变局部状态});}
}

调用 increment() 时:

  • count++ 执行
  • setState() 标记 dirty
  • Flutter 在下一帧重建 build() → 打印 "build called"

🧭 为什么不要在 build() 中调用 setState()

因为:

  • build() 每帧都会被调用(只要有状态变动)
  • 如果在里面调用 setState(),就会无限触发重新 build
  • 导致 死循环 + 性能爆炸

🧱 内部类核心:StatefulElement

class StatefulElement extends ComponentElement {State state;void markNeedsBuild() {if (!_dirty) {_dirty = true;scheduleBuildFor(this);}}Widget build() => state.build(this);
}

Flutter 核心通过 StatefulElement 调用你的 State.build() 方法,连接了框架与业务逻辑。


🎯 总结重点

核心点说明
setState() 本质标记 Widget 为 dirty,等待下一帧重建
谁触发 build()Flutter 调用 StatefulElement.build()
何时不该用build() 中调用、dispose() 后调用
是否立即刷新否,是下一帧
可控粒度不支持局部更新,整个 build() 重建

好的,下面是对 **Flutter 的三层架构(Widget → Element → RenderObject)**的结构图讲解和详细说明,帮助你深入理解 setState() 的底层机制。


Flutter 三层结构核心图解

╔═══════════════════════════╗
║ Widget(描述层) ║ ← build() 返回的就是 Widget
╠═══════════════════════════╣
║ StatelessWidget / StatefulWidget 等 ║
║ 描述 UI 结构,无状态、不可变 ║
╚═══════════════════════════╝
↓ 创建

╔═══════════════════════════╗
║ Element(桥梁层) ║ ← 持久存在,管理生命周期 & 状态
╠═══════════════════════════╣
║ StatelessElement / StatefulElement ║
║ 持有 Widget & State 的引用 ║
║ 执行 build() 构建子 Widget 树 ║
╚═══════════════════════════╝
↓ attach/renderObject

╔═══════════════════════════╗
║ RenderObject(渲染层). ║ ← 真正控制布局、绘制、点击
╠═══════════════════════════╣
║ RenderBox、RenderFlex 等 ║
║ 处理 layout、paint、hitTest 等渲染逻辑 ║
╚═══════════════════════════╝


🔄 三层之间的关系(动态流程)

更新
rebuild
setState
repaint/layout
Widget - UI描述
Element - 桥梁
RenderObject - 渲染
  • Widget 是短生命周期的,每次 build() 都新建
  • Element 是连接状态和视图的“桥梁”,管理生命周期
  • RenderObject 是长期存在的,真正控制 UI 渲染

🎯 setState() 穿越三层过程示意

setState(() {count++;  // 修改状态
});

发生了什么?

  1. State.setState() 被调用
  2. 内部调用 _element.markNeedsBuild()
  3. Flutter 将 Element 标记为 dirty,加入 build 队列
  4. 下一帧执行:调用 build() → 新 Widget 树
  5. Flutter 比较新旧 Widget 树(Widget diff)
  6. 如果发现 Widget 类型或属性变了 → 更新对应的 RenderObject

✅ 举个具体例子

class MyWidget extends StatefulWidget {State<MyWidget> createState() => _MyWidgetState();
}class _MyWidgetState extends State<MyWidget> {int count = 0;Widget build(BuildContext context) {return Text("count: $count");}
}
  1. 创建 MyWidget 时:

    • Widget → MyWidget
    • Element → StatefulElement
    • State → _MyWidgetState
    • RenderObject → RenderParagraph(因为 Text
  2. 每次 setState() 时:

    • 只是重新执行 build(),生成新的 Widget(Text Widget)
    • Flutter 检查新旧 Text Widget → 如果数据变了,更新文字
    • 不重新创建 Element,不重建 RenderObject,只是更新其属性

📌 小贴士:什么时候会重建 Widget / Element / RenderObject?

改变内容Widget 重建Element 重建RenderObject 重建
属性变化(文本、颜色)❌(只更新属性)
Widget 类型变化✅(换了渲染类型)
setState() 调用❌(除非 Widget 类型变)

📷 总结一句话图解

用户写的 build() → 生成 Widget Tree↓
setState() 触发 → 当前 Element 标记为 dirty↓
下一帧执行 build → diff → 更新 RenderObject 属性或结构

深入理解 Flutter 的性能优化机制,关键就是理解它如何通过 Widget diff 算法渲染管线(Render Pipeline) 来实现:

  • 高效 UI 更新
  • 避免不必要的 rebuild/repaint/layout

🧠 一句话总结:

Flutter 借助 immutable Widget + Element diff + RenderObject 最小更新,实现了 React 样式的响应式 UI,又比 Web 更接近原生性能。


🧩 一、Flutter 的 Widget diff 算法(又称 Reconciliation)

Flutter 每次 build() 都会 重新创建 Widget 树,这看似“浪费”,但其实它借助 diff 算法 很聪明地做了优化。

🧱 Widget 是临时描述(不可变)

每次 build() 都会返回新 Widget 对象,但 Flutter 会:

  • 比较新旧 Widget
  • 如果类型相同,只更新属性
  • 如果类型不同,销毁旧 Element/RenderObject,创建新的

🧠 Diff 算法的核心逻辑(简化版):

if (newWidget.runtimeType == oldWidget.runtimeType &&newWidget.key == oldWidget.key) {// ✅ 复用 Element/RenderObject,仅更新属性
} else {// ❌ 类型变了 → 销毁旧的,创建新的
}

✅ 举例对比:

情况一:仅属性改变
Text('Hello')Text('World')
  • Widget 类型相同 → 复用 Element + RenderObject
  • 只更新 RenderParagraph 的文本内容
情况二:类型改变
Text('Hello')Icon(Icons.add)
  • 类型不一样 → 销毁 Text 的 Element/RenderObject,创建新的 Icon 渲染结构

🎯 Diff 高效的关键:key

如果你用 ListView 等组件,动态插入/删除/移动元素时:

  • Flutter 不仅靠 index 匹配,还依赖 key
  • key 能帮助 Flutter 正确地识别“哪个 Widget 是哪个”

⚠️ 没有 key 时的问题:

List<String> items = ['A', 'B', 'C'];// 删除 B,列表变成 ['A', 'C']
  • 没有 key → Flutter 以为 A→A, B→C(错误复用)
  • 会导致 UI 逻辑错误、动画错位、状态异常

解决:使用 ValueKey/UniqueKey


🧱 二、Flutter 渲染管线 Render Pipeline

完整流程如下:

flowchart TDA[Widgets] -->|build| B[Element Tree]B -->|diff| C[RenderObject Tree]C --> D[Layout]D --> E[Paint]E --> F[Compositing]F --> G[Rasterize (GPU)]

每一层的职责:

阶段描述
WidgetUI 声明,构建 UI 树
Element持久节点,负责 build 和 diff 逻辑
RenderObject实际布局 & 绘制对象,继承 RenderBox
Layout测量 & 安排位置(如 constraintssize
Paint将 UI 绘制指令写入 Layer 树(并不会立即绘制)
Compositing将不同 layer 合成,优化合成过程(如缓存、硬件加速)
Rasterize将 Layer 转为像素图,由 Skia 引擎提交 GPU

⏱️ 三、性能优化重点点拨

技术点意义与作用
const Widget避免重复 build,提升构建性能
合理使用 key减少 diff 误判,提升重排效率
shouldRebuild 控制避免不必要 rebuild,例如 AnimatedBuilder
RepaintBoundary创建隔离区域,防止重绘扩散
分离 widget减少 setState 影响范围
使用 ListView.builder延迟构建 item,提升大列表性能

📌 小结:Flutter 高性能的核心秘诀

秘诀说明
Widget 不可变易于比较,diff 高效
Element 长驻内存持久对象,减少创建销毁开销
RenderObject 重用类型一致不重建,只更新属性
分帧渲染机制每帧只更新变化区域,GPU 高效
Layer 合成优化避免全屏绘制,支持局部缓存

🧪 想要实战中验证 diff 效果?

你可以在 build() 中打 log:


Widget build(BuildContext context) {print('build called');return Text("value");
}

或使用 Flutter DevTools → Timeline → Frame chart 看每帧 build/repaint/layout 开销。


源码链接

📚 Flutter 状态管理系列文章目录
  1. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  2. setState() 使用详解:原理及注意事项

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解

  8. Flutter Riverpod 使用详细解析

  9. Riverpod原理解析(实现一个自己的Riverpod

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

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

相关文章

ffmpeg环境配置

ffmpeg是一个跨平台功能强大的音视频处理工具。它不仅能够进行音视频的转换、剪切、合并等操作&#xff0c;还支持多种音视频格式的解码和编码。无论是处理高清视频还是音频文件&#xff0c;ffmpg都能提供高效且稳定的服务。其丰富的参数设置和插件支持&#xff0c;使得用户可以…

Lamp和友点CMS一键部署脚本(Rocky linux)

先上传youdiancms9.3.0.zip包&#xff0c;可以去官网下载 #!/bin/bash #function:install apache mysql php for youdiancms9.3.0ip$(hostname -I | awk {print $1}) yhyoudian passwordyoudian123#检查是否为root用户 if [ "$USER" ! "root" ]; thenecho…

Arm架构下麒麟V10桌面版安装MySQL

Arm架构下麒麟V10桌面版安装MySQL 文章目录 Arm架构下麒麟V10桌面版安装MySQL基础环境下载安装包安装步骤**一、准备工作****二、解压并配置 MySQL****三、初始化 MySQL****四、启动 MySQL 服务****五、设置环境变量****六、配置 MySQL****七、验证安装****常见问题****总结** …

Science Advances:皮肤附着触觉贴片,实现多功能和增强的触觉交互

可穿戴触觉界面可以通过向皮肤添加触觉刺激以及传递给用户的视觉和听觉信息来增强虚拟/增强现实系统中的沉浸式体验。研究人员介绍了一种平锥介电弹性体致动器&#xff08;FCDEA&#xff09;阵列&#xff0c;该阵列薄而柔软&#xff0c;能够响应大面积皮肤上的电压信号产生时空…

Ntfs!LfsGetLbcb函数分析之nt!CcPreparePinWrite

Ntfs!LfsGetLbcb函数分析之nt!CcPreparePinWrite 第一部分&#xff1a; 1: kd> p Ntfs!LfsPrepareLfcbForLogRecord0x78: f7179d72 e891210000 call Ntfs!LfsGetLbcb (f717bf08) 1: kd> t Ntfs!LfsGetLbcb: f717bf08 6a40 push 40h 1: kd> kc …

面试150 整数转罗马数字

思路 建立数字和字符的字典映射表&#xff0c;遍历映射表做差&#xff0c;将字符添加到结果中&#xff0c;当差为0的时候&#xff0c;break退出循环。返回最后的结果output class Solution:def intToRoman(self, num: int) -> str:if num<1:return num_to_map[(1000,M)…

大模型推理-高通qnn基础

一、高通ai 软件的介绍 QualcommAI Engine Direct SDK(qnn) 提供较低级别、高度可定制的统一API&#xff0c;通过单独的库加速所有AI加速器核心上的AI模型, 可以直接用于针对特定的加速器核心或从流行的运行时&#xff08;包括Qualcomm Neural Processing SDK、TensorFlow Li…

UE5 - 制作《塞尔达传说》中林克的技能 - 14 - 技能面板

让我们继续《塞尔达传说》中林克技能的制作&#xff01;&#xff01;&#xff01; UE版本&#xff1a;5.6.0 VS版本&#xff1a;2022 本章节的核心目标&#xff1a;技能面板 先让我们看一下完成后的效果&#xff1a; 第14章效果 本章节项目链接&#xff1a; 通过网盘分享的文件…

用Tensorflow进行线性回归和逻辑回归(八)

新的TensorFlow概念 创建简单的机器学习系统需要学习一些新的概念。 优化器 上两节介绍的元素已经提示了TensorFlow是如何完成机器学习的。你已学习了如何用张量操作来定义损失函数。缺少的是你不知道如何用TensorFlow进行梯度下降。尽管可以用TensorFlow元素直接用 Python定…

基于python代码的通过爬虫方式实现TK下载视频(2025年6月)

Tk的视频页面通常需要登录才能获取完整数据,但通过构造匿名游客的请求,我们可以绕过登录限制,提取视频的元信息(如标题、ID和播放地址)。核心思路如下: 构造匿名Cookie:通过模拟浏览器的请求,获取Tk服务器分配的游客Cookie。解析网页:利用BeautifulSoup解析HTML,定位…

火山 RTC 引擎14 设置CB

一、火山RTC引擎集成时,设置CB 1、统一设置 void NRTCEngine::SetByteRtcCBS() {UserPublishStreamCallback callback = [this](const std::string& roomId, const std::string& uid, bytertc::MediaStreamType type) {this->OnSigUserPublishStream(roomId, uid, …

BUUCTF在线评测-练习场-WebCTF习题[极客大挑战 2019]PHP1-flag获取、解析

解题思路 打开靶场&#xff0c;提示备份 常见的备份后缀名有.bak&#xff0c;.backup&#xff0c;.zip等等 这里肯定是要扫目录了&#xff0c;不知道是我的问题还是目录扫描工具的问题还是BUUCTF的问题&#xff0c;每次要扫目录能扫出一堆东西来&#xff0c;不管你用什么后缀…

对话云蝠智能:大模型如何让企业呼叫系统从 “成本中心” 变身 “价值枢纽”?

在人工智能重塑企业服务的浪潮中&#xff0c;云蝠智能&#xff08;南京星蝠科技有限公司旗下品牌&#xff09;以深厚的技术积累和行业实践&#xff0c;逐步成长为国内智能外呼领域的标杆企业。其发展路径揭示了技术自主创新与场景深度结合的必然性。 一、技术架构&#xff1a;全…

Python-文件管理

1. Open方法 Python 中的文件操作主要通过内置的 open() 函数来完成&#xff0c;该函数用于打开文件&#xff0c;并返回一个文件对象。通过文件对象&#xff0c;可以进行各种文件操作&#xff0c;如读取、写入、关闭等。 使用 open() 方法一定要保证关闭文件对象&#xff0c;即…

高速DIC技术之推进剂样品在霍普金森杆的高速冲击下的变形监测与不同材质头盔在不同冲击位置下的变形测试-VIC-3D HS非接触全场动态应变测量系统

工程领域对材料与结构在极端动态载荷下复杂变形行为的测量有强烈的需求&#xff0c;且这种测量必须是精确、全域、非接触式的&#xff0c;高速DIC技术应运而生并不断得到发展。 常见动态应用包括&#xff08;但不限于&#xff09;&#xff1a;碰撞测试、爆炸试验、冲击试验、跌…

微算法科技融合Grover算法与统一哈希函数的混合经典-量子算法技术,可在多领域高效提升文本处理效率

随着数据规模的不断扩大&#xff0c;尤其是在大数据和人工智能驱动的应用中&#xff0c;这些经典算法的线性复杂度逐渐成为瓶颈。面对数十亿级别的文本数据&#xff0c;线性时间的算法仍然难以满足实时性的要求。此外&#xff0c;经典算法在处理无序或随机文本时&#xff0c;性…

Spring Boot Security Core

依赖配置&#xff08;Maven&#xff09; xml 复制 下载 运行 <!-- Spring Security Core --> <dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>6.2.5<…

【赵渝强老师】OceanBase云平台OCP

OCP的全称是OceanBase Cloud Platform&#xff0c;即&#xff1a;OceanBase云平台。OCP面向有OceanBase管理需求的中小型用户&#xff0c;提供了基础的主机管理、OceanBase 集群和租户运维等能力。在OCP中主要包含两个组成部分&#xff0c;它们分别是&#xff1a;MetaDB和OCP S…

快速定位Swagger文档请求地址‘/v2/api-docs‘,发生未知异常.NullPointerException:精准定位+根治方案

问题现场&#xff1a;访问 http://localhost:8080/v2/api-docs 时日志报错&#xff1a; 请求地址/v2/api-docs,发生未知异常. java.lang.NullPointerException: nullat springfox.documentation.swagger2.mappers.RequestParameterMapper.bodyParameter(RequestParameterMappe…

图像处理解决方案

证件照小工具微信小程序系统是基于微擎开源生态开发的多功能图像处理平台&#xff0c;专为用户提供便捷的证件照制作及图片编辑服务。微擎系统作为一款基于 PHP 开发的开源应用生态系统&#xff0c;具备快速搭建多端应用的能力&#xff0c;其模块化架构与跨平台兼容性为证件照工…