向现有群组添加用户时触发Firestore字段不存在错误排查
问题:添加群组成员时触发「Bad state: field does not exist within the DocumentSnapshotPlatform」错误
开发一款位置追踪应用,用户可创建群组,点击「在地图上显示」查看所有成员位置。目前已完成群组信息页,点击「添加成员」跳转至新页面,用户可搜索其他用户,点击搜索结果的ListTile后应将该用户加入群组并返回群组信息页。但实现添加用户到群组功能时触发错误:
Bad state: field does not exist within the DocumentSnapshotPlatform
相关代码
DatabaseService类代码
import 'package:cloud_firestore/cloud_firestore.dart'; class DatabaseService { final String? uid; DatabaseService({this.uid}); // 集合引用 final CollectionReference userCollection = FirebaseFirestore.instance.collection("users"); final CollectionReference groupCollection = FirebaseFirestore.instance.collection("groups"); // 保存用户数据 Future savingUserData(String name, String email) async { return await userCollection.doc(uid).set({ "name": name, "email": email, "friends": [], "groups": [], "profilePicture": "", "uid": uid, "latitude": "", "longitude": "", }); } // 获取用户数据 Future gettingUserData(String email) async { QuerySnapshot snapshot = await userCollection.where("email", isEqualTo: email).get(); return snapshot; } // 创建群组 Future createGroup(String name, String groupName, String id) async { DocumentReference groupDocumentReference = await groupCollection.add({ "groupName": groupName, "groupIcon": "", "groupCreator": "", "admins": [], "members": [], "groupId": "", }); await groupDocumentReference.update({ "admins": FieldValue.arrayUnion(["${uid}_$name"]), "groupCreator": FieldValue.arrayUnion(["${id}_$name"]), "groupId": groupDocumentReference.id }); DocumentReference userDocumentReference = userCollection.doc(uid); return await userDocumentReference.update({ "groups": FieldValue.arrayUnion(["${groupDocumentReference.id}_$groupName"]) }); } getUserGroups() async { return userCollection.doc(uid).snapshots(); } // 搜索用户 searchByEmail(String userEmail) async { return userCollection.where('email', isEqualTo: userEmail).get(); } // 获取群组管理员 getGroupAdmins(String groupId) async { return groupCollection.doc(groupId).snapshots(); } // 获取群组成员 getGroupMembers(String groupId) async { return groupCollection.doc(groupId).snapshots(); } // 添加成员到群组 Future addMember( String groupId, String groupName, String memberId, memberName, ) async { DocumentReference userDocumentReference = userCollection.doc(memberId); DocumentReference groupDocumentReference = groupCollection.doc(groupId); DocumentSnapshot userDocumentSnapshot = await userDocumentReference.get(); List<dynamic> groups = userDocumentSnapshot['groups']; DocumentSnapshot groupDocumentSnapshot = await groupDocumentReference.get(); List<dynamic> members = groupDocumentSnapshot['members']; if (groups.contains("${groupId}_$groupName") && members.contains("${memberId}_$memberName")) { return null; } else { await groupCollection.doc(groupId).update({ "members": FieldValue.arrayUnion(["${memberId}_$memberName"]) }); // 同步更新用户的群组列表 await userDocumentReference.update({ "groups": FieldValue.arrayUnion(["${groupId}_$groupName"]) }); } } }
AddMembers页面代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:tracker_app/page_changer/page_changer.dart'; import 'package:tracker_app/pages/app_pages/info_page.dart'; import 'package:tracker_app/services/database_service.dart'; import '../../theme/theme.dart'; class AddMembers extends StatefulWidget { final String groupId; final String groupName; const AddMembers({super.key, required this.groupId, required this.groupName}); @override State<AddMembers> createState() => _AddMembersState(); } class _AddMembersState extends State<AddMembers> { QuerySnapshot? searchSnapshot; TextEditingController emailController = TextEditingController(); String email = ""; String friendId = ""; String friendName = ""; bool isLoading = false; bool hasUserSearched = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text( "Add members", style: TextStyle(fontSize: 30, fontWeight: FontWeight.w500), ), centerTitle: true, ), body: SingleChildScrollView( child: Column(children: [ const Divider( height: 5, indent: 5, endIndent: 5, thickness: 1.5, ), Container(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: TextFormField( controller: emailController, cursorColor: AppColors.secondary, decoration: InputDecoration( errorBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.red), borderRadius: BorderRadius.circular(50)), focusedBorder: OutlineInputBorder( borderSide: const BorderSide( color: AppColors.secondary, ), borderRadius: BorderRadius.circular(50)), enabledBorder: OutlineInputBorder( borderSide: const BorderSide( color: AppColors.secondary, ), borderRadius: BorderRadius.circular(50)), labelText: "email", labelStyle: const TextStyle( color: AppColors.secondary, fontWeight: FontWeight.w500, ), suffixIcon: InkWell( splashColor: AppColors.secondary, borderRadius: BorderRadius.circular(360), onTap: initiateSearchMethod, child: const Icon( Icons.search, color: AppColors.secondary, ), ), hintText: "Enter persons email"), onChanged: (value) { setState(() { email = value; }); }, ), ), userList(), ]), ), ); } void initiateSearchMethod() async { if (emailController.text.isNotEmpty) { setState(() { isLoading = true; }); final snapshot = await DatabaseService(uid: FirebaseAuth.instance.currentUser!.uid) .searchByEmail(email); setState(() { searchSnapshot = snapshot; isLoading = false; hasUserSearched = true; if (snapshot.docs.isNotEmpty) { // 用户文档中存储ID的字段是uid,不是id friendId = snapshot.docs[0]['uid']; friendName = snapshot.docs[0]['name']; } }); } } Widget userList() { return hasUserSearched && searchSnapshot != null ? ListView.builder( shrinkWrap: true, itemCount: searchSnapshot!.docs.length, itemBuilder: (context, index) { return friendSearchTile(index); }, ) : Container(); } Widget friendSearchTile(int index) { final userDoc = searchSnapshot!.docs[index]; return ListTile( leading: CircleAvatar( radius: 25, backgroundColor: AppColors.secondary, child: Text( userDoc['name'].substring(0, 1).toUpperCase(), style: const TextStyle(color: Colors.white)), ), title: Text(userDoc['name']), subtitle: Text(userDoc['email']), trailing: InkWell( borderRadius: BorderRadius.circular(360), onTap: () async { await DatabaseService(uid: FirebaseAuth.instance.currentUser!.uid) .addMember( widget.groupId, widget.groupName, userDoc['uid'], userDoc['name']); nextScreenReplace(context, InfoPage(groupName: widget.groupName, groupId: widget.groupId)); print("friendId: ${userDoc['uid']}"); }, splashColor: AppColors.secondary, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: const Icon(Icons.add), ), ), ); } }
问题分析与修复
1. 核心错误:字段名不匹配
在AddMembers页面的initiateSearchMethod中,原代码尝试读取snapshot!.docs[0]['id'],但查看DatabaseService的savingUserData方法可知,用户文档中存储用户ID的字段是uid,不是id,这直接触发了「field does not exist」错误。
2. 补充空结果判断
搜索可能返回空结果(如输入的邮箱不存在),原代码直接访问snapshot!.docs[0]会触发索引越界错误,新增snapshot.docs.isNotEmpty判断避免崩溃。
3. 修复addMember方法逻辑
- 将判断条件的位运算符
&改为逻辑运算符&&; - 新增同步更新用户
groups数组的逻辑,确保用户侧能看到加入的群组。
4. 优化列表渲染逻辑
原friendSearchTile固定取第一个文档,修改为接收索引参数,支持处理多个搜索结果的场景。
内容的提问来源于stack exchange,提问作者netko
相关产品推荐
相关产品推荐

