You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中能否实现Sliver懒加载?SliverMainAxisGroup全量构建问题

带粘性头部的Sliver懒加载实现问题

问题场景

原本使用SliverList.builder可实现列表懒加载,但为添加粘性头部改用SliverMainAxisGroup后,所有Section及其下的Item被一次性全部构建,无法实现懒加载,询问是否有可行的实现方案。

原UI代码

CustomScrollView(
  physics: const BouncingScrollPhysics(),
  controller: controller,
  slivers: [
    for (final section in _data)
      SliverMainAxisGroup(
        slivers: [
          PinnedHeaderSliver(
            child: Builder(
              builder: (context) {
                return Container(
                  height: 64,
                  alignment: Alignment.center,
                  color: Colors.red.withOpacity(1),
                  child: Text('DATE HEADER'),
                );
              }
            ),
          ),
          SliverList.builder(
            itemBuilder: (_, i) {
              return ListTile(
                title: Text(section.items[i].content),
              );
            },
            itemCount: section.items.length,
          ),
        ],
      ),
  ],
);

原数据模型代码

final _data = List.generate(
  500,
  (index) => Section(
    'Section $index',
    List.generate(
      100,
      (index) => Item('Item $index'),
    ),
  ),
);

final class Section {
  Section(
    this.title,
    this.items,
  );

  final List<Item> items;
  final String title;

  @override
  String toString() {
    return 'Section{title: $title}';
  }
}

final class Item {
  Item(this.content);
  final String content;

  @override
  String toString() {
    return 'Item{content: $content}';   
  }
}

解决方案:原生Flutter实现懒加载+粘性头部

无需依赖SliverMainAxisGroup,直接用SliverPersistentHeader实现粘性头部,结合SliverChildBuilderDelegate动态构建所有列表项(包括头部),即可实现全局懒加载。

修改后的代码

CustomScrollView(
  physics: const BouncingScrollPhysics(),
  controller: controller,
  slivers: [
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          // 计算当前index对应的Section和位置
          int sectionIndex = 0;
          int itemIndex = index;
          while (sectionIndex < _data.length) {
            // 每个Section占1个头部位置 + N个列表项位置
            final sectionTotalCount = 1 + _data[sectionIndex].items.length;
            if (itemIndex < sectionTotalCount) break;
            itemIndex -= sectionTotalCount;
            sectionIndex++;
          }

          if (sectionIndex >= _data.length) return null;

          // 构建粘性头部
          if (itemIndex == 0) {
            return SliverPersistentHeader(
              pinned: true,
              delegate: _SectionHeaderDelegate(title: _data[sectionIndex].title),
            );
          }
          // 构建列表项
          else {
            final targetItem = _data[sectionIndex].items[itemIndex - 1];
            return ListTile(title: Text(targetItem.content));
          }
        },
        // 计算总条目数:所有Section头部数 + 所有Item数
        childCount: _data.length + _data.fold(0, (sum, section) => sum + section.items.length),
      ),
    ),
  ],
);

// 自定义粘性头部代理类
class _SectionHeaderDelegate extends SliverPersistentHeaderDelegate {
  const _SectionHeaderDelegate({required this.title});

  final String title;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      height: 64,
      alignment: Alignment.center,
      color: Colors.red.withOpacity(1),
      child: Text(title),
    );
  }

  @override
  double get maxExtent => 64;

  @override
  double get minExtent => 64;

  @override
  bool shouldRebuild(covariant _SectionHeaderDelegate oldDelegate) {
    return oldDelegate.title != title;
  }
}

实现说明

  • 动态索引计算:通过遍历Section,确定当前滚动位置对应的是某个Section的头部还是列表项,仅当视图滚动到对应位置时才触发构建,实现懒加载。
  • 原生粘性头部:利用SliverPersistentHeader的pinned: true属性实现头部粘性效果,无需额外第三方组件。
  • 性能优化:所有UI元素仅在进入视口时才会被构建,避免一次性加载全部500个Section和50000个Item的性能问题。

内容的提问来源于stack exchange,提问作者Nagual

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 10:05:58