How do you load array and object from Cloud Firestore in Flutter
I have a class that has several embedded arrays as well as a couple of objects. I'm using Flutter and can't figure out how to read/write to Cloud Firestore.
I can read/write data members that are default types like String and Int. Here is the constructor I'm trying to use to instantiate an object from a DocumentSnapshot:
class GameReview {
String name;
int howPopular;
List<String> reviewers;
}
class ItemCount {
int itemType;
int count;
ItemCount.fromMap(Map<dynamic, dynamic> data)
: itemType = data['itemType'],
count = data['count'];
}
class GameRecord {
// Header members
String documentID;
String name;
int creationTimestamp;
List<int> ratings = new List<int>();
List<String> players = new List<String>();
GameReview gameReview;
List<ItemCount> itemCounts = new List<ItemCount>();
GameRecord.fromSnapshot(DocumentSnapshot snapshot)
: documentID = snapshot.documentID,
name = snapshot['name'],
creationTimestamp = snapshot['creationTimestamp'],
ratings = snapshot['ratings'], // ERROR on run
players = snapshot['players'], // ERROR on run
gameReview = snapshot['gameReview']; // ERROR on run
itemCount = ????
}
It works until I add the last 3 members (ratings, players and gameReview). This should be obvious but none the less, it eludes me.
Help!
UPDATE: Here is a sample of the document stored in Cloud Firestore. This is stored in a single document. In other words, I'm not using sub-collections for the embedded objects. I put it into a JSON format for clarity. I hope this helps.
{
"documentID": "asd8didjeurkff3",
"name": "My Game Record",
"creationTimestamp": 1235434,
"ratings": [
4,
2012,
4
],
"players": [
"Fred",
"Sue",
"John"
],
"gameReview": {
"name": "Review 1",
"howPopular": 5,
"reviewers": [
"Bob",
"Hanna",
"George"
]
},
"itemCounts": [
{
"itemType": 2,
"count": 3
},
{
"itemType": 1,
"count": 2
}
]
}
UPDATE 2: I didn't put in the whole class definition because I thought it would be obvious to me how to do the rest but alas that was not the case.
I have a list of objects that I want to load.vbandrade's answer is BANG on but I can't quite figure out how I'm supposed to create the list of objects. List.from(...) is looking for an iterator, not a created class. I'm sure it's some variation of creating a new object and then adding it to a list but I'm a little confused. (see edits in class above, specifically, the "itemCounts" member.
Solution 1:
load the list from the array and let the framework take care of type casting.
an object is simply a map, like you wrote in your Json. I also use named constructor. ((still learning and dont know how to use the static constructor @ganapat mentioned))
here´s the working code. I kept firebase auth out and used the StreamBuilder widget.
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'model/firebase_auth_service.dart';
void main() async {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
final firebaseAuth = new FirebaseAuthService();
MyApp() {
firebaseAuth.anonymousLogin();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: FlatButton(
color: Colors.amber,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("get Game Record"),
StreamBuilder<GameRecord>(
stream: getGame(),
builder: (BuildContext c, AsyncSnapshot<GameRecord> data) {
if (data?.data == null) return Text("Error");
GameRecord r = data.data;
return Text("${r.creationTimestamp} + ${r.name}");
},
),
],
),
onPressed: () {
getGame();
},
))));
}
}
Stream<GameRecord> getGame() {
return Firestore.instance
.collection("games")
.document("zZJKQOuuoYVgsyhJJAgc")
.get()
.then((snapshot) {
try {
return GameRecord.fromSnapshot(snapshot);
} catch (e) {
print(e);
return null;
}
}).asStream();
}
class GameReview {
String name;
int howPopular;
List<String> reviewers;
GameReview.fromMap(Map<dynamic, dynamic> data)
: name = data["name"],
howPopular = data["howPopular"],
reviewers = List.from(data['reviewers']);
}
class GameRecord {
// Header members
String documentID;
String name;
int creationTimestamp;
List<int> ratings = new List<int>();
List<String> players = new List<String>();
GameReview gameReview;
GameRecord.fromSnapshot(DocumentSnapshot snapshot)
: documentID = snapshot.documentID,
name = snapshot['name'],
creationTimestamp = snapshot['creationTimestamp'],
ratings = List.from(snapshot['ratings']),
players = List.from(snapshot['players']),
gameReview = GameReview.fromMap(snapshot['gameReview']);
}
snapshot['itemCount']
is an array of objects. map each item in that array to an ItemCount object and return as a List:
itemCounts = snapshot['itemCount'].map<ItemCount>((item) {
return ItemCount.fromMap(item);
}).toList();
Solution 2:
If you came here because of List<dynamic> is not of type List<someType>
error while reading data from firestore, you can just use List.castFrom
Example:
List<String> cards = List.castFrom(cardsListFromFirebase);
Checkout Flutter firebase, List<dynamic> is not of type List<String>