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

Flutter Infinite Scroll Pagination自动加载下一页而非滚动触发问题

问题

使用infinite_scroll_pagination包构建PagedGridView时,预期首次加载9个颜色块网格项,用户滚动到页面底部时再触发分页监听器请求更多数据(直到达到总数据量)。但当前无需触发滚动行为,PagedGridView就会自动按pageSize(9)增量加载所有数据,这会导致后续对接数据库时产生不必要的读取操作。

当前布局是将PagedGridView<int, Color>作为ListView的子组件,并用RefreshIndicator包裹页面主体以支持手动刷新。

解决方案

问题根源

  1. PagedGridView设置了shrinkWrap: true和physics: NeverScrollableScrollPhysics(),导致它失去自身滚动感知能力,外层ListView会一次性渲染所有内容,触发分页控制器持续加载数据。
  2. 分页逻辑中的终止条件判断不准确,pageKey > _colors.length无法正确识别是否已加载完所有数据。

修改步骤

  1. 调整布局结构:移除PagedGridView的shrinkWrap和physics配置,将外层ListView替换为CustomScrollView,把顶部Card转为SliverToBoxAdapter组件,与PagedGridView的sliver版本结合,既保留顶部固定内容,又让网格正常监听滚动。
  2. 修正分页终止逻辑:调整_fetchPage中的判断条件,确保当起始加载位置超过总数据长度时,直接标记为最后一页。

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';

class InfiniteScroll extends StatefulWidget {
  const InfiniteScroll({super.key});

  @override
  State<InfiniteScroll> createState() => _InfiniteScrollState();
}

class _InfiniteScrollState extends State<InfiniteScroll> {
  final List<Color> _colors = List.generate(
    100,
    (index) => Color(index * 0xFFFFFF ~/ 1000).withOpacity(0.5),
  );

  static const int _pageSize = 9;
  final PagingController<int, Color> _pagingController =
      PagingController(firstPageKey: 0);

  @override
  void initState() {
    _pagingController.addPageRequestListener(_fetchPage);
    super.initState();
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      // 修正终止条件:当前起始位置已超过总数据量,直接返回空的最后一页
      if (pageKey >= _colors.length) {
        _pagingController.appendLastPage([]);
        return;
      }
      final newItems = await Future.wait(List.generate(_pageSize,
              (index) => Future.delayed(const Duration(milliseconds: 50))))
          .then((_) => _colors.skip(pageKey).take(_pageSize).toList());

      final isLastPage = pageKey + newItems.length >= _colors.length;
      if (isLastPage) {
        _pagingController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingController.appendPage(newItems, nextPageKey);
      }
    } catch (e) {
      _pagingController.error = e;
      debugPrint(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () async => _pagingController.refresh(),
        // 替换为CustomScrollView,结合sliver组件实现混合布局
        child: CustomScrollView(
          slivers: [
            SliverToBoxAdapter(
              child: Card(
                child: ListTile(
                  title: const Text("Infinite Scroll Pagination"),
                  subtitle: const Text("Infinite Scroll Pagination"),
                  trailing: const Icon(Icons.arrow_forward_ios),
                ),
              ),
            ),
            // 使用PagedGridView的sliver版本,配合CustomScrollView工作
            PagedSliverGridView<int, Color>(
              pagingController: _pagingController,
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                mainAxisSpacing: 4.0,
                crossAxisSpacing: 4.0,
              ),
              builderDelegate: PagedChildBuilderDelegate<Color>(
                itemBuilder: (context, item, index) {
                  return Container(
                    color: item,
                    child: InkWell(
                      onTap: () {
                        debugPrint("Color: $item");
                      },
                      child: Center(
                        child: Text(
                          index.toString(),
                          style: const TextStyle(fontSize: 20),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _pagingController.dispose();
    super.dispose();
  }
}

说明

  • 使用PagedSliverGridView替代PagedGridView,配合CustomScrollView实现顶部固定内容+滚动网格的混合布局。
  • 修正后的_fetchPage逻辑会准确判断是否已加载完所有数据,避免不必要的请求触发。
  • 移除了NeverScrollableScrollPhysics,让网格自身处理滚动事件,只有当用户滚动到底部时才会触发下一页数据加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:57:14