Flutter使用FirebaseFirestore查询嵌套集合返回空列表无报错问题
问题原因
- 前置校验缺失,查询路径可能无效
你没有校验currentUser是否完成初始化、传入的workoutId是否合法,若currentUser!.uid或workoutId和Firestore中实际存储的路径不匹配,查询会直接返回空快照,不会抛出你捕获的常规Exception。
- 前置校验缺失,查询路径可能无效
- 安全规则拦截无报错
如果Firestore安全规则拒绝了当前用户对users/[uid]/workouts/[workoutId]/exercises路径的读权限,get()方法会返回空快照,默认不会抛出可见错误。
- 安全规则拦截无报错
- 异步时序错误
你对sets子集合的查询是异步操作,代码未等待sets数据返回就提前执行了setState渲染,此时listOfExerciseSets还未赋值,会出现空数据问题。
- 异步时序错误
- 文档ID获取语法错误
listOfExercises[i]本身就是QueryDocumentSnapshot类型,直接取.id即可获取文档ID,你写的.doc.id属于语法错误,会导致后续sets子集合的查询路径完全错误,且异步错误未被捕获所以看不到提示。
- 文档ID获取语法错误
解决步骤
1. 先做问题排查
- 打印
currentUser?.uid和传入的workoutId,和Firestore控制台中实际存储的路径对比,确认路径完全一致。 - 到Firebase控制台的安全规则沙盒中,模拟当前用户对对应路径的读请求,确认规则未拦截查询。
2. 修正代码
修正后的完整代码如下:
_getDataRowsForCompletedExerciseSets(String workoutId) async { // 前置校验,避免无效请求 if (currentUser == null || workoutId.trim().isEmpty) return; try { // 清空历史数据,避免重复累加 listOfExerciseSets.clear(); completedExerciseSetRows.clear(); // 获取exercise列表,去掉冗余的then调用 final exerciseSnapshot = await FirebaseFirestore.instance .collection('users') .doc(currentUser!.uid) .collection("workouts") .doc(workoutId) .collection("exercises") .get(); final listOfExercises = exerciseSnapshot.docs; // 打印查询结果,排查路径问题 print("查询到的exercise数量:${listOfExercises.length}"); if (listOfExercises.isEmpty) return; // 批量查询所有exercise对应的sets子集合,等待所有请求完成 final setQueryFutures = listOfExercises.map((exerciseDoc) { return FirebaseFirestore.instance .collection('users') .doc(currentUser!.uid) .collection("workouts") .doc(workoutId) .collection("exercises") .doc(exerciseDoc.id) // 修正文档ID获取语法 .collection("sets") .get() .then((setSnapshot) => _createListOfExerciseSets(setSnapshot)); }); await Future.wait(setQueryFutures); // 所有数据加载完成后再更新UI setState(() { int setCount = 1; for (ExerciseSet set in listOfExerciseSets) { completedExerciseSetRows.add(DataRow(cells: [ DataCell(Text(setCount.toString())), DataCell(Text(set.weight.toString())), DataCell(Text(set.reps.toString())), DataCell(set.isSetToFailure ? const Icon(Icons.check) : const SizedBox.shrink()), ])); setCount++; } }); } catch (e) { // 捕获所有类型的错误,避免漏看报错 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(e.toString()), duration: const Duration(milliseconds: 1000), ), ); } } }
_createListOfExerciseSets(QuerySnapshot snapshot) { for (var doc in snapshot.docs) { listOfExerciseSets.add(ExerciseSet.fromFireStore(doc)); } print('已加载的sets数量:${listOfExerciseSets.length}'); }
如果你需要监听sets数据的实时变化,把
get()替换为snapshots().listen(_createListOfExerciseSets)即可,同时把setState逻辑移到_createListOfExerciseSets方法末尾,每次拿到新数据自动更新UI。
内容的提问来源于stack exchange,提问作者who-aditya-nawandar
相关产品推荐
相关产品推荐

