Firebase Firestore 原子事务,用于在 Flutter 中将文档与 TextField 同步

Firebase Firestore atomic Transactions For syncing document with TextField in Flutter

我是 firebase 和 firestore 的新手,在将 TextField 数据存储在云 firestore 中并对其执行事务时遇到问题,我怀疑我们是否可以执行更新事务,如果文档不存在,那么它应该创建和然后执行交易...

我想实现以下目标:

  1. 我想将当前用户uidemail存储在'X'[=59的文档中=].

  2. 我想对 'Y'(这里 Y 是 uid/email)文件执行交易更新 'X' collection 而且如果该用户的文档不存在,它应该创建一个然后执行事务,然后如果用户在没有按下 save 按钮的情况下退出文本字段,那么它应该从 firestore 中删除。

  3. 我想传递用户的 uid, title, tag, desc 等。作为文档中的字段(docID 应该是 emailuser's uid) 当前用户的。

到目前为止,我能够从 auth class 获取用户并在我当前的工作 class 中使用 initState() 访问它,但我不知道如何获取和存储 uid/email 到 'X' collections 文档

将数据传递给 firebase 的代码是,

 Future<void> postQ() async {
    FirebaseFirestore db = FirebaseFirestore.instance;
    CollectionReference question =
        FirebaseFirestore.instance.collection('question');
    DocumentReference cUser = question.doc(_currentUser.uid);
      // in the above line I tried to get currentuser uid but that also not working and below I want to pass title,etc... in the doc of that current user with fields title, tag, desc, users uid and email and docID should be the email or user's uid....
      await db.runTransaction((transaction) async {
      String title = title_Controller.text;
      String desc = desc_Controller.text;
      String tag = tag_Controller.text;
      DocumentReference qRef = db.collection('question').doc();
      DocumentSnapshot snapshot = await transaction.get(qRef);
      // int likesCount = snapshot.data['likes'];
      await transaction.update(qRef, {
        // 'authorId': _currentUser,
        'title': title,
        'desc': desc,
        'tag': tag,
      });
    });
  }
}

但是上面的代码向我抛出以下消息,所以我如何在 运行 交易之前创建一个并且该文档应该将当前用户 uid 和电子邮件作为字段之一。

cannnot run transaction on not existing document

下面是从终端中的文本字段获取持续更新的工作代码,

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();
  @override
  void initState() {
    super.initState();
    _currentUser = widget.user;
    super.initState();
    // Start listening to changes.
    title_Controller.addListener(_latestTitle);
    desc_Controller.addListener(_latestDesc);
    tag_Controller.addListener(_latestTag);
  }

  // @override
  // void dispose() {
  // Clean up the controller when the widget is removed from the widget tree.
  // This also removes the latestvalue listener.
  // title_Controller.dispose();
  // 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}');
  }

  // ignore: unused_field
  late User _currentUser;
  

  UploadTask? task;
  File? file;

  @override
  Widget build(BuildContext context) {
 body: Container(
          padding: EdgeInsets.all(32),
          child: ListView(shrinkWrap: true, children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                TextField(
                  controller: title_Controller,
                  decoration: InputDecoration(
                      hintText: 'Your Title here',
                      counterText: '',
                      border: OutlineInputBorder()),
                  keyboardType: TextInputType.multiline,
                  maxLines: 1,
                  maxLength: 100,
                ),
                SizedBox(height: 8),
                TextField(
                  controller: desc_Controller,
                  decoration: InputDecoration(
                      hintText:
                          'Enter Your Description here... ',
                      border: OutlineInputBorder()),
                  keyboardType: TextInputType.multiline,
                  maxLines: 15,
                  maxLength: 10000,
                ),
                SizedBox(height: 8),
                TextField(
                  controller: tag_Controller,
                  decoration: InputDecoration(
                      hintText:
                          'Add up to 5 tags to describe what your question is about',
                      counterText: '',
                      border: OutlineInputBorder()),
                  keyboardType: TextInputType.text,
                  maxLines: 1,
                  maxLength: 100,
                ),  ));
  }

我尝试使用 set() 方法,但会抛出错误如果有人能提供当前问题的工作样本,那将非常有帮助, 谢谢...

如果文档不存在,调用更新将不会创建文档。 如果文档不存在,则设置 create/update 文档。 但是,您可以检查文档是否存在,然后根据需要使用设置或更新。

 DocumentSnapshot snapshot = await transaction.get(qRef);
if(snapshot.exists){
  //run update/set operation
}else{
 // run set operation
}

无论您是否使用事务,设置和更新都将按照我上面提到的方式进行。如果对任何文档的任何 sets/updates 失败,事务使您能够撤消任何文档操作。

您可以通过此处了解设置和更新之间的区别。 https://firebase.google.com/docs/firestore/manage-data/add-data