Mapping Flutter Object with List to Firebase realtime DB

Viewed 1861

I am trying to save my Poll Object to Firebase Realtime DB, but I dont really know how to do that with my Poll Object because it has a List. I tried to find a tutorial how to map an object with map to firebase but didnt find any.

class Poll {
  String id;
  String name;
  String description;
  List<Question> questions;

  Poll({this.name, this.description, this.questions});

  Poll.fromSnapshot(DataSnapshot snapshot) {
    id = snapshot.key;
    name = snapshot.value['name'];
    description = snapshot.value['description'];
    questions = snapshot.value['questions'];
  }

  toJson() {
    return {'name': name, 'description': description, 'questions': questions};
  }
}

class Question {
  String id;
  String question;
  String customAnswer;

  Question.customAnswer({this.question, this.customAnswer});

  Question.fromSnapshot(DataSnapshot snapshot) {
    id = snapshot.key;
    question = snapshot.value['question'];
    customAnswer = snapshot.value['customAnswer'];
  }

  toJson() {
    return {'question': question, 'customAnswer': customAnswer};
  }
}

Here I try to write to DB:

RaisedButton(
                onPressed: () async {
                  Poll poll1 =
                      Poll(name: 'poll1', description: 'desc1', questions: [
                    Question.customAnswer(
                        question: 'who am i', customAnswer: 'Ostap'),
                    Question.customAnswer(
                        question: 'who are you', customAnswer: 'test'),
                  ]);
                  await databaseReference
                      .child('Polls')
                      .push()
                      .set(poll1.toJson());
                },
                child: Text('Write To DB'),

And here the error Im getting:

Exception has occurred.
ArgumentError (Invalid argument: Instance of 'Question')

Its caused on await databaseReference

Can somebody help me? Thanks in advance!

2 Answers

I have created a JSON from your code as

{
  "id":"",
  "name":"",
  "description":"",
  "questions": [
      {
        "id":"",
        "question":"",
        "customAnswer":""
      }
  ]
}

Then to generate the dart classes Use this website. your object class will look like this.

class Welcome {
Welcome({
    this.id,
    this.name,
    this.description,
    this.questions,
});

String id;
String name;
String description;
List<Question> questions;

factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
    id: json["id"],
    name: json["name"],
    description: json["description"],
    questions: List<Question>.from(json["questions"].map((x) => Question.fromJson(x))),
);

Map<String, dynamic> toJson() => {
    "id": id,
    "name": name,
    "description": description,
    "questions": List<dynamic>.from(questions.map((x) => x.toJson())),
};

}

class Question {
Question({
    this.id,
    this.question,
    this.customAnswer,
});

String id;
String question;
String customAnswer;

factory Question.fromJson(Map<String, dynamic> json) => Question(
    id: json["id"],
    question: json["question"],
    customAnswer: json["customAnswer"],
);

Map<String, dynamic> toJson() => {
    "id": id,
    "question": question,
    "customAnswer": customAnswer,
};

}

Now you will be able to call await ...set(poll1.toJson()); without any error

If you want to set your data in format that under each node every object has it id instead of 0, 1, 2 ... just like below map

deliveryCharges: 2
hasPromoCode: true
id: "-MhHzoacmGb5839WG9_C"
uid: "LcZXgcW2VAfz34qJbu6QCm3u2B32"
 items:
   -MhCGlHJLOExb3eXxR08
       itemId: "-MhCElzl6B7FZMakA2Kr"
       itemName: "Regular package"
       itemPerPrice: 40
       itemQuantity: 3
       subtitle: "40 p.c"
       variationId: "-MhCGlHJLOExb3eXxR08"
  -MhCGlTiT2qy317c79_n
       itemId: "-MhCElzl6B7FZMakA2Kr"
       itemName: "Regular package"
       itemPerPrice: 100
       itemQuantity: 5
       subtitle: "60 p.c"
       variationId: "-MhCGlTiT2qy317c79_n"

Order class

class Order {
 String id;
 String uid;
 int timestamp;
 bool hasPromoCode;
 String promoCode;
 num promoDiscount;
 num deliveryCharges;
 num totalAmount;
 List<Cart> items;

 Order({
   @required this.id,
   @required this.uid,
   @required this.timestamp,
   @required this.deliveryCharges,
   @required this.totalAmount,
   @required this.items,
 });

 factory Order.fromMap(Map<dynamic, dynamic> map) {
   return new Order(
     id: map['id'] as String,
     uid: map['uid'] as String,
     timestamp: map['timestamp'] as int,
     deliveryCharges: map['deliveryCharges'],
     totalAmount: map['totalAmount'],
     items: map['items'] == null ? [] :Cart.toOrderItemList(map['items']),
   );
 }

 Map<dynamic, dynamic> toSetMap() {
   return {
     'id': this.id,
     'uid': this.uid,
     'timestamp': this.timestamp,
     'deliveryCharges': this.deliveryCharges,
     'totalAmount': this.totalAmount,
     'items': Cart.toOrderMap(this.items),
   } as Map<dynamic, dynamic>;
 }

}

class Cart {
 String itemId;
 String variationId;
 String itemName;
 String subtitle;
 num itemPerPrice;
 int itemQuantity;

 Cart({
   this.itemId,
   this.variationId,
   this.itemName,
   this.subtitle,
   this.itemPerPrice = 0.0,
   this.itemQuantity = 1,
 });

 factory Cart.fromMap(Map<dynamic, dynamic> map) {
   return new Cart(
     itemId: map['itemId'] as String,
     variationId: map['variationId'] as String,
     itemName: map['itemName'] as String,
     subtitle: map['subtitle'] as String,
     itemPerPrice: map['itemPerPrice'] ,
     itemQuantity: map['itemQuantity'] as int,
   );
 }

 Map<dynamic, dynamic> toMap() {
   return {
     'itemId': this.itemId,
     'variationId': this.variationId,
     'itemName': this.itemName,
     'subtitle': this.subtitle,
     'itemPerPrice': this.itemPerPrice,
     'itemQuantity': this.itemQuantity,
   } as Map<dynamic, dynamic>;
 }

 static Map<dynamic, dynamic> toOrderMap(List<Cart> cartItems) {
   Map<dynamic, dynamic> orderMap = new Map<dynamic, dynamic>();
   for (Cart cart in cartItems) {
     orderMap[cart.variationId] = {
       'itemId': cart.itemId,
       'variationId': cart.variationId,
       'itemName': cart.itemName,
       'subtitle': cart.subtitle,
       'itemPerPrice': cart.itemPerPrice,
       'itemQuantity': cart.itemQuantity,
     };
   }
   return orderMap;
 }

 static toOrderItemList(var map) {
   Map values = map as Map;
   List<Cart> cartItem = [];
   values.forEach((key, data) {
     final Cart connect = Cart.fromMap(data);
     cartItem.add(connect);
   });
  return cartItem;
 }

} }

It is working fine, I set my Object list (cart items) and fetch as whole and convert Map of order to Order Object and then map of CartItem to List to cartItems.

Related