Martin Palmer
Martin Palmer

Reputation: 97

Use FieldValue.arrayUnion in Flutter with Cloud FireStore

fairly new to Flutter. I'm trying to update an Array field that contains a List Map of type <String, dynamic>. The data structure I'm trying to achieve looks similar to the image below: Data Structure

In order to update the "notes" field for my custom object without overwriting the entire Array, I'm using FieldValue.arrayUnion with SetOptions(merge: true)like so during a call to an update function :

 notes: FieldValue.arrayUnion([
                                      {
                                        "date": DateTime.now(),
                                        "notes": "some test notes",
                                        "user": "some user name",
                                      }
                                    ]),

The "notes" field is part of a much larger Object and is defined as being of type nullable List of type Map<String, dynamic> like so:

List<Map<String, dynamic>>? notes;

The problem is surfacing with this error:

The argument type 'FieldValue' can't be assigned to the parameter type 'List<Map<String, dynamic>>?'.

and in looking at every example I've come across it looks like the function "FieldValue.arrayUnion" will only accept a String value for the field name to update. Below is a code sample from the FireStore documentation at this link: https://cloud.google.com/firestore/docs/manage-data/add-data

// Atomically add a new region to the "regions" array field.
washingtonRef.update({
  "regions": FieldValue.arrayUnion(["greater_virginia"]),
});

My question is: Is there a way to pass a field from a custom object to FieldValue.arrayUnion or is the only thing it will accept a string of the field name?

As requested, I've included the full code for this problem in the hopes it can be resolved:

This is the class definition with the 'notes' property with a Constructor

class SchedulerEvent extends ChangeNotifier {
  String? key;
  String? title;
  String? fname;
  String? lname;
  DateTime? from;
  DateTime? to;
  List<Map<String, dynamic>>? notes;
  Color? background;
  bool? isAllDay;

  /// CTOR
  SchedulerEvent({
    this.key,
    this.title,
    this.fname,
    this.lname,
    this.from,
    this.to,
    this.notes,
    this.background,
    this.isAllDay,
  });

This is a Factory where I pull data from FireStore:

  factory SchedulerEvent.fromFireStore(DocumentSnapshot<Map<String, dynamic>> fireStoreSnapshot) {
    final data = fireStoreSnapshot.data();

    return SchedulerEvent(
      key: fireStoreSnapshot.id,
      title: data?['title'],
      fname: data?['fname'],
      lname: data?['lname'],
      from: data?['from']?.toDate(),
      to: data?['to']?.toDate(),
      notes: data?['notes'] is Iterable ? List.from(data?['notes']) : null,
      background: null,
      isAllDay: false,
    );
  }

This is where I save data back to Firestore:

  Map<String, dynamic> toFireStore() {
    return {
      'title': title, 
      'fname': fname, 
      'lname': lname, 
      'from': from, 
      'to': to, 
      'notes': notes, 
      'background': background, 
      'isAllDay': isAllDay
    };
  }

This is where I create the events with (in this case) some hard-coded values for the notes:

await FirebaseFirestore.instance.collection('SchedulerEventCollection').add(SchedulerEvent(
        title: '${_firstNameFormFieldController.text} ${_lastNameFormFieldController.text}',
        fname: _firstNameFormFieldController.text,
        lname: _lastNameFormFieldController.text,
        from: targetEvent.from,
        to: targetEvent.to,
        notes: [
          {
            "date": DateTime.now(),
            "notes": "some notes",
            "user": "some user",
          }
        ],
        background: null,
        isAllDay: false)
    .toFireStore());
formSubmit("Submitted the entry!");
} on Exception catch (e) {
formSubmit("Error submitting your event! ${e.toString()}");

Finally, this is where the errors start to happen when I try to update. The error states

await FirebaseFirestore.instance.collection('SchedulerEventCollection').doc(targetEvent.key).set(SchedulerEvent(
        title: '${_firstNameFormFieldController.text} ${_lastNameFormFieldController.text}',
        fname: _firstNameFormFieldController.text,
        lname: _lastNameFormFieldController.text,
        from: targetEvent.from,
        to: targetEvent.to,
        notes: FieldValue.arrayUnion([
          {
            "date": DateTime.now(), 
            "notes": "some new notes", 
            "user": "some different user",
          }
        ]),
        background: null,
        isAllDay: false)
    .toFireStore(), SetOptions(merge: true));
formSubmit("Updated the Event!");
} on Exception catch (e) {
formSubmit("Error submitting your event! ${e.toString()}");

The error is below (I've tried multiple data types for "notes" but still the same problem. enter image description here

Upvotes: 0

Views: 884

Answers (1)

Peter Obiechina
Peter Obiechina

Reputation: 2835

Your toFirestore is expecting notes to be of type List<Map<String, dynamic>>?, not FieldValue.arrayUnion (and that is okay). Update your data like this:

try {
  final schedulerEventJson = SchedulerEvent(
    title:
        '${_firstNameFormFieldController.text} ${_lastNameFormFieldController.text}',
    fname: _firstNameFormFieldController.text,
    lname: _lastNameFormFieldController.text,
    from: targetEvent.from,
    to: targetEvent.to,
    // remove notes from here.
    background: null,
    isAllDay: false,
  ).toFireStore();

  // here I set the value of notes directly into the json
  schedulerEventJson['notes'] = FieldValue.arrayUnion([
    {
      "date": DateTime.now(),
      "notes": "some new notes4",
      "user": "some different user4",
    },
  ]);
  await FirebaseFirestore.instance
      .collection('SchedulerEventCollection')
      .doc(targetEvent.key)
      .set(schedulerEventJson, SetOptions(merge: true));
  formSubmit("Updated the Event!");
} on Exception catch (e) {
  formSubmit("Error submitting your event! ${e.toString()}");
}

Upvotes: 1

Related Questions