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

向现有群组添加用户时触发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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:35:27