如何在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
相关产品推荐
相关产品推荐

