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

Flutter结合Provider与Firestore使用Document Reference的嵌套引用更新问题

解决方案

问题核心原因

你当前的实现没有将内部Stat的获取流和外层Check的流做组合,外层流发射Check数据时内部Stat的异步请求还未完成,后续Stat加载完成后也无法触发外层流重新发射数据,所以UI不会自动刷新。

具体实现方案

1. 引入RxDart依赖

在pubspec.yaml中添加RxDart用于流组合操作:

dependencies:
  rxdart: ^0.27.7 # 请使用当前最新稳定版本

2. 改造DataService的getChecks方法

使用流组合操作,将嵌套的Stat获取流和外层Check流绑定,只要任意数据更新都会触发整体流发射新值:

import 'package:rxdart/rxdart.dart';

class DataService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  Stream<List<Check>> getChecks() {
    var ref = _db.collection("/users/.../.../checks/");
    // 外层监听Check集合的实时变化
    return ref.snapshots().switchMap((querySnapshot) {
      // 把每个Check文档转换成带Stat数据的Stream<Check>
      final checkStreams = querySnapshot.docs.map((doc) {
        Map _a = doc.data() as Map<dynamic, dynamic>;
        var _alternatives = _a['rollAlternatives']['default'];
        List<DocumentReference> statRefs = _alternatives['stats'];

        // 如果没有stat引用直接返回基础Check
        if (statRefs.isEmpty) {
          return Stream.value(Check.fromFirestore(doc, statList: []));
        }

        // 把每个stat引用转成Stream<Stat>
        final statStreams = statRefs.map((ref) => getStat(ref.parent.path, ref.id));
        // 组合所有Stat的流,只要有一个Stat更新就会发射最新的全量Stat列表
        return CombineLatestStream.list<Stat>(statStreams).map((statList) {
          return Check.fromFirestore(doc, statList: statList);
        });
      }).toList();

      // 组合所有Check的流,输出最终的List<Check>
      return CombineLatestStream.list<Check>(checkStreams);
    });
  }

  Stream<Stat> getStat(String parentPath, String docId) {
    // 如需Stat实时更新,保持snapshots()即可;如果只需要拉取一次,改成.get()
    return _db
        .collection(parentPath)
        .doc(docId)
        .snapshots()
        .map((doc) => Stat.fromFirestore(doc));
  }
}

3. 配合Provider使用

确保你在Widget树中使用StreamProvider监听这个流,即可自动响应数据变化刷新UI:

// 注入Provider
StreamProvider<List<Check>>(
  create: (context) => DataService().getChecks(),
  initialData: const [],
  child: const YourPage(),
)

// 页面中使用
Consumer<List<Check>>(
  builder: (context, checks, child) {
    if (checks.isEmpty) {
      return const CircularProgressIndicator();
    }
    return ListView.builder(
      itemCount: checks.length,
      itemBuilder: (context, index) {
        final check = checks[index];
        return CheckItem(check: check);
      },
    );
  }
)

可选优化

可以给Check类增加一个isStatsLoading字段,在Stat加载过程中展示占位符,避免首次渲染出现空白。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 14:18:05