The above described functionalities are NOT POSSIBLE with:
FutureProvider
: you can use this to retrieve a document from firestore and show a loading-screen while waiting, but can't make changes to it afterwards, that will also show in the UI
FutureBuilder
: same as above, even worse, this can't provide data down the widget-tree
It was however possible with ChangeNotifierProvider
(& ChangeNotifier
), and this is how I did it:
enum DocumentDataStatus { noData, loadingData, dataAvailable }
...
class QuestionModel extends ChangeNotifier {
DocumentSnapshot _questionDoc; //the document object, as retrieved from firestore
dynamic _data; //the data of the document, think of it as Map<String, dynamic>
DocumentDataStatus _documentDataStatus;
DocumentDataStatus get status => _documentDataStatus;
//constructors
QuestionModel.example1NoInitialData() {
_documentDataStatus = DocumentDataStatus.noData; //no question selected at first
}
QuestionModel.example2WithInitialData() {
_documentDataStatus = DocumentDataStatus.loadingData; //waiting for default document or something...
//can't use async/await syntax in a dart constructor
FirebaseFirestore.instance.collection('quiz').doc('defaultQuestion').get().then((doc) {
_questionDoc = doc;
_data = _questionDoc.data();
_documentDataStatus = DocumentDataStatus.dataAvailable;
notifyListeners(); //now UI will show document data
});
}
//all kinds of getters for specific data of the firestore document
dynamic get questionText => _data['question'];
dynamic get answerText => _data['answer'];
// dynamic get ...
///if operation too complex to update in frontend (or if lazy dev), just reload question-document
Future<void> loadQuestionFromFirestore(String questionID) async {
_data = null;
_documentDataStatus = DocumentDataStatus.loadingData;
notifyListeners(); //now UI will show loading-screen while waiting for document
_questionDoc = await FirebaseFirestore.instance.collection('quiz').doc(questionID).get();
_data = _questionDoc.data();
_documentDataStatus = DocumentDataStatus.dataAvailable;
notifyListeners(); //now UI will show document data
}
///instantly update data in the UI
void updateQuestionTextInUI(String newText) {
_data['question'] = newText; //to show new data in UI (this line does nothing on the backend)
notifyListeners(); //UI will instantly update with new data
}
}
...
Widget build(BuildContext context) {
QuestionModel questionModel = context.watch<QuestionModel>();
return [
Text('No question to show'),
Loading(),
Text(questionModel.questionText),
][questionModel.status.index]; //display ONE widget of the list depending on the value of the enum
}
The code in my example is simplified for explanation. I have implemented it in a larger scale, and everything works just as expected.