deathyr
deathyr

Reputation: 423

Flutter Firestore Stream<QuerySnapshot> to Stream<DocumentSnapshot>

My Flutter project has two main pages:
a) A list page which uses a QuerySnapshot to list all the documents, and for each one of these documents you can click a button to land on
b) a detail page which lists and update the details of a single document.

Ideally the details page should use a stream too, but given this is for a single document it should be a DocumentSnapshot stream instead.

From the QuerySnapshot object I already can get a DocumentSnapshot, but that seems to be static and doesn't reflect the updates in Firestore. Is there a way to get a Stream<DocumentSnapshot> instead?

I know one way would be to reissue another query for the doc instead of a collection (similar to this FirebaseFirestore.instance.doc('order/${widget.orderid}').snapshots()), but ideally I would like to avoid another read/query since I'm already listening to these objects. Is that possible?

  final Stream<QuerySnapshot> _bundlesStream =
      FirebaseFirestore.instance.collection(orderTable)
          .where("pickupPending", isEqualTo: true)
          .orderBy("orderDate", descending: true)
          .snapshots();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _bundlesStream,

              children: snapshot.data!.docs.asMap().map((int i, DocumentSnapshot document) {
                Map<String, dynamic> data =
                    document.data()! as Map<String, dynamic>;
                Order order = Order.fromJson(data);
                // createWidget returns a button that goes to the detail page
                // How can I get a Stream<DocumentSnapshot> here? 
                return  MapEntry(i, createWidget(i, order));
              }).values.cast<Widget>().toList(),


Upvotes: 1

Views: 999

Answers (1)

CLucera
CLucera

Reputation: 1201

Ideally what you can do (assuming that your stream is at a higher level and inside some kind of state management that renders it accessible from both widgets) is to pass the id to your detail widget, then use the same stream and from the QuerySnapshot you pick the one with that id for building your widget, this way since the DetailWiget have a StreamBuilder listening to the changes on the list it will rebuild if something changed on it.

The drawback of this approach is that anytime the list is updated (and not just your Document) you will get the Detail rebuilt.

Something like this:

class DetailWidget extends StatelessWidget {
  
  const DetailWidget(this.id);
  
  final String id;
  
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _bundlesStream,
      builder: (context, snapshot) {
        //use the ID here to retrieve the document
        final document = snapshot.data.docs.firstWhere(...);
        //build the widget
        return Container(...);
      }
     );
  }
}

Personally, I would use the second query even if it means another reading.

Upvotes: 1

Related Questions