Flutter中Firebase Firestore原子事务同步TextField数据问题求解
问题修复与完整实现
你遇到的报错核心原因是Firestore的transaction.update只能针对已存在的文档执行,原代码还存在文档ID随机生成、生命周期逻辑缺失的问题,以下是符合你全部需求的可运行实现:
核心修改点
- 用事务的
set方法配合merge: true参数,实现「文档不存在则创建、存在则更新字段」的逻辑,解决不存在文档调用update的报错 - 直接使用用户uid作为文档ID,如需改用email替换文档ID参数即可
- 新增保存状态标记,在页面销毁时自动删除未提交的文档
- 修复原代码重复调用
super.initState()、未正确释放控制器的问题
完整代码
class CreateQ extends StatefulWidget { final User user; CreateQ({Key? key, required this.user}) : super(key: key); @override State<CreateQ> createState() => _CreateQState(); } class _CreateQState extends State<CreateQ> { final title_Controller = TextEditingController(); final desc_Controller = TextEditingController(); final tag_Controller = TextEditingController(); // 标记用户是否点击了保存按钮 bool _isSaved = false; late User _currentUser; UploadTask? task; File? file; @override void initState() { super.initState(); _currentUser = widget.user; // 监听输入变化 title_Controller.addListener(_latestTitle); desc_Controller.addListener(_latestDesc); tag_Controller.addListener(_latestTag); } @override void dispose() { // 清理输入控制器避免内存泄漏 title_Controller.dispose(); desc_Controller.dispose(); tag_Controller.dispose(); // 未点击保存则自动删除对应文档 if (!_isSaved) { FirebaseFirestore.instance .collection('question') .doc(_currentUser.uid) .delete(); } super.dispose(); } void _latestTitle() { print('Title text field: ${title_Controller.text}'); } void _latestDesc() { print('Desc text field: ${desc_Controller.text}'); } void _latestTag() { print('Tag text field: ${tag_Controller.text}'); } // 提交数据到Firestore Future<void> postQ() async { // 基础输入校验,可根据需求扩展 if (title_Controller.text.trim().isEmpty || desc_Controller.text.trim().isEmpty || tag_Controller.text.trim().isEmpty) { // 可在此处添加Toast/提示弹窗告知用户补全信息 return; } FirebaseFirestore db = FirebaseFirestore.instance; // 使用用户uid作为文档ID,如需改用email则替换为doc(_currentUser.email) DocumentReference qRef = db.collection('question').doc(_currentUser.uid); await db.runTransaction((transaction) async { // merge: true 表示文档不存在则创建,存在则仅更新传入的字段,不会覆盖已有字段(如likes等) transaction.set(qRef, { 'uid': _currentUser.uid, 'email': _currentUser.email, 'title': title_Controller.text.trim(), 'desc': desc_Controller.text.trim(), 'tag': tag_Controller.text.trim(), }, SetOptions(merge: true)); }); // 标记已保存,dispose时不会删除文档 _isSaved = true; // 保存成功后可自行添加返回上一页/提示成功的逻辑 } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('提交问题')), body: Container( padding: const EdgeInsets.all(32), child: ListView( shrinkWrap: true, children: [ TextField( controller: title_Controller, decoration: const InputDecoration( hintText: 'Your Title here', counterText: '', border: OutlineInputBorder(), ), keyboardType: TextInputType.multiline, maxLines: 1, maxLength: 100, ), const SizedBox(height: 8), TextField( controller: desc_Controller, decoration: const InputDecoration( hintText: 'Enter Your Description here... ', border: OutlineInputBorder(), ), keyboardType: TextInputType.multiline, maxLines: 15, maxLength: 10000, ), const SizedBox(height: 8), TextField( controller: tag_Controller, decoration: const InputDecoration( hintText: 'Add up to 5 tags to describe what your question is about', counterText: '', border: OutlineInputBorder(), ), keyboardType: TextInputType.text, maxLines: 1, maxLength: 100, ), const SizedBox(height: 16), // 保存按钮 ElevatedButton( onPressed: postQ, child: const Text('保存内容'), ) ], ), ), ); } }
注意事项
如果你的文档需要保留likes、comments等已有字段,必须保留SetOptions(merge: true)参数,否则set方法会直接覆盖整个文档,清空原有字段。
内容的提问来源于stack exchange,提问作者Mithson
相关产品推荐
相关产品推荐

