在这里插入图片描述
在这里插入图片描述

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

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

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

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

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

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

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

很多人在项目里第一次把 ListView 换成 CustomScrollView + SliverList 后,都会有一个共同感受:

好像也没怎么优化,但滚动更稳了。

这背后不是魔法,而是 Flutter 在 Sliver 体系里,换了一种组织渲染的方式

Sliver 不是“更快的 ListView”

  • Sliver ≠ 更快的 ListView
  • Sliver ≠ 神奇性能组件

它真正不一样的地方在于:

Sliver 把“滚动上下文”和“内容渲染”彻底拆开了。

ListView 的真实结构,比你想象得“厚”

从源码角度看,一个 ListView 本质上是:

Scrollable
 └─ Viewport
    └─ SliverList

也就是说:

ListView 只是 Sliver 体系的一个“封装版入口”。

那为什么直接用 ListView,rebuild 更容易失控?

ListView 的问题,不在 List,而在“整体感”

ListView 给你的,是一个整体组件的错觉

ListView.builder(
  itemBuilder: ...
)

看起来:

  • 这是一个列表
  • item 是它的子节点

但在 rebuild 关系上:

ListView 是一个整体 Widget

只要 ListView rebuild:

  • Viewport 会重新参与计算
  • SliverList 会重新评估
  • itemBuilder 可能被重新触发

Sliver 的设计,从一开始就不是“整体列表”

Sliver 的核心思想是:

滚动区域中,每一段内容,都是独立参与布局和渲染的。

来看一个典型结构:

CustomScrollView(
  slivers: [
    SliverToBoxAdapter(child: Header()),
    SliverList(delegate: ...),
    SliverToBoxAdapter(child: Footer()),
  ],
);

这里有一个非常重要的变化:

  • Header 是一个 sliver
  • List 是一个 sliver
  • Footer 也是一个 sliver

它们不是 children,而是:

滚动流中的独立单元

Sliver 是如何“切断 rebuild 传播路径”的

在 Sliver 体系里:

  • 每一个 sliver 都有自己的 layout / paint 边界
  • Sliver 之间不会互相影响
  • rebuild 更容易被限制在 sliver 内部

也就是说:

Header rebuild,不会影响 List 的滚动布局

List 内 item rebuild,不会波及其他 sliver

一个最常见的对比例子

ListView 写法

Column(
  children: [
    Header(),
    Expanded(
      child: ListView.builder(
        itemBuilder: ...
      ),
    ),
  ],
);

问题在于:

  • Column rebuild
  • ListView 作为子节点重新 build
  • 滚动上下文被整体触发

Sliver 写法

CustomScrollView(
  slivers: [
    SliverToBoxAdapter(child: Header()),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => ItemTile(index: index),
      ),
    ),
  ],
);

这里:

  • Header 是独立 sliver
  • List 是独立 sliver
  • rebuild 的传播路径被切断

SliverChildBuilderDelegate 的“天然边界感”

SliverChildBuilderDelegateListView.builder 看起来很像,但有一个隐含差异:

它是为“按需生成、按需回收”设计的。

它天然假设:

  • child 是不稳定的
  • child 会频繁进入/离开视口
  • child 必须是可被安全重建的

这反而倒逼你:

  • item 写得更轻
  • 状态更干净
  • 副作用更少

Sliver + key,对状态边界更友好

在 Sliver 场景下,key 的意义会被放大。

SliverList(
  delegate: SliverChildBuilderDelegate(
    (context, index) {
      final item = items[index];
      return ItemTile(
        key: ValueKey(item.id),
        item: item,
      );
    },
  ),
);

因为:

  • Sliver 会更激进地复用 Element
  • key 是你唯一能“声明身份”的方式

没有 key,状态错位的概率会比 ListView 更高。

为什么 Sliver 更“逼你写对”

ListView 很“宽容”

  • item 写重了,也能跑
  • rebuild 大一点,也不立刻炸
  • Debug 下卡,Release 下可能还能接受

Sliver 很“严格”

  • item 不干净,马上暴露问题
  • 状态放错位置,很快掉帧
  • rebuild 边界一眼就能看出来

但这恰恰是它的优势。

Sliver 更适合复杂、长生命周期页面

当你的页面出现以下特征时,Sliver 的优势会越来越明显:

  • 多段列表
  • Header / Tab / 吸顶
  • 局部刷新
  • 页面常驻

因为它的结构本身就在表达:

哪些内容在滚动中是独立存在的。

一个“更安全”的 Sliver 结构示例

CustomScrollView(
  slivers: [
    SliverToBoxAdapter(
      child: HeaderView(),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return ItemTile(index: index);
        },
        childCount: items.length,
      ),
    ),
  ],
);

配合之前讲过的:

  • item 自己订阅状态
  • sliver 本身不 watch 大模型

rebuild 边界会非常清晰。

Sliver 的优势

Sliver 不是让你“更快”,而是让你“更不容易写错”。

它通过结构本身:

  • 限制 rebuild 传播
  • 切割滚动影响面
  • 强化状态边界

总结

很多 Flutter 列表性能问题,并不是你不会优化,而是:

你用的是一个“整体感太强”的容器。

Sliver 给你的,不是性能魔法,而是一种更接近底层真实模型的表达方式

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐