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 r
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>
You can use JsoSerializable()
Add the following dependencies to pubspec.yaml
dependencies:
# Your other regular dependencies here
json_annotation: ^2.0.0
dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.0.0
json_serializable: ^2.0.0
and make your classes JsonSerializable()
import 'package:json_annotation/json_annotation.dart';
part 'game.g.dart';
@JsonSerializable()
class GameReview {
String name;
int howPopular;
List<String> reviewers;
GameReview();
factory GameReview.fromJson(Map<String, dynamic> json) => _$GameReviewFromJson(json);
Map<String, dynamic> toJson() => _$GameReviewToJson(this);
}
@JsonSerializable()
class ItemCount {
int itemType;
int count;
ItemCount();
factory ItemCount.fromJson(Map<String, dynamic> json) => _$ItemCountFromJson(json);
Map<String, dynamic> toJson() => _$ItemCountToJson(this);
}
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();
factory GameRecord.fromJson(Map<String, dynamic> json) => _$GameRecordFromJson(json);
Map<String, dynamic> toJson() => _$GameRecordToJson(this);
}
Then generate the JSON serialization code by running the code generation utility from your terminal:
flutter packages pub run build_runner build
Now you can use jsonEncode() and jsonDecode() in order to store and retrieve objects from the firestore
For setting data:
Firestore.instance
.collection("games")
.document("zZJKQOuuoYVgsyhJJAgc")
.setData(jsonDecode(jsonEncode(gameRecord)));
For retrieving data:
GameRecord.fromJson(jsonDecode(jsonEncode(snapshot.data)));
Firebase packages returns List types for Array/List types present in a snapshot. Try converting List to List or List before assigning to variables. And for GameReview object, currently, you are trying to assign object of Map to the object, it would be beneficial if you write static fromMap method in GameReview class which takes map argument and converts it to desired object structure, as you would see in many of flutters example codes.
class GameReivew{
static GameReivew fromMap(Map<String, dynamic> map){
GameReivew gameReivew = new GameReivew();
gameReivew.name = map["name"];
gameReivew.howPopular = map["howPopular"];
....
return gameReivew;
}
}
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();