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

使用Riverpod和Algolia管理搜索界面状态的问题

问题:Flutter+Algolia+Riverpod日期筛选后结果不更新

现象

开发基于Algolia的搜索界面,支持关键词搜索和日期筛选。修改查询词时结果列表正常更新,但修改开始/结束日期后,结果列表无变化;只有修改日期后再更新查询词,筛选才生效。

所有参数更新时都调用同一代码:

ref.read(comboSearchQueryNotifierProvider.notifier).setQuery(_controller.text, startDate: _startDate.value, endDate: _endDate.value,);

核心代码片段

Riverpod仓库代码

part 'combo_search_repository.g.dart';

class ComboSearchQuery {
  final String query;
  final DateTime? startDate;
  final DateTime? endDate;

  const ComboSearchQuery({
    required this.query,
    this.startDate,
    this.endDate,
  });

  ComboSearchQuery copyWith({
    String? query,
    DateTime? startDate,
    DateTime? endDate,
  }) {
    return ComboSearchQuery(
      query: query ?? this.query,
      startDate: startDate ?? this.startDate,
      endDate: endDate ?? this.endDate,
    );
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is ComboSearchQuery &&
        other.query == query &&
        other.startDate == startDate &&
        other.endDate == endDate;
  }

  @override
  int get hashCode => Object.hash(query, startDate, endDate);
}

class ComboSearchRepository {
  ComboSearchRepository()
      : _multiSearcher = MultiSearcher(
          applicationID: Env.algoliaAppId,
          apiKey: Env.algoliaSearchKey,
      ) {
    _eventsSearcher = _multiSearcher.addHitsSearcher(
      initialState: const SearchState(
        indexName: 'tm_events_index',
      ),
    );
    _usersSearcher = _multiSearcher.addHitsSearcher(
      initialState: const SearchState(
        indexName: 'users',
      ),
    );
  }

  final MultiSearcher _multiSearcher;
  late final HitsSearcher _eventsSearcher;
  late final HitsSearcher _usersSearcher;
  
  Future<List<dynamic>> search(ComboSearchQuery query) async {
  print('Searching in combo repo for: query: ${query.query} startDate: ${query.startDate} endDate: ${query.endDate}');
  if (query.query.isEmpty) {
    print('Search text is empty, returning []');
    return [];
  }

  List<String> filters = [];

  if (query.startDate != null) {
    filters.add('date>=${query.startDate!.millisecondsSinceEpoch}');
  }

  if (query.endDate != null) {
    filters.add('date<=${query.endDate!.millisecondsSinceEpoch}');
  }

  _eventsSearcher.applyState((state) => state.copyWith(
        query: query.query,
        numericFilters: filters,
      ));

  _usersSearcher.applyState((state) => state.copyWith(
        query: query.query
      ));

  final eventResponse = await _eventsSearcher.responses.first;
  final userResponse = await _usersSearcher.responses.first;

  // process stream results (removed for brevity)

  return results;
}


}

@Riverpod(keepAlive: true)
ComboSearchRepository comboSearchRepository(ComboSearchRepositoryRef ref) {
  return ComboSearchRepository();
}

@riverpod
Future<List<dynamic>> comboListSearch(ComboListSearchRef ref, ComboSearchQuery query) async {

  if (query.query.isNotEmpty || query.startDate != null || query.endDate != null) {
    final searchRepository = ref.watch(comboSearchRepositoryProvider);
    return searchRepository.search(query);
  } else {
    return [];
  }
}


@riverpod
Future<List<dynamic>> comboSearchResults(ComboSearchResultsRef ref) {
  final searchQuery = ref.watch(comboSearchQueryNotifierProvider); 
  return ref.watch(comboListSearchProvider(searchQuery).future);
}

查询通知器代码

part 'combo_search_query_notifier.g.dart';

@riverpod
class ComboSearchQueryNotifier extends _$ComboSearchQueryNotifier {
  final _searchQueryController = StreamController<ComboSearchQuery>();
  late final StreamSubscription<ComboSearchQuery> _subscription;

  @override
  ComboSearchQuery build() {
    _subscription = _searchQueryController.stream
    .debounceTime(const Duration(milliseconds: 200))
    .listen(_updateState);

    ref.onDispose(() {
      _searchQueryController.close();
      _subscription.cancel();
    });

    return ComboSearchQuery(query: '', startDate: null, endDate: null);
  }

  void _updateState(ComboSearchQuery query) {
      state = ComboSearchQuery(
        query: query.query,
        startDate: query.startDate,
        endDate: query.endDate,
      );
  }



  void setQuery(String query, {DateTime? startDate, DateTime? endDate}) {

    final newQuery = ComboSearchQuery(
      query: query,
      startDate: startDate,
      endDate: endDate,
    );
    state = newQuery; // Force Riverpod to detect a state change
    
  }

}

@riverpod
Future<List<dynamic>> comboSearchResults(ComboSearchResultsRef ref) {
  final searchQuery = ref.watch(comboSearchQueryNotifierProvider);
  debugPrint('Updated search query in comboSearchResults: $searchQuery');

  return ref.watch(comboListSearchProvider(searchQuery).future);
}

问题原因

  1. Algolia HitsSearcher流缓存:仅修改日期时,_eventsSearcher的查询词未变,responses.first返回的是之前缓存的流结果,没有触发新的搜索请求。
  2. Riverpod重复Provider定义:代码中两次定义comboSearchResultsProvider,导致状态监听逻辑冲突,日期更新的状态未能正确传递到结果列表。
  3. Riverpod缓存复用:comboListSearchProvider作为带参数的FutureProvider,当ComboSearchQuery仅日期变化时,Riverpod可能复用已完成的Future缓存结果。

解决方案

1. 强制Algolia刷新搜索

在search方法中更新筛选条件后,调用refresh()触发新的搜索请求:

Future<List<dynamic>> search(ComboSearchQuery query) async {
  // ... 原有筛选逻辑 ...

  _eventsSearcher.applyState((state) => state.copyWith(
        query: query.query,
        numericFilters: filters,
      ));
  // 强制刷新事件搜索器
  _eventsSearcher.refresh();

  _usersSearcher.applyState((state) => state.copyWith(
        query: query.query
      ));
  // 强制刷新用户搜索器
  _usersSearcher.refresh();

  final eventResponse = await _eventsSearcher.responses.first;
  final userResponse = await _usersSearcher.responses.first;

  // ... 结果处理逻辑 ...
}

2. 移除重复的Provider定义

删除查询通知器代码中重复的comboSearchResultsProvider定义,仅保留Riverpod仓库中的那一个,确保状态监听逻辑唯一。

3. 禁用Riverpod Future缓存

在comboListSearchProvider中禁用缓存,确保参数变化时重新执行搜索:

@riverpod
Future<List<dynamic>> comboListSearch(ComboListSearchRef ref, ComboSearchQuery query) async {
  // 禁用缓存,每次参数变化都重新发起请求
  ref.keepAlive(false);
  
  if (query.query.isNotEmpty || query.startDate != null || query.endDate != null) {
    final searchRepository = ref.watch(comboSearchRepositoryProvider);
    return searchRepository.search(query);
  } else {
    return [];
  }
}

验证步骤

  1. 先删除重复的comboSearchResultsProvider定义
  2. 在search方法中添加refresh()调用
  3. 测试仅修改日期的场景,确认结果列表是否同步更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:29:51