

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学"明白",也用"到位"
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 这套"安全写法"要解决什么问题
- 列表容器不承载业务状态
- [item 是 rebuild 的最小战斗单位](#item 是 rebuild 的最小战斗单位)
-
- [Item 必须满足三点](#Item 必须满足三点)
- [推荐的 Item 写法](#推荐的 Item 写法)
- [能 Stateless,就不要 Stateful](#能 Stateless,就不要 Stateful)
-
- [Stateful item 的隐性成本](#Stateful item 的隐性成本)
- 如果你真的需要局部状态
- [key 不是可选项](#key 不是可选项)
-
- [正确使用 key](#正确使用 key)
- [列表 item 里不做"异步决策"](#列表 item 里不做“异步决策”)
- [页面复杂了,优先 Sliver](#页面复杂了,优先 Sliver)
- [keepAlive 是"高级权限"](#keepAlive 是“高级权限”)
- ["长期安全"的完整 Demo 结构](#“长期安全”的完整 Demo 结构)
- 长期"安全"的原因
- 总结
大多数 Flutter 列表性能问题,不是"写错了",而是写得太随意了。
它们在 Demo 里没问题、在首版也没问题、在数据量小的时候更没问题,但一旦:
- 页面常驻
- 状态变复杂
- 列表变长
- 需求开始叠加
问题一定会出现。
这套"安全写法"要解决什么问题
我们先明确目标,不是为了"更快",而是为了:
- rebuild 范围可预测
- 滚动成本稳定
- 状态位置清晰
- 后期改需求不崩盘
总结:
让列表的性能,和业务复杂度"线性增长",而不是指数爆炸。
列表容器不承载业务状态
永远不要这样写
dart
class ListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = context.watch<ListModel>();
return ListView.builder(
itemCount: model.items.length,
itemBuilder: ...
);
}
}
问题不在 ListView,而在这句:
dart
context.watch<ListModel>()
这等于在告诉 Flutter:
"整个列表,依赖整个业务模型。"
长期安全写法
dart
class ListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final count = context.select<ListModel, int>(
(m) => m.items.length,
);
return ListView.builder(
itemCount: count,
itemBuilder: (context, index) {
return ItemTile(index: index);
},
);
}
}
列表容器只关心结构,不关心内容。
item 是 rebuild 的最小战斗单位
Flutter 列表的"安全边界",是 item,而不是列表。
Item 必须满足三点
- 足够小
- 足够纯
- 状态明确
推荐的 Item 写法
dart
class ItemTile extends StatelessWidget {
final int index;
const ItemTile({required this.index});
@override
Widget build(BuildContext context) {
final item = context.select<ListModel, Item>(
(m) => m.items[index],
);
return ListTile(
title: Text(item.title),
subtitle: Text(item.subtitle),
);
}
}
rebuild 边界非常明确:
只有这个 item 的数据变了,它才 rebuild。
能 Stateless,就不要 Stateful
这是 Flutter 列表里一个长期收益极高的习惯。
Stateful item 的隐性成本
- 状态容易被 keepAlive
- Element 复用时更复杂
- 错位问题更隐蔽
很多"诡异 Bug",都来自 Stateful item。
如果你真的需要局部状态
比如:
- 是否展开
- 是否选中
- 动画进度
明确声明它的生命周期
dart
class ExpandableItem extends StatefulWidget {
final Item item;
const ExpandableItem({required this.item});
@override
State<ExpandableItem> createState() => _ExpandableItemState();
}
class _ExpandableItemState extends State<ExpandableItem> {
bool expanded = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
ListTile(
title: Text(widget.item.title),
onTap: () {
setState(() => expanded = !expanded);
},
),
if (expanded) Text(widget.item.detail),
],
);
}
}
并且默认不 keepAlive。
key 不是可选项
在长期维护的列表中:
没有 key 的列表,迟早出事。
正确使用 key
dart
return ItemTile(
key: ValueKey(item.id),
index: index,
);
不要用:
- index
- hashCode
- runtimeType
key 的本质是身份,而不是位置。
列表 item 里不做"异步决策"
危险写法
dart
itemBuilder: (context, index) {
return FutureBuilder(
future: fetchExtra(items[index].id),
builder: ...
);
}
问题不是 FutureBuilder,而是:
- 滚动时会频繁触发
- build 不再是纯函数
- Debug 下性能极差
长期安全替代方案
- 异步逻辑提前到 model
- item 只展示状态
dart
class Item {
final String title;
final String? extra;
}
页面复杂了,优先 Sliver
当你出现以下需求时:
- Header + 列表
- 吸顶
- 多段列表
- 页面常驻
不要犹豫,直接 Sliver。
推荐结构模板
dart
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: HeaderView(),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ItemTile(index: index);
},
childCount: count,
),
),
],
);
Sliver 的好处不是快,而是:
- rebuild 传播路径短
- 结构天然清晰
- 后期加需求不翻车
keepAlive 是"高级权限"
AutomaticKeepAliveClientMixin
在列表里,永远是最后考虑的选项。
问问自己三个问题
- 这个状态真的值得活着吗?
- 离开屏幕后还能复用吗?
- 内存上涨你能接受吗?
如果有一个答不上来:
不要 keepAlive。
"长期安全"的完整 Demo 结构
dart
class ListModel extends ChangeNotifier {
final List<Item> items = [];
void updateItem(int index, Item newItem) {
items[index] = newItem;
notifyListeners();
}
}
dart
class ListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final count = context.select<ListModel, int>(
(m) => m.items.length,
);
return CustomScrollView(
slivers: [
SliverToBoxAdapter(child: HeaderView()),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ItemTile(
key: ValueKey(
context.read<ListModel>().items[index].id,
),
index: index,
);
},
childCount: count,
),
),
],
);
}
}
长期"安全"的原因
总结一下它解决了什么:
- rebuild 边界清晰
- item 粒度稳定
- 滚动成本可控
- 状态不扩散
- Debug / Release 行为一致
总结
Flutter 列表性能,从来不是靠"技巧堆出来的"。
真正能活得久的列表,只有一个特征:
结构清楚,状态克制。
当你把这套写法变成肌肉记忆之后:
- Debug 下你会更安心
- 需求改动你会更从容
- 性能问题会变成"可以推理的问题"