如何在Flutter中关联Firebase Auth用户ID与Firestore用户ID
用户注册与Firestore档案关联实现方案
核心逻辑
必须先通过Firebase Auth完成用户账号创建,拿到系统生成的唯一UID,再用这个UID作为Firestore文档ID,将用户名、邮箱等档案信息写入users集合,这样就能保证Auth账号和Firestore档案一一对应。
原代码存在的问题
- 执行顺序颠倒:先写Firestore再创建Auth用户,导致无法获取真实用户UID,还错误用了固定字符串
"uid"作为文档ID,根本关联不到真实用户。 - 数据存储错误:直接把
TextEditingController对象存进Firestore,而不是用户输入的文本内容(应该取.text属性)。 - 错误处理脱节:Firestore写入和Auth创建没有绑定,Auth创建失败时,Firestore可能已经写入无效数据。
修正后的完整代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dev/reusable.dart'; import 'package:flutter_dev/home.dart'; class SignUp extends StatefulWidget { const SignUp({super.key}); @override State<SignUp> createState() => _SignUpState(); } class _SignUpState extends State<SignUp> { final TextEditingController _passwordTextController = TextEditingController(); final TextEditingController _emailTextController = TextEditingController(); final TextEditingController _userNameTextController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, extendBodyBehindAppBar: true, appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, title: const Text('Sign Up'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const SizedBox(height: 20), reusableTextField( "Enter Username", Icons.person_outline, false, _userNameTextController, context, ), const SizedBox(height: 20), reusableTextField( "Enter Email ID", Icons.email_outlined, false, _emailTextController, context, ), const SizedBox(height: 20), reusableTextField( "Enter Password", Icons.lock_outline, true, _passwordTextController, context, ), const SizedBox(height: 20), signInsignUpButton(context, false, () async { try { // 第一步:创建Firebase Auth用户,获取唯一UID UserCredential userCredential = await FirebaseAuth.instance .createUserWithEmailAndPassword( email: _emailTextController.text.trim(), password: _passwordTextController.text.trim(), ); String userId = userCredential.user!.uid; final db = FirebaseFirestore.instance; // 第二步:用UID作为文档ID,写入用户档案到Firestore await db.collection("users").doc(userId).set({ 'userName': _userNameTextController.text.trim(), 'email': _emailTextController.text.trim(), 'accountCreated': Timestamp.now(), 'avatarUrl': 'https://example.com/default-avatar.png', // 默认头像,后续可让用户修改 }); print("账号与档案创建成功"); // 跳转到首页,先检查Widget是否还挂载 if (!mounted) return; Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const Home()), ); } on FirebaseAuthException catch (e) { print("Auth错误: ${e.message}"); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(e.message ?? "注册失败")), ); } on FirebaseException catch (e) { print("Firestore错误: ${e.message}"); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("档案创建失败: ${e.message}")), ); } catch (e) { print("未知错误: $e"); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("注册出错了")), ); } }), ], ), ), ); } }
关键细节说明
- Firestore结构:自动创建
users集合,每个文档的ID就是Auth生成的UID,文档内包含用户名、邮箱、创建时间、默认头像URL等字段。 - 输入处理:用
.trim()去除输入文本前后的空格,避免存储无效的空白内容。 - 异步优化:用
async/await替代链式then,代码逻辑更清晰,错误处理更集中。 - Widget安全:添加
mounted检查,防止Widget销毁后执行导航、弹窗等操作引发错误。 - 后续扩展:如果要支持用户上传头像,可以用Firebase Storage存储图片文件,然后把图片的下载URL更新到Firestore的
avatarUrl字段即可。
内容的提问来源于stack exchange,提问作者Boatti
相关产品推荐
相关产品推荐

