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

如何在CustomScrollView滚动到底部时触发加载更多函数

实现CustomScrollView带粘性头部列表的加载更多功能

我来帮你解决这个问题,要在带粘性头部的CustomScrollView列表底部触发加载更多,我们需要调整滚动监听逻辑和列表结构,避免嵌套滚动带来的计算问题。下面是一步步的解决方案:

1. 给CustomScrollView添加滚动控制器

首先修改你的AppScaffold组件,允许传入ScrollController,这样我们能监听整个CustomScrollView的滚动事件:

class AppScaffold extends StatelessWidget {
  const AppScaffold({
    Key key,
    @required this.title,
    @required this.slivers,
    this.reverse = false,
    this.scrollController, // 新增控制器参数
  }) : super(key: key);

  final String title;
  final List<Widget> slivers;
  final bool reverse;
  final ScrollController scrollController; // 声明控制器

  @override
  Widget build(BuildContext context) {
    return DefaultStickyHeaderController(
      child: CustomScrollView(
        controller: scrollController, // 绑定控制器
        slivers: slivers,
        reverse: reverse,
      ),
    );
  }
}

2. 在页面中添加滚动监听逻辑

把SoldEntryScreen改成StatefulWidget,因为我们需要管理滚动控制器的生命周期,并且添加滚动监听来判断是否到达底部:

class SoldEntryScreen extends StatefulWidget {
  const SoldEntryScreen({
    Key key,
    this.isDataLoading,
    this.isNextPageAvailable,
    this.transactions,
    this.loadNextPage,
    this.noError,
  }) : super(key: key);

  final bool isDataLoading;
  final bool isNextPageAvailable;
  final transactions;
  final Function loadNextPage;
  final bool noError;

  @override
  State<SoldEntryScreen> createState() => _SoldEntryScreenState();
}

class _SoldEntryScreenState extends State<SoldEntryScreen> {
  final _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    // 给控制器添加滚动监听
    _scrollController.addListener(_onScroll);
  }

  @override
  void dispose() {
    // 销毁控制器,避免内存泄漏
    _scrollController.dispose();
    super.dispose();
  }

  void _onScroll() {
    // 判断是否滚动到接近底部(这里设为距离底部200像素),同时满足加载条件
    final shouldLoad = _scrollController.position.pixels >=
            _scrollController.position.maxScrollExtent - 200 &&
        !widget.isDataLoading &&
        widget.isNextPageAvailable &&
        widget.noError;

    if (shouldLoad) {
      widget.loadNextPage();
    }
  }

  @override
  Widget build(BuildContext context) {
    var history = {};
    widget.transactions.forEach((element) {
      final formatedDate = Utils.dateFormater(element.createdAt);
      if (history[formatedDate['dateClass']] == null) {
        history[formatedDate['dateClass']] = [element];
      } else {
        history[formatedDate['dateClass']].add(element);
      }
    });

    return AppScaffold(
      title: 'List Example',
      scrollController: _scrollController, // 传入控制器
      slivers: [
        SliverToBoxAdapter(child: SoldEntryPage()),
        _StickyHeaderDateSort(history: history),
      ],
    );
  }
}

3. 优化列表结构,避免嵌套滚动问题

原来的代码里用ListView.separated嵌套在SliverToBoxAdapter中,这会导致滚动范围计算不准确。我们把它替换成纯Sliver组件:

修改_StickyHeaderList

class _StickyHeaderList extends StatelessWidget {
  const _StickyHeaderList({
    Key key,
    this.keyTitle,
    this.keyData,
  }) : super(key: key);

  final keyTitle;
  final List keyData;

  @override
  Widget build(BuildContext context) {
    return SliverStickyHeader(
      header: Header(title: keyTitle),
      // 用SliverList实现带分隔符的列表,替代嵌套的ListView
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            return Column(
              children: [
                MoneyTransactionModelListItem(
                  itemIndex: index,
                  transaction: keyData[index],
                ),
                // 最后一个item不显示分隔符
                if (index != keyData.length - 1)
                  Divider(color: Theme.of(context).dividerColor),
              ],
            );
          },
          childCount: keyData.length,
        ),
      ),
    );
  }
}

修改_StickyHeaderDateSort

去掉嵌套的ShrinkWrappingViewport和SliverToBoxAdapter,直接用Sliver组件承载所有分组:

class _StickyHeaderDateSort extends StatelessWidget {
  const _StickyHeaderDateSort({
    Key key,
    this.history,
  }) : super(key: key);

  final Map history;

  @override
  Widget build(BuildContext context) {
    return SliverStickyHeader(
      header: HeaderDatePicker(),
      // 用SliverList生成所有分组的粘性头部列表
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            final key = history.keys.elementAt(index);
            return _StickyHeaderList(keyTitle: key, keyData: history[key]);
          },
          childCount: history.length,
        ),
      ),
    );
  }
}

关键说明

  • 滚动触发阈值(200像素)可以根据你的UI需求调整,让用户在接近底部时就开始加载,提升体验。
  • 必须同时判断isDataLoading、isNextPageAvailable和noError,避免重复加载、无数据时加载,以及错误状态下继续加载。
  • 替换嵌套的ListView为纯Sliver组件,确保CustomScrollView能正确计算整个滚动区域的范围,让滚动监听的判断更准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 09:37:42