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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:27:13