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

Flutter嵌套Firebase GET请求出现RangeError该如何解决?

问题原因
  • 核心问题是Dart的List.forEach()方法不支持等待异步回调执行完成:你在forEach的回调里加了async/await,但这个await只作用于回调函数内部,外层的fetchAndSetExercises函数不会等待所有forEach内部的Firestore查询请求完成,就直接执行了_exercises = loadedExercises.toList();和notifyListeners(),此时loadedExercises还是空数组,后续你访问数组第0位元素时自然抛出范围错误。
  • 你移除第二个get请求后没有异步操作,forEach同步执行完所有add操作,数组有值,所以不会报错。Firebase本身完全支持跨集合嵌套发起get请求,问题出在异步逻辑的写法上。
可行解决方案

方案1:用Future.wait配合map处理批量异步操作(推荐,性能最优,并行发起所有查询)

把forEach替换为map生成Future列表,用Future.wait等待所有异步操作完成后再统一处理:

Future<void> fetchAndSetExercises() async {
  final List<Exercise> loadedExercises = [];
  // 先获取所有训练动作
  final extracted = await _firestore.collection('exercises').get();
  // 生成所有收藏查询的Future列表
  final favCheckFutures = extracted.docs.map((exerciseData) async {
    print("EXERCISE DATA: ${exerciseData['title']}");
    // 查询收藏状态
    final checkFav = await _firestore
        .collection('favorites')
        .doc(_firebaseAuth.currentUser.uid)
        .collection('userFavorites')
        .doc(exerciseData.id)
        .get();
    // 直接组装Exercise对象返回,可新增字段存储收藏状态
    return Exercise(
      id: exerciseData.id,
      title: exerciseData['title'],
      description: exerciseData['description'],
      imageUrl: exerciseData['imageUrl'],
      itemUrl: exerciseData['itemUrl'],
      minutes: exerciseData['minutes'],
      isFavorite: checkFav.exists,
    );
  });
  // 等待所有查询和组装完成,一次性拿到所有训练动作
  loadedExercises.addAll(await Future.wait(favCheckFutures));
  
  _exercises = loadedExercises;
  notifyListeners();
}

方案2:用普通for循环(顺序执行查询,适合数据量小的场景)

如果你的训练动作数量不多,也可以用普通for循环,Dart的普通for循环会等待异步操作完成:

Future<void> fetchAndSetExercises() async {
  final List<Exercise> loadedExercises = [];
  final extracted = await _firestore.collection('exercises').get();
  // 普通for循环支持等待内部异步操作
  for (var exerciseData in extracted.docs) {
    print("EXERCISE DATA: ${exerciseData['title']}");
    final checkFav = await _firestore
        .collection('favorites')
        .doc(_firebaseAuth.currentUser.uid)
        .collection('userFavorites')
        .doc(exerciseData.id)
        .get();
    loadedExercises.add(Exercise(
      id: exerciseData.id,
      title: exerciseData['title'],
      description: exerciseData['description'],
      imageUrl: exerciseData['imageUrl'],
      itemUrl: exerciseData['itemUrl'],
      minutes: exerciseData['minutes'],
      isFavorite: checkFav.exists,
    ));
  }
  _exercises = loadedExercises;
  notifyListeners();
}

优化建议(可选,大幅降低请求次数)

如果训练动作数量较多,逐个查询收藏会产生大量请求,你可以改为一次性查询当前用户的所有收藏ID,再和训练动作列表做本地匹配,全程只需2次Firestore请求即可完成:

Future<void> fetchAndSetExercises() async {
  // 并行请求训练动作列表和用户所有收藏列表,性能更高
  final futures = await Future.wait([
    _firestore.collection('exercises').get(),
    _firestore.collection('favorites')
        .doc(_firebaseAuth.currentUser.uid)
        .collection('userFavorites')
        .get()
  ]);
  final exerciseSnapshot = futures[0] as QuerySnapshot;
  final favSnapshot = futures[1] as QuerySnapshot;
  // 把收藏的动作ID存成Set,匹配时时间复杂度为O(1)
  final favIds = favSnapshot.docs.map((doc) => doc.id).toSet();
  // 批量组装数据
  _exercises = exerciseSnapshot.docs.map((exerciseDoc) {
    return Exercise(
      id: exerciseDoc.id,
      title: exerciseDoc['title'],
      description: exerciseDoc['description'],
      imageUrl: exerciseDoc['imageUrl'],
      itemUrl: exerciseDoc['itemUrl'],
      minutes: exerciseDoc['minutes'],
      isFavorite: favIds.contains(exerciseDoc.id),
    );
  }).toList();
  notifyListeners();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 15:24:07