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

如何实现Flutter CustomScrollView的内容懒渲染以解决性能问题

CustomScrollView内容懒渲染解决方案

你当前直接传入预构建的Sliver列表到CustomScrollView的slivers参数中,Flutter会在CustomScrollView初始化阶段就完成所有Sliver的布局与渲染计算,当Sliver数量较多时就会出现性能瓶颈。

可行实现方案

方案1:整合可复用条目到SliverList.builder(推荐)

如果你的items列表中大部分是SliverToBoxAdapter包裹的普通组件,优先做条目整合:

  • 把所有单条的SliverToBoxAdapter条目抽取出来,统一放到SliverList.builder中实现懒加载
  • 仅把少量固定的特殊Sliver(比如SliverAppBar、SliverPersistentHeader等)放在slivers列表的固定位置

代码示例:

Widget _build({required List<Widget> specialSlivers, required List<Widget> normalItems}) {
  return ScrollListener(
    onScroll: (e) => onScroll?.call(),
    onEnd: () => onScroll?.call(),
    child: CustomScrollView(
      physics: const AlwaysScrollableScrollPhysics(),
      controller: pageScrollController,
      slivers: [
        // 固定的头部特殊Sliver
        ...specialSlivers,
        // 大量普通条目用SliverList.builder懒加载
        SliverList.builder(
          itemCount: normalItems.length,
          itemBuilder: (context, index) => normalItems[index],
        ),
      ],
    ),
  );
}

方案2:自定义懒加载Sliver包装器(适配异构Sliver场景)

如果你的items列表确实是大量无法合并的异构Sliver(比如每个条目都是独立的SliverGrid、SliverPersistentHeader等),可以实现通用的懒加载Sliver包装组件:

  • 组件内部监听滚动位置,仅当Sliver即将进入视口时才构建真实的Sliver内容
  • 未触发构建时返回固定高度的占位Sliver,避免布局偏移

代码示例:

class LazySliver extends StatefulWidget {
  final double estimatedHeight;
  final Widget Function() sliverBuilder;

  const LazySliver({super.key, required this.estimatedHeight, required this.sliverBuilder});

  @override
  State<LazySliver> createState() => _LazySliverState();
}

class _LazySliverState extends State<LazySliver> {
  bool _isBuilt = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final scrollPosition = Scrollable.of(context).position;
    final viewportHeight = scrollPosition.viewportDimension;
    final currentOffset = scrollPosition.pixels;
    // 预加载2屏范围,接近视口时提前构建避免白屏
    if (currentOffset + viewportHeight * 2 > widget.estimatedHeight) {
      _isBuilt = true;
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isBuilt) return widget.sliverBuilder();
    return SliverToBoxAdapter(child: SizedBox(height: widget.estimatedHeight));
  }
}

使用时把你原来的Sliver列表做一层包装即可:

Widget _build({required List<Widget> items}) {
  final lazySlivers = items.map((item) => LazySliver(
    // 替换为对应Sliver的实际预估高度
    estimatedHeight: 300,
    sliverBuilder: () => item,
  )).toList();

  return ScrollListener(
    onScroll: (e) => onScroll?.call(),
    onEnd: () => onScroll?.call(),
    child: CustomScrollView(
      physics: const AlwaysScrollableScrollPhysics(),
      controller: pageScrollController,
      slivers: lazySlivers,
    ),
  );
}

优化提示

可以配合CustomScrollView的cacheExtent参数调整预加载的范围,平衡加载速度和内存占用,进一步优化滚动性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 19:45:04