I am making an app where people can sign up for events. Some events are highly popular and are fully booked within less than a second. To prevent overbooking I am trying to implement Firebase Transactions.

In most tutorials I found people use transaction.update(docRef, {"field": value});, is this also needed when modifying an array?

With the way I do it now I directly update it via the document reference, rather than using transaction.update(). Is this the way to do it? Will this 100% prevent overbooking?

If I use transaction.update() I get an error:

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument: Instance of 'Future<void>'
#0      StandardMessageCodec.writeValue (package:flutter/src/services/message_codecs.dart:466:7)
#1      FirestoreMessageCodec.writeValue (package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart:114:13)
#2      StandardMessageCodec.writeValue.<anonymous closure> (package:flutter/src/services/message_codecs.dart:463:9)
#3      _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13)
#4      StandardMessageCodec.writeValue (package:flutter/src/services/message_codecs.dart:461:13)
#5      FirestoreMessageCodec.writeValue (package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart:114:13)
#6      StandardMessageCodec.writeValue.<anonymous closure> (package:flutter/src/services/message_codecs.dart:463:9)
#7      _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13)
#8      StandardM<…>

My code

    final eventDocRef = events.doc(event.documentId);
    final addedGuests = ref.watch(guestsProvider);

  return await FirebaseFirestore.instance.runTransaction((transaction) {
        return transaction.get(eventDocRef).then((eventsDoc) {
          if (eventsDoc.get("max attendees") >= (eventsDoc
              .get("attendees")
              .length + addedGuests.guests.length + 1)) {
            try {
              var newEvent = eventDocRef.update({
                "attendees": FieldValue.arrayUnion(
                    [FirebaseAuth.instance.currentUser!.uid])});

              ref
                 .read(guestsProvider)
                 .guests
                 .forEach((element) {
               newEvent = eventDocRef.update({
                  "attendees": FieldValue.arrayUnion(
                      [element.uid!])});
              });

              /// Is it needed?
             // transaction.update(eventDocRef, {"attendees": newEvent});

              return newEvent;
            }
            catch (e) {
              return Future.error(e);
            }
          } else {
            return Future.error("Evenement is vol!");
          }
        });
      })


Solution 1: sempakonka

I got it working right. Thanks to @danh for pointing in the right direction.

return await FirebaseFirestore.instance.runTransaction((transaction) {
        return transaction.get(eventDocRef).then((eventsDoc) {
          if (eventsDoc.get("max attendees") >= (eventsDoc
              .get("attendees")
              .length + addedGuests.guests.length + 1)) {
            try {
              // get the attendees array
              final attendees = eventsDoc.get("attendees") as List;

              addedGuests.guests.forEach((element) {
                attendees.add(element.uid);
              });

              attendees.add(FirebaseAuth.instance.currentUser!.uid);

              // update the attendees array
              return transaction.update(eventDocRef, {
                "attendees": attendees,
              });
            }
            catch (e) {
              return Future.error(e);
            }
          } else {
            return Future.error("Evenement is vol!");
          }
        })