news 2026/6/16 23:34:51

Flutter 列表 rebuild 的真正边界在哪里

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter 列表 rebuild 的真正边界在哪里


子玥酱(掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:
掘金、知乎、CSDN、简书
创作特点:
实战导向、源码拆解、少空谈多落地
文章状态:
长期稳定更新,大量原创输出

我的内容主要围绕前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”

持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱

文章目录

    • Flutter 从来不是“按行 rebuild”
    • 一次列表滚动,Flutter 实际在做什么
      • 滚动时 Flutter 的真实流程
    • rebuild 的“真正触发条件”
      • setState
      • InheritedWidget 依赖变化
      • 父 Widget rebuild
    • 一个“看似安全,实际危险”的列表写法
    • Flutter 列表的“安全边界”到底在哪里
    • 正确拆边界的基本写法
      • 列表容器不订阅业务状态
      • item 自己订阅自己的数据
    • rebuild、layout、paint,不是一回事
      • rebuild ≠ layout ≠ paint
    • 为什么你“感觉整个列表都在 rebuild”
    • 一个更接近真实项目的 Demo 对比
      • 错误边界写法
      • 正确边界写法
    • Flutter 为什么不替你“自动做对”
    • 一句话总结 rebuild 的真正边界
    • 总结

很多 Flutter 列表性能问题,本质都可以归结成一句话:

你以为只 rebuild 了一行,其实 rebuild 了一片。

Flutter 不慢,慢的是你没搞清楚它以什么为边界做更新

Flutter 从来不是“按行 rebuild”

这是一个非常重要、但经常被误解的点。

在 Flutter 里:

  • rebuild 的最小单位不是 ListView 的 item
  • 而是:Widget 子树

也就是说:

rebuild 到哪一层停,完全取决于你把状态“挂”在了哪一层。

一次列表滚动,Flutter 实际在做什么

我们从一次最普通的滚动开始拆。

滚动时 Flutter 的真实流程

  1. 滚动发生

  2. Sliver 判断哪些 item 进入 / 离开可视区

  3. 对新进入的 item:

    • 调用 itemBuilder
    • build Widget
    • layout
    • paint
  4. 对离开的 item:

    • Element 可能被回收或缓存

注意这里的关键词:itemBuilder 会被重新调用

但这并不等于:

item 一定 rebuild
更不等于整个列表 rebuild

rebuild 的“真正触发条件”

Flutter rebuild 的触发只有几类,非常明确:

setState

setState((){count++;});
  • 会触发当前 State 对应 Widget 子树 rebuild
  • 不会自动波及兄弟节点
  • 更不会影响父节点

InheritedWidget 依赖变化

finalmodel=context.watch<MyModel>();
  • 当前 widget 订阅了 model
  • model notifyListeners
  • 当前 widget 及其子树 rebuild

这里是列表最容易“炸”的地方。

父 Widget rebuild

这是最容易被忽略、但影响最大的一类

@overrideWidgetbuild(BuildContextcontext){returnColumn(children:[Header(),ListView.builder(...),],);}

如果Columnrebuild:

ListView.builder 会重新执行 build

但注意:

  • 不等于所有 item 都重新 layout
  • 但 itemBuilder 会被重新调用

一个“看似安全,实际危险”的列表写法

classListPageextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalmodel=context.watch<ListModel>();returnListView.builder(itemCount:model.items.length,itemBuilder:(context,index){returnListTile(title:Text(model.items[index].title),);},);}}

表面看起来:

  • ListView 是 builder
  • item 是轻量的

但这里的 rebuild 边界是:

整个 ListPage

只要model.notifyListeners()

  • ListPage rebuild
  • ListView rebuild
  • itemBuilder 被重新执行

在 Debug 下,你会明显感觉到卡顿。

Flutter 列表的“安全边界”到底在哪里

真正合理的 rebuild 边界,应该满足三件事:

  1. 列表容器本身尽量稳定
  2. item 自己决定要不要 rebuild
  3. 状态变化只影响必要的子树

正确拆边界的基本写法

列表容器不订阅业务状态

classListPageextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnListView.builder(itemCount:context.select<ListModel,int>((m)=>m.items.length,),itemBuilder:(context,index){returnItemTile(index:index);},);}}

这里有两个关键点:

  • ListPage 不 watch 整个 model
  • 只关心列表长度

item 自己订阅自己的数据

classItemTileextendsStatelessWidget{finalint index;constItemTile({requiredthis.index});@overrideWidgetbuild(BuildContextcontext){finalitem=context.select<ListModel,Item>((m)=>m.items[index],);returnListTile(title:Text(item.title),);}}

这样 rebuild 边界就变成了:

单个 item

rebuild、layout、paint,不是一回事

这里必须澄清一个非常常见的误解。

rebuild ≠ layout ≠ paint

  • rebuild:重新生成 Widget
  • layout:重新计算大小和位置
  • paint:重新绘制像素

Flutter 在很多情况下:

  • 会 rebuild
  • 但不会 layout
  • 更不会 repaint

而列表性能问题,往往出在 rebuild 范围过大。

为什么你“感觉整个列表都在 rebuild”

因为 Flutter 没有替你做“业务级别”的判断。

它只知道:

  • 哪个 widget 依赖了什么
  • 哪个 widget 变了

它不知道:

“你只是改了一行数据”

所以如果你写的是:

context.watch<ListModel>();

Flutter 的理解是:

“这个 widget 依赖整个 ListModel”

一个更接近真实项目的 Demo 对比

错误边界写法

ChangeNotifierProvider(create:(_)=>ListModel(),child:ListPage(),);
classListPageextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalmodel=context.watch<ListModel>();returnListView.builder(itemCount:model.items.length,itemBuilder:...);}}

任何一条 item 更新,整个列表参与 rebuild。

正确边界写法

classListPageextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalcount=context.select<ListModel,int>((m)=>m.items.length,);returnListView.builder(itemCount:count,itemBuilder:(context,index){returnItemTile(index:index);},);}}

更新单个 item,只影响对应的 ItemTile。

Flutter 为什么不替你“自动做对”

这是一个设计取舍,而不是能力不足。

Flutter 的哲学是:

  • 框架只保证机制正确
  • 不猜测你的业务意图
  • 不偷偷帮你缓存或合并更新

代价就是:

你必须自己划清 rebuild 的边界

一句话总结 rebuild 的真正边界

如果用一句话概括 Flutter 列表 rebuild 的真相:

rebuild 到哪里停,不是 ListView 决定的,而是你把状态放在哪一层决定的。

理解这一点之后:

  • Debug 下的卡顿
  • 大列表掉帧
  • Provider / Riverpod 的性能差异

都会变成可解释、可控制的问题

总结

Flutter 列表性能的天花板,从来不在框架。

它在于:

  • 你是否明确 rebuild 边界
  • 你是否尊重 Widget 子树的更新模型
  • 你是否愿意为“结构清晰”多写一点代码
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 6:32:58

售后完善的高速线剥线机源头厂家

《高速线剥线机哪家好&#xff1a;专业深度测评》开篇&#xff1a;定下基调在工业自动化与智能制造快速发展的今天&#xff0c;高速线剥线机作为电线电缆加工环节中的关键设备&#xff0c;其效率、精度与稳定性直接影响生产节奏与产品质量。面对市场上琳琅满目的品牌与型号&…

作者头像 李华
网站建设 2026/6/13 10:31:09

从入门到精通:程序员必学的9种RAG架构实战指南,解决大模型幻觉问题

本文详细介绍了9种RAG架构及其适用场景&#xff0c;包括标准RAG、对话式RAG、纠正性RAG等&#xff0c;强调应根据实际需求选择合适架构而非盲目追求复杂度。文章提供了从简单开始的决策框架&#xff0c;提醒避免过度设计、忽略检索质量等陷阱。RAG可将语言模型从"自信的谎…

作者头像 李华
网站建设 2026/6/16 5:46:44

StatelessWidget与StatefulWidget区别与使用场景

Flutter核心技术解析&#xff1a;StatelessWidget与StatefulWidget的深度对比与实践指南 引言 在Flutter的世界中&#xff0c;Widget是构建用户界面的基本单元。对于每一个Flutter开发者而言&#xff0c;深刻理解StatelessWidget与StatefulWidget的区别不仅是入门必修课&#x…

作者头像 李华
网站建设 2026/6/10 18:16:58

Java进阶10 IO流

Java进阶10 IO流 IO流就是数据传输。I为input的缩写&#xff0c;表示输入读取&#xff1b;O为out的缩写&#xff0c;表示输出写出。 一、IO流体系结构巧记区分tips&#xff1a;以Stream结尾的是字节流&#xff1b;以Reader、Writer结尾的都是字符流 二、字节输出流FileOutputSt…

作者头像 李华
网站建设 2026/6/4 12:35:44

Java进阶06List集合泛型

Java进阶06 集合 一、集合及其体系结构 集合是一个长度可变的容器 1、集合的体系结构 1.1 单列集合单列集合使用add()方法添加集合元素&#xff0c;一次只能添加一个元素。单列集合均实现了Collection接口&#xff0c;该接口还有两个子接口List和Set。List接口 List集合的特点是…

作者头像 李华