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

Riverpod provider仅调用一次:后续访问返回缓存数据的原因是什么?

理解偏差与问题根因
  1. autoDispose 销毁逻辑认知偏差
    autoDispose 修饰的provider默认不会在最后一个观察者移除后立刻销毁,会保留5秒的宽限期,用于适配短时间内页面重建、路由跳转的场景,避免频繁销毁重建导致的性能损耗。如果你的页面跳转间隔小于5秒,第一个页面退出触发观察者移除后,第二个页面在宽限期内又监听了同一个provider实例(参数相同),就会直接复用之前缓存的Future结果,不会重新执行Firestore查询逻辑,导致拿到旧数据。
  2. FutureProvider 特性限制
    你当前使用的FutureProvider只会在实例创建时执行一次异步逻辑,之后只要实例不销毁,就会一直缓存执行结果,不会主动跟踪Firestore缓存的变更,哪怕本地hasPendingWrites状态已经更新,provider也不会自动刷新。
解决方案

你可以根据业务需求选择对应方案:

  • 如果仅需要每次进入页面时查询最新的同步状态,可手动取消autoDispose的宽限期,在provider逻辑中添加立刻销毁的配置:
final pendingUploadsCounterProvider =
    FutureProvider.autoDispose.family<int, List<Report>>((ref, reports) async {
  // 新增:取消监听时立刻销毁当前provider实例,不保留缓存
  ref.onCancel(() => ref.invalidateSelf());
  final db = ref.read(firebaseServiceProvider).db;
  final reportIds = reports.map((e) => e.id).toList();

  final documents = await Future.wait([
    for (final name in kReportTypes)
      db
          .collection(name)
          .where('report_id', whereIn: reportIds)
          .get(GetOptions(source: Source.cache))
  ]);

  return documents.where((event) => event.metadata.hasPendingWrites).length;
});
  • 如果需要实时同步pendingWrites状态,建议改用StreamProvider监听Firestore快照,避免每次进页面都手动查询,所有页面共用同一份实时数据:
import 'package:stream_channel/stream_channel.dart';

final pendingUploadsCounterProvider =
    StreamProvider.autoDispose.family<int, List<Report>>((ref, reports) async* {
  final db = ref.read(firebaseServiceProvider).db;
  final reportIds = reports.map((e) => e.id).toList();

  // 监听每个集合的快照变化,必须开启includeMetadataChanges才能捕获pendingWrites变更
  final streams = [
    for (final name in kReportTypes)
      db
          .collection(name)
          .where('report_id', whereIn: reportIds)
          .snapshots(includeMetadataChanges: true)
  ];

  // 合并多个集合的快照流
  await for (final snapshots in StreamZip(streams)) {
    yield snapshots.where((snap) => snap.metadata.hasPendingWrites).length;
  }
});
  • 额外检查参数一致性:family修饰的provider会根据传入参数区分不同实例,默认使用==判断参数是否相等。如果多个页面传入的List<Report>内容相同但不是同一个内存实例,==判断会返回false,会生成多个独立的provider实例,如果你需要复用同一实例,可自定义provider的参数比对逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 12:39:00