Firebase Firestore atomic Transactions For syncing document with TextField in Flutter

Issue

I am new to firebase and firestore and facing problem while storing TextField data in cloud firestore and performing transactions on them, and my doubt is can we perform update transaction and if the document doesn’t exist then it should create and then perform transaction…

I want to achieve the Following:

  1. I want to store the current user uid and email in a document of ‘X’ collection.

  2. I want to perform transaction update on ‘Y’ (here Y is either uid/email) document of ‘X’ collection
    and also if that document for that user doesn’t exist it should create one and then perform transaction and then if the user exits the textfield without pressing the save button then it should be deleted from firestore.

  3. I want to pass user’s uid, title, tag, desc, etc.. as field in the doc(docID should be the email or user's uid) of that current user.

so far I am able to get user from auth class and access it in my current working class using initState() but I dont know how to get and store that uid/email to a ‘X’ collections document

the code for passing data to firebase is,

 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,
      });
    });
  }
}

but it above code throws me below message, so how can I create one before running transactions and that document should have current users uid and email as one of the field.

cannnot run transaction on not existing document

below is the working code to get constant update from textfield in terminal,

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,
                ),  ));
  }

and I tried using set() method but that throws error It would be very helpful if anyone can give working sample of the current problem,
Thank you…

Solution

Calling update will not create the doc if the document doesn’t exists.
Set will create/update the doc if it doesn’t exists.
You can however check if the doc exists or not and then use set or update as required.

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

Whether you use transactions or not, the set and update would work just the way I mentioned above. Transactions provide you with ability to reverse any doc operations if any of the sets/updates to any one document fails.

You can learn about differences between set and update through here.
https://firebase.google.com/docs/firestore/manage-data/add-data

Answered By – Rutvik_110

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *