Flutter Firestore: FirestoreBuilder with initial data

152

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
    
}

...
}
Share:
152
Floran
Author by

Floran

Updated on January 04, 2023

Comments

  • Floran
    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 and initialDataparam but since it's expecting stream I didn't know how to cast my data.

  • Floran
    Floran over 1 year
    Thank 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
    Floran over 1 year
    Yes 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 and eventRef 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
    Simon over 1 year
    The 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.