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

如何基于Riverpod的FutureProvider实现infinite_scroll_pagination的分页与搜索

在infinite_scroll_pagination中结合Riverpod实现搜索驱动的无限滚动

要实现带搜索查询的无限滚动,核心是让分页逻辑能响应搜索条件的变化(自动重置分页、重新加载数据),我们可以用StateNotifierProvider来管理分页状态,同时监听搜索和类型的StateProvider,具体实现如下:

1. 定义分页状态类

首先创建一个类来保存分页相关的所有状态:

class AgentPaginationState {
  const AgentPaginationState({
    required this.items,
    required this.currentPage,
    required this.hasMore,
    required this.isLoading,
  });

  // 已加载的列表数据
  final List<AgentPreviewModel> items;
  // 当前页码
  final int currentPage;
  // 是否还有更多数据可加载
  final bool hasMore;
  // 是否正在加载中
  final bool isLoading;

  // 拷贝方法,用于更新状态
  AgentPaginationState copyWith({
    List<AgentPreviewModel>? items,
    int? currentPage,
    bool? hasMore,
    bool? isLoading,
  }) {
    return AgentPaginationState(
      items: items ?? this.items,
      currentPage: currentPage ?? this.currentPage,
      hasMore: hasMore ?? this.hasMore,
      isLoading: isLoading ?? this.isLoading,
    );
  }
}

2. 创建StateNotifier管理分页逻辑

实现一个StateNotifier,负责处理加载更多、监听搜索条件变化重置分页的逻辑:

class AgentPaginationNotifier extends StateNotifier<AgentPaginationState> {
  AgentPaginationNotifier(this.ref)
      : super(const AgentPaginationState(
          items: [],
          currentPage: 1,
          hasMore: true,
          isLoading: false,
        )) {
    // 监听搜索查询和类型变化,一旦变化就重置分页并重新加载第一页
    ref.listen(allAgentSearchQueryProvider, (_, __) => _resetAndLoadFirstPage());
    ref.listen(agentTypeQueryProvider, (_, __) => _resetAndLoadFirstPage());
  }

  final Ref ref;
  // 假设每页加载的数量
  static const _pageSize = 20;

  // 加载更多数据
  Future<void> loadMore() async {
    if (state.isLoading || !state.hasMore) return;

    state = state.copyWith(isLoading: true);
    try {
      final query = ref.read(allAgentSearchQueryProvider);
      final type = ref.read(agentTypeQueryProvider);
      final api = ref.read(agentAPIProvider);

      final newItems = await api.getAgentsByPage(
        query: query,
        type: type,
        page: state.currentPage,
        pageSize: _pageSize,
      );

      if (newItems.length < _pageSize) {
        // 返回的数据不足一页,说明没有更多数据了
        state = state.copyWith(
          items: [...state.items, ...newItems],
          hasMore: false,
          isLoading: false,
        );
      } else {
        state = state.copyWith(
          items: [...state.items, ...newItems],
          currentPage: state.currentPage + 1,
          isLoading: false,
        );
      }
    } catch (e) {
      // 处理加载错误,这里可以根据需求添加错误状态
      state = state.copyWith(isLoading: false);
      rethrow;
    }
  }

  // 重置分页并加载第一页
  Future<void> _resetAndLoadFirstPage() async {
    state = const AgentPaginationState(
      items: [],
      currentPage: 1,
      hasMore: true,
      isLoading: false,
    );
    await loadMore();
  }
}

3. 定义StateNotifierProvider

final agentPaginationProvider = StateNotifierProvider<AgentPaginationNotifier, AgentPaginationState>((ref) {
  return AgentPaginationNotifier(ref);
});

4. UI层结合infinite_scroll_pagination使用

用PagedListView来实现无限滚动,结合Riverpod监听分页状态:

class AgentListScreen extends ConsumerWidget {
  const AgentListScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final paginationState = ref.watch(agentPaginationProvider);
    final notifier = ref.read(agentPaginationProvider.notifier);

    return PagedListView<int, AgentPreviewModel>(
      pagingController: PagingController(firstPageKey: 1)
        ..addPageRequestListener((pageKey) {
          // 触发加载更多
          notifier.loadMore();
        }),
      builderDelegate: PagedChildBuilderDelegate<AgentPreviewModel>(
        itemBuilder: (context, item, index) => AgentPreviewCard(item: item),
        firstPageErrorIndicatorBuilder: (context) => const Text('加载失败,请重试'),
        noItemsFoundIndicatorBuilder: (context) => const Text('没有找到匹配的代理人'),
        newPageErrorIndicatorBuilder: (context) => const Text('加载更多失败,请重试'),
      ),
    );
  }
}

关键逻辑说明

  • 当搜索查询或类型变化时,StateNotifier中的listen会触发_resetAndLoadFirstPage,重置分页状态并重新加载第一页数据。
  • loadMore方法会检查当前是否正在加载或是否还有更多数据,避免重复请求。
  • 后端需要支持分页接口(根据page和pageSize返回对应数据),如果你的getAllAgents原本是返回全部数据,需要调整为分页版本。

内容的提问来源于stack exchange,提问作者Tanvir Ibn Mizan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 22:38:23