💥 Advanced Firestore
Data Organization and Deserialization
Data Models
Model Firestore data as a Dart class.
models.dart
class Option {
String value;
String detail;
bool correct;
Option({ this.correct, this.value, this.detail });
Option.fromMap(Map data) {
value = data['value'];
detail = data['detail'] ?? '';
correct = data['correct'];
}
}
class Question {
String text;
List<Option> options;
Question({ this.options, this.text });
Question.fromMap(Map data) {
text = data['text'] ?? '';
options = (data['options'] as List ?? []).map((v) => Option.fromMap(v)).toList();
}
}
///// Database Collections
class Quiz {
String id;
String title;
String description;
String video;
String topic;
List<Question> questions;
Quiz({ this.title, this.questions, this.video, this.description, this.id, this.topic });
factory Quiz.fromMap(Map data) {
return Quiz(
id: data['id'] ?? '',
title: data['title'] ?? '',
topic: data['topic'] ?? '',
description: data['description'] ?? '',
video: data['video'] ?? '',
questions: (data['questions'] as List ?? []).map((v) => Question.fromMap(v)).toList()
);
}
}
class Topic {
final String id;
final String title;
final String description;
final String img;
final List<Quiz> quizzes;
Topic({ this.id, this.title, this.description, this.img, this.quizzes });
factory Topic.fromMap(Map data) {
return Topic(
id: data['id'] ?? '',
title: data['title'] ?? '',
description: data['description'] ?? '',
img: data['img'] ?? 'default.png',
quizzes: (data['quizzes'] as List ?? []).map((v) => Quiz.fromMap(v)).toList(), //data['quizzes'],
);
}
}
class Report {
String uid;
int total;
dynamic topics;
Report({ this.uid, this.topics, this.total });
factory Report.fromMap(Map data) {
return Report(
uid: data['uid'],
total: data['total'] ?? 0,
topics: data['topics'] ?? {},
);
}
}
Global Data Mapping
Map the data types to their constructors so they can be instantiated dynamically.
globals.dart
import 'services.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
/// Static global state. Immutable services that do not care about build context.
class Global {
// Data Models
static final Map models = {
Topic: (data) => Topic.fromMap(data),
Quiz: (data) => Quiz.fromMap(data),
Report: (data) => Report.fromMap(data),
};
}
Data Fetching Services
Create generic services for fetching data from Firestore as a document or Collection.
db.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import './globals.dart';
class Document<T> {
final Firestore _db = Firestore.instance;
final String path;
DocumentReference ref;
Document({ this.path }) {
ref = _db.document(path);
}
Future<T> getData() {
return ref.get().then((v) => Global.models[T](v.data) as T);
}
Stream<T> streamData() {
return ref.snapshots().map((v) => Global.models[T](v.data) as T);
}
Future<void> upsert(Map data) {
return ref.setData(Map<String, dynamic>.from(data), merge: true);
}
}
class Collection<T> {
final Firestore _db = Firestore.instance;
final String path;
CollectionReference ref;
Collection({ this.path }) {
ref = _db.collection(path);
}
Future<List<T>> getData() async {
var snapshots = await ref.getDocuments();
return snapshots.documents.map((doc) => Global.models[T](doc.data) as T ).toList();
}
Stream<List<T>> streamData() {
return ref.snapshots().map((list) => list.documents.map((doc) => Global.models[T](doc.data) as T) );
}
}