Flutter Riverpod流更新触发两次:如何仅返回新过滤列表?
问题
过滤器变更时,filteredOrdersStream本该只返回新的过滤后列表,但绑定的监听器会触发两次——一次返回旧值,一次返回新值,且无法区分二者。已确认tourFilterProvider仅触发一次,问题出在filteredOrdersStream的定义上。
原因分析
当tourFilterProvider变更时,filteredOrdersStreamProvider会重新生成一个全新的流。Riverpod默认会先把旧流的最后一个值作为过渡状态发送一次,再发送新流的第一个值,这就导致监听器收到两次回调。
解决方案
可以通过两种方式修改filteredOrdersStreamProvider,避免触发两次回调:
方案1:用异步生成器(async*)构建流
通过异步生成器同时监听过滤器和订单流的变化,确保每次只输出基于最新过滤器的结果:
@riverpod Stream<List<Order>> filteredOrdersStream(FilteredOrdersStreamRef ref) async* { final repository = ref.watch(ordersRepositoryProvider); // 监听过滤器的变化流 await for (final filter in ref.watch(tourFilterProvider).stream) { // 监听订单流,每次订单更新或过滤器变更时,输出过滤后的结果 await for (final orders in repository.watchOrders()) { yield filter.isEmpty ? orders : orders.where((order) => filter.contains(order.tour)).toList(); } } }
方案2:用switchMap合并流
将过滤器的流与订单流通过switchMap合并,过滤器变更时自动切换到新的过滤逻辑,不会残留旧流的值:
@riverpod Stream<List<Order>> filteredOrdersStream(FilteredOrdersStreamRef ref) { final repository = ref.watch(ordersRepositoryProvider); // 把过滤器转为流,再通过switchMap关联订单流的过滤逻辑 return ref.watch(tourFilterProvider).stream.switchMap((filter) { return repository.watchOrders().map((orders) { return filter.isEmpty ? orders : orders.where((order) => filter.contains(order.tour)).toList(); }); }); }
额外优化(可选)
如果不想修改流提供者,也可以在消费者侧添加值对比,忽略重复的旧值:
ref.listen<AsyncValue<List<Order>>>(filteredOrdersStreamProvider, (previous, state) async { // 仅当状态有值,且新值与旧值不同时执行操作 if (state.hasValue && state.value != previous?.value) { final filteredOrders = state.value!; // 执行你的操作 } });
内容的提问来源于stack exchange,提问作者duffy
相关产品推荐
相关产品推荐

