Flutter中如何结合Hits Searcher与无限滚动实现Algolia多索引搜索
实现Algolia多索引搜索结合Flutter Hits Searcher与无限滚动分页
核心思路
用MultiSearcher管理两个索引的搜索实例,同步它们的查询参数(关键词、页码),合并两个索引的搜索结果后传递给PagingController实现无限滚动。如果两个索引存在重复用户,可基于uid字段去重。
修改后的完整代码
import 'dart:async'; import 'package:algolia_helper_flutter/algolia_helper_flutter.dart'; import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; // 假设你的AlgoliaUser模型已定义 class AlgoliaUser { final String uid; final String username; // 其他字段... AlgoliaUser.fromJson(Map<String, dynamic> json) : uid = json['uid'], username = json['username']; } class UserSearchScreen extends StatefulWidget { const UserSearchScreen({super.key}); @override State<UserSearchScreen> createState() => _UserSearchScreenState(); } class _UserSearchScreenState extends State<UserSearchScreen> { final TextEditingController _searchTextEditingController = TextEditingController(); Timer? _debounce; // 使用MultiSearcher管理多索引搜索 final MultiSearcher _multiSearcher = MultiSearcher( applicationID: 'myAppID', apiKey: 'myApiKey', ); // 初始化两个索引的搜索器 late final HitsSearcher _usersSearcher = _multiSearcher.addHitsSearcher( indexName: 'users', ); late final HitsSearcher _anotherUsersSearcher = _multiSearcher.addHitsSearcher( indexName: 'another_users', // 替换为你的第二个索引名称 ); final PagingController<int, AlgoliaUser> _pagingController = PagingController(firstPageKey: 0); // 合并两个索引的搜索结果流,处理分页和去重 Stream<HitsPage<AlgoliaUser>> get _combinedSearchPage { return CombineLatestStream.combine2( _usersSearcher.responses, _anotherUsersSearcher.responses, (Response usersResp, Response anotherUsersResp) { // 转换为用户模型 final users = usersResp.hits.map((hit) => AlgoliaUser.fromJson(hit)).toList(); final anotherUsers = anotherUsersResp.hits.map((hit) => AlgoliaUser.fromJson(hit)).toList(); // 合并并去重(基于uid) final combined = <String, AlgoliaUser>{}; for (var user in [...users, ...anotherUsers]) { combined[user.uid] = user; } final uniqueUsers = combined.values.toList(); // 计算下一页页码:任意一个索引还有结果则返回下一页 final hasMoreUsers = usersResp.nbPages > usersResp.page + 1; final hasMoreAnotherUsers = anotherUsersResp.nbPages > anotherUsersResp.page + 1; final actualNextPageKey = hasMoreUsers || hasMoreAnotherUsers ? usersResp.page + 1 : null; return HitsPage( items: uniqueUsers, pageKey: usersResp.page, nextPageKey: actualNextPageKey, ); }, ); } @override void initState() { super.initState(); // 监听合并后的搜索结果流,更新分页控制器 _combinedSearchPage.listen((page) { if (page.pageKey == 0) { _pagingController.refresh(); } _pagingController.appendPage(page.items, page.nextPageKey); }).onError((error) => _pagingController.error = error); // 监听分页请求,同步更新两个索引的页码 _pagingController.addPageRequestListener((pageKey) { _usersSearcher.applyState((state) => state.copyWith(page: pageKey)); _anotherUsersSearcher.applyState((state) => state.copyWith(page: pageKey)); }); } @override void dispose() { _multiSearcher.dispose(); _pagingController.dispose(); _searchTextEditingController.dispose(); _debounce?.cancel(); super.dispose(); } void _onSearchChanged(String query) { if (_debounce?.isActive ?? false) { _debounce?.cancel(); } _debounce = Timer(const Duration(milliseconds: 500), () { // 同步更新两个索引的查询关键词和重置页码 _usersSearcher.applyState((state) => state.copyWith( query: query, page: 0, )); _anotherUsersSearcher.applyState((state) => state.copyWith( query: query, page: 0, )); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: TextField( controller: _searchTextEditingController, onChanged: _onSearchChanged, decoration: const InputDecoration( hintText: '搜索用户...', border: InputBorder.none, ), ), ), body: PagedListView<int, AlgoliaUser>( pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate<AlgoliaUser>( itemBuilder: (context, item, index) { return ListTile( title: Text(item.username), subtitle: Text(item.uid), ); }, firstPageErrorIndicatorBuilder: (context) => const Text('加载失败'), noItemsFoundIndicatorBuilder: (context) => const Text('没有找到用户'), ), ), ); } } // 保持你原有的HitsPage和SearchMetadata类(如果需要) class HitsPage<T> { final List<T> items; final int pageKey; final int? nextPageKey; HitsPage({ required this.items, required this.pageKey, required this.nextPageKey, }); } class SearchMetadata { SearchMetadata.fromResponse(Response response); }
关键说明
- MultiSearcher管理多索引:替代单索引的HitsSearcher,统一管理多个索引的搜索实例,确保API调用的一致性。
- 结果合并与去重:通过
CombineLatestStream合并两个索引的响应流,利用uid字段去重避免重复用户展示。 - 同步分页与查询:分页请求时同步更新两个索引的页码;搜索关键词变化时,同时重置两个索引的查询参数。
- 分页逻辑控制:只有当任意一个索引还有未加载的结果时,才返回下一页页码,避免无效请求。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

