Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter merge two firestore streams into a single stream

I simply want to perform an 'OR' operation and get the both results of two queries into one stream.

Here's my code with a single stream

 StreamBuilder(
    stream: Firestore.instance
        .collection('list')
        .where('id', isEqualTo: 'false')
        .orderBy('timestamp')
        .snapshots(),
    builder: (context, snapshot) {
      if (!snapshot.hasData)
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Center(
              child: CircularProgressIndicator(),
            )
          ],
        );
      if (snapshot.data.documents.length == 0)
        return const Center(
          child: Text(
            "Not Available",
            style: TextStyle(fontSize: 30.0, color: Colors.grey),
          ),
        );
      return ListView.builder(
        padding: EdgeInsets.all(5.0),
        key: Key(randomString(20)),
        itemCount: snapshot.data.documents.length,
        itemBuilder: (BuildContext context, int index) {
          return ListCard(snapshot.data.documents[index]);
        },
      );
    }),

Instead of a single stream now I want to feed two stream to the same stream builder.

I tried StreamGroup but it's not working since Widgets rebuild

StreamGroup.merge([streamOne, streamTwo]).asBroadcastStream();

I tried followed method also

 Stream<List<DocumentSnapshot>> searchResult()  {
List<Stream<List<DocumentSnapshot>>> streamList = [];

Firestore.instance
    .collection('room-list')
    .where('id', isEqualTo: 'false')
    .snapshots()
    .forEach((snap) {
  streamList.add(Observable.just(snap.documents));
});

Firestore.instance
    .collection('room-list')
    .where('id', isEqualTo: 'pending')
    .snapshots()
    .forEach((snap) {
  streamList.add(Observable.just(snap.documents));
});

var x = Observable.merge(streamList)
    .scan<List<DocumentSnapshot>>((acc, curr, i) {
  return acc ?? <DocumentSnapshot>[]
    ..addAll(curr);
});
return x;
}

Here I get the error there should be at least a single stream to merge. Its because Observable.merge(streamList) is called before items are added to streamList.

I simply want to get the both results of two queries into one stream.

like image 530
Praneeth Dhanushka Fernando Avatar asked Nov 13 '18 18:11

Praneeth Dhanushka Fernando


People also ask

How do you combine two streams in Dart?

You can use StreamZip in package:async to combine two streams into one stream of pairs, then create the C objects from that. Note however, StreamZip only emits pairs. If As and Bs emit at different rates, C will wait until they've both emitted a value before emitting a merged pair.

What is StreamBuilder and stream controller flutter?

A stream can have multiple listeners and all those listeners can get the pipeline. puting in all will get equal value. The way you put values on the stream is by using the Stream Controller. A stream builder is a widget that can convert user-defined objects into a stream.

How many stream builder are in flutter?

There are two types of streams in Flutter: single subscription streams and broadcast streams. Single subscription streams are the default.

What are streams in flutter?

Streams provide an asynchronous sequence of data. Data sequences include user-generated events and data read from files. You can process a stream using either await for or listen() from the Stream API. Streams provide a way to respond to errors. There are two kinds of streams: single subscription or broadcast.


2 Answers

This should work.

//Change your streams here
    Stream<List<QuerySnapshot>> getData() {
        Stream stream1 = Firestore.instance.collection('list').where('id', isEqualTo: 'false').orderBy('timestamp').snapshots();
        Stream stream2 = Firestore.instance.collection('list').where('id', isEqualTo: 'true').orderBy('timestamp').snapshots();
        return StreamZip([stream1, stream2]);
      }


  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: StreamBuilder(
          stream: getData(),
          builder: (BuildContext context, AsyncSnapshot<List<QuerySnapshot>> snapshot1) {

            List<QuerySnapshot> querySnapshotData =  snapshot1.data.toList();

            //copy document snapshots from second stream to first so querySnapshotData[0].documents will have all documents from both query snapshots
            querySnapshotData[0].documents.addAll(querySnapshotData[1].documents);

            if (querySnapshotData[0].documents.isEmpty)
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Center(
                    child: CircularProgressIndicator(),
                  )
                ],
              );
            if (querySnapshotData[0].documents.length == 0)
              return const Center(
                child: Text(
                  "Not Available",
                  style: TextStyle(fontSize: 30.0, color: Colors.grey),
                ),
              );

            return new ListView(
                children: querySnapshotData[0].documents.map((DocumentSnapshot document){
                 // put your logic here. You will have access to document from both streams as "document" here
                  return new ListCard(document);
                }).toList()
            );
          }
      ),
    );
  }

Hope this helps!!!

like image 112
m-bhole Avatar answered Sep 21 '22 15:09

m-bhole


I’m not sure why you’re using forEach and Observable.just().

You can just merge two firestore streams directly like:

Observable.merge([stream1, stream2]).pipe(combineStream);

Wherre stream1/2 is just your firestore snapshot.

like image 31
Jason Scott Avatar answered Sep 21 '22 15:09

Jason Scott