0

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

2
  • 1
    Can you share the complete code snippet that you tried and throws this error instead of just the declaration? Commented Sep 8, 2022 at 15:23
  • Updated - I help that helps. Commented Sep 8, 2022 at 20:53

1 Answer 1

1

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()}");
}
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks Peter!, so what you're saying is to convert the Object to Json directly and then to make a separate call that then updates the notes value. Correct?
Yes. Convert the object to json, then set the notes property to FieldValue.ArrayUnion.
I will 100% test this out - Thank you!
Curiosity question - you indicated that the 2nd option is not recommended although I don't see it now. I think it's not a bad solution at all. It avoids the potential for making multiple calls to FireStore (can get expensive). Can you elaborate on why it's not recommended?
This first solution only makes 1 call to Firestore. FieldValue.arrayUnion is not a call to Firestore. I deleted the second option (still visible in edit) because it involves manipulating your toFirestore function. The toFirestore function is usually generated automatically using json_serializable or freezed, so, I would not recommend manipulating it (unless you have no other option).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.