Firestore动态创建用户专属日记集合并访问的代码修正求助
修正后的Firebase Firestore日记服务代码
问题分析
你修改后的代码主要存在三个核心问题:
- 异步方法调用未使用
await,导致拿到的是Future<String>而非实际邮箱字符串,强制类型转换引发错误 - 邮箱包含
@、.等Firestore集合名不允许的特殊字符,直接用作集合名会触发异常 - 未做空安全处理,当用户文档不存在或缺少email字段时会崩溃
完整修正代码
import 'package:cloud_firestore/cloud_firestore.dart'; class DatabaseService { final FirebaseFirestore _db = FirebaseFirestore.instance; // 用户相关操作 Future<void> addUserData(UserModel userData) async { await _db.collection("Users").doc(userData.uid).set(userData.toMap()); } Future<List<UserModel>> retrieveUserData() async { QuerySnapshot<Map<String, dynamic>> snapshot = await _db.collection("Users").get(); return snapshot.docs .map((docSnapshot) => UserModel.fromDocumentSnapshot(docSnapshot)) .toList(); } Future<String?> retrieveUserName(UserModel user) async { DocumentSnapshot<Map<String, dynamic>> snapshot = await _db.collection("Users").doc(user.uid).get(); return snapshot.data()?["displayName"]; // 空安全处理,避免崩溃 } Future<String?> retrieveUserEmail(UserModel user) async { DocumentSnapshot<Map<String, dynamic>> snapshot = await _db.collection("Users").doc(user.uid).get(); return snapshot.data()?["email"]; // 空安全处理 } // 处理邮箱特殊字符,转换为Firestore允许的集合名 String _sanitizeEmailForCollection(String email) { return email.replaceAll("@", "%40").replaceAll(".", "%2E"); } // 日记条目操作(修正后) Future<void> addEntryData(UserModel user, EntryModel entryData) async { // 异步获取邮箱并处理特殊字符 final String? userEmail = await retrieveUserEmail(user); if (userEmail == null) { throw Exception("用户邮箱不存在"); } final String sanitizedEmail = _sanitizeEmailForCollection(userEmail); // 使用Firestore自动生成文档ID,避免手动指定ID重复 await _db.collection(sanitizedEmail).doc().set(entryData.toMap()); } Future<List<EntryModel>> retrieveEntryData(UserModel user) async { final String? userEmail = await retrieveUserEmail(user); if (userEmail == null) { throw Exception("用户邮箱不存在"); } final String sanitizedEmail = _sanitizeEmailForCollection(userEmail); QuerySnapshot<Map<String, dynamic>> snapshot = await _db.collection(sanitizedEmail).orderBy("date").get(); return snapshot.docs .map((docSnapshot) => EntryModel.fromDocumentSnapshot(docSnapshot)) .toList(); } } // 参考:UserModel和EntryModel的标准定义 class UserModel { final String uid; final String? displayName; final String? email; UserModel({required this.uid, this.displayName, this.email}); Map<String, dynamic> toMap() { return { "uid": uid, "displayName": displayName, "email": email, }; } factory UserModel.fromDocumentSnapshot(DocumentSnapshot<Map<String, dynamic>> doc) { return UserModel( uid: doc["uid"], displayName: doc["displayName"], email: doc["email"], ); } } class EntryModel { final String? title; final String content; final DateTime date; EntryModel({this.title, required this.content, required this.date}); Map<String, dynamic> toMap() { return { "title": title, "content": content, "date": Timestamp.fromDate(date), }; } factory EntryModel.fromDocumentSnapshot(DocumentSnapshot<Map<String, dynamic>> doc) { return EntryModel( title: doc["title"], content: doc["content"], date: (doc["date"] as Timestamp).toDate(), ); } }
关键修改说明
- 异步操作修正:所有调用
retrieveUserEmail的地方添加await,确保拿到实际邮箱字符串,而非Future对象。 - 邮箱特殊字符处理:新增
_sanitizeEmailForCollection方法,将邮箱中的@、.转换为URL编码,符合Firestore集合名规则。 - 空安全与错误处理:将返回类型改为
String?,添加空判断避免空指针崩溃;若邮箱不存在则抛出异常,方便上层捕获处理。 - 文档ID优化:添加日记条目时使用Firestore自动生成的文档ID(
.doc()不带参数),避免手动指定ID可能出现的重复问题。
额外建议
如果不需要展示用户邮箱,更推荐使用用户UID作为集合名——UID是Firebase生成的唯一标识符,无特殊字符,无需额外处理,只需把retrieveUserEmail替换为直接使用user.uid即可,代码会更简洁。
内容的提问来源于stack exchange,提问作者Texter
相关产品推荐
相关产品推荐

