Flutter Firestore: FirestoreBuilder with initial data
Solution 1
Okay, so I found the solution myself after many tries, so I added my Model object that can be null as initialData, but the thing that makes me struggle with is how you get the data in the builder. You have to call different methods depending on where the data is coming from.
return StreamBuilder(
initialData: args.event
ref: eventsRef.doc(args.uid),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
// Here is the trick, when data is coming from initialData you only
// need to call requireData to get your Model
Event event = snapshot.requireData is EventDocumentSnapshot ? snapshot.requireData.data : snapshot.requireData;
return Scafold(); //Rest of my rendering code
}
);
Solution 2
Reading through cloud_firestore's documentation you can see that a Stream from a Query can be obtained via snapshots()
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']),
);
}).toList(),
);
},
);
Solution 3
This won't help you, but with GetX it's simple to implement like this: You don't need StreamBuilder anymore.
//GetXcontroller
class pageController extends GetXcontroller {
...
RxList<EventModel> events = RxList<EventModel>([]);
Stream<List<EventModel>> eventStream(User? firebaseUser) =>
FirebaseFirestore.instance
.collection('events')
.snapshots()
.map((query) =>
query.docs.map((item) => UserModel.fromMap(item)).toList());
@override
void onReady() async {
super.onReady();
events.bindStream(
eventStream(controller.firebaseUser)); // subscribe any change of events collection
}
@override
onClose() {
super.onClose();
events.close(); //close rxObject to stop stream
}
...
}
Floran
Updated on January 04, 2023Comments
-
Floran 10 months
I'm making my first Flutter app and I encounter a problem and doesn't found any solution for it.
I have a view where I render a Firestore document, and there is two ways of getting there:
- From a list where I already loaded my documents
- From Dynamic Links with uid attached as arguments (args)
So in order to listen document changes and loading the data when arriving from the link I used FirestoreBuilder like this:
return FirestoreBuilder<EventDocumentSnapshot>( ref: eventsRef.doc(args.uid), builder: (context, AsyncSnapshot<EventDocumentSnapshot> snapshot, Widget? child) { if (!snapshot.hasData) { return Container(); } Event? event = snapshot.requireData.data; return Scafold(); //Rest of my rendering code } );
How I could avoid first call to Firebase when I already have the data but still listen to changes? The main problem is that my hero animation doesn't work because of this.
I tried with a
StreamBuilder
andinitialData
param but since it's expecting stream I didn't know how to cast my data. -
Floran over 1 yearThank you for your anwser, yes on my previous page it's what I do. On your exemple now imagine you want to display only one Document on a separate view but still able to load it with an uid. How do you do?
-
Floran over 1 yearYes thank you for your anwser but it's actually the case, if you read carefully the code I pasted you will see an
Event
class that is the mapping andeventRef
is the ODM. And you do not helping with the problem that was how to provide initial data to the stream when I have it. -
Simon over 1 yearThe solution you provided yourself still requests the document. The solution will cause an additional rebuild of the tree immediately after initial rendering unless you work offline or there is considerable network latency. The idea of a stream is to always get the latest data - the initial data is provided in any case. The problem you actually have is better solved through proper state management rather than providing initial data to a builder. Have a look at the links I provided at the end.