StreamBuilder shows only the results of the second MergeStream element - flutter

I'm trying to use a SearchDelegate() that can search among more than one stream. Now I'm trying to search among two. To merge both FireStore streams, I'm using MergeStream(). I'm not getting errors, but I only can see the results of the second stream. This is my code:
Stream stream1 =
FirebaseFirestore.instance.collection('stream1').snapshots();
Stream stream2 =
FirebaseFirestore.instance.collection('stream2').snapshots();
Stream<QuerySnapshot> mergedStream =
MergeStream([stream1, stream2]);
return StreamBuilder(
stream: mergedStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Center(
child: Text('Loading...'),
);
final results = snapshot.data.docs.where(
(DocumentSnapshot a) => a.data().toString().toLowerCase().contains(
query.toLowerCase(),
),
);
return Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
child: ListView(................ etc,

Using Rx.combineLatest instead of merge

Related

Flutter, Stream prints null even if there is data in firebase

I tried printing firebase data using stream, but instead of the data I got this in terminal " [ ] ". This means the data is null. But there is data in firebase, how do I solve this problem.
firebase's data
stream builder
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('paymentData')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return const Center(
child: Text('Loading'),
);
}
print(snapshot.data!.docs);
return ListView(
children: snapshot.data!.docs.map((data) {
return ListTile(
title: Text(data['amount']),
);
}).toList());
},
),
Based on your firebase structure, there is no path called paymentData, it is actually a nested sub-collection, so in order to reach paymentData, you need to also include the parent of that collection, like this:
stream: FirebaseFirestore.instance
.collection('lender').doc(yourLenderId).collection('paymentData')
.snapshots(),
Get the lender ID from a previus step, which is the id of the document

(Flutter) StreamBuilder returns only null

I am trying to create a "CategoryStream" to update the UI based on the users choice.
This is my stream:
import 'dart:async';
class CategoryStream {
StreamController<String> _categoryStreamController =
StreamController<String>();
closeStream() {
_categoryStreamController.close();
}
updateCategory(String category) {
print("Update category in stream was called");
print("the new category is: " + category);
_categoryStreamController.sink.add(category);
}
Stream<String> get stream => _categoryStreamController.stream;
}
And my StreamBuilder looks like this:
return StreamBuilder<String>(
stream: CategoryStream().stream,
builder: (context, snapshot) {
return Container(
color: Colors.white,
child: Center(
child: Text(snapshot.data.toString()),
),
);
},
);
So when the User choses a new category, i try to update the Stream like this:
CategoryStream().updateCategory(currentChosenCategory);
Whatever i do, the result is always null. Although the right category is displayed in the print() function...
What am i missing?
Maybe i need to work with a StreamProvider? Not a StreamBuilder? Because i am adding the data from a Parent-Widget to a Child-Widget of a Child-Widget..
By default, the value of a StreamController is null. You need to set at initialData or add data to the stream before you call snapshot.data in your StreamBuilder:
final CategoryStream _categoryStream = CategoryStream();
return StreamBuilder<String>(
stream: _categoryStream.stream,
initialData: "",
builder: (context, snapshot) {
return Container(
color: Colors.white,
child: Center(
child: Text(snapshot.data), //toString() is unnecessary, your data is already a string
),
);
},
);

Flutter - How to Merge Two Stream in Flutter ? Why I'a getting Only Last Stream?

I want to Merge two Firestore Stream in a Futter. I have tried so many ways But in some cases, I got an error and others only return the last stream, not the merged Stream. This is my code
class Classes extends StatefulWidget {
#override
_ClassesState createState() => _ClassesState();
}
class _ClassesState extends State<Classes> {
String path0 = "class_schedule/Classes";
String path1 = "users/R2z6mt5/Classes";
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: Rx.merge([
Firestore.instance.collection(path1).snapshots(),
Firestore.instance.collection(path0).snapshots()
]),
builder: (context, snapshot) {
return !snapshot.hasData
? Text('PLease Wait')
: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot items = snapshot.data.documents[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.yellow,
height: 50,
width: double.infinity,
child: Center(
child: Text(
items['Course'],
style: TextStyle(
fontSize: 24,
),
),
),
),
);
},
);
}),
);
}
}
Please Tell My How can I Solve this Issue?
Thanks in Advance
if I use StreamZip then I got "Class 'List' has no instance getter 'documents'. Receiver: Instance(length:2) of '_GrowableList' Tried calling: documents "
It is because when you merge with StreamZip, every stream event will be A list,
for example if 2 streams A and B which emit int, merging them with StreamZip will emit List<int> of length 2, at index 0 will be the event of Stream A and at index 1 will be the event of Stream B, So you need to access them like zippedStreamEvent[0].docs and zippedStreamEvent[1].docs
let me give you a code sample
StreamBuilder<List<QuerySnapshot>>(
stream: yourZippedStream,
builder: (_, snap) {
if(!snap.hasData){
return CircularProgressIndicator();
}
final allDocs = [...snap.data[0].docs, snap.data[1].docs];
return ListView.builder(itemBuilder: (_, i) {
final doc = allDocs[i];
return YourListWidget();
});
},
);
You can use this library to merge two streams together. See documentation for better understanding.
As per docs, StreamZip:
[E]mits lists of collected values from each input stream. The first list
contains the first value emitted by each stream, the second contains
the second value, and so on.
Hence, the type of snapshot.data in your code is List<QuerySnapshot>, which obviously doesn't have a documents getter. You would access the first QuerySnapshot like this: snapshot.data[0], then call documents on that. Similarly, the second would be snapshot.data[1], etc.

Firestore compound query not working for Flutter Web

Hi I have a simple query for Firestore in a StreamBuilder
StreamBuilder(
stream: FirestoreManager.firebaseFirestore
.collection("orders")
.orderBy('logs.0', descending: true)
.where('status', whereIn: current['id'])
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snap) {
print(snap.data.toString());
if (!snap.hasError && snap.hasData) {
QuerySnapshot snapshot = snap.data;
if (snapshot.documents.isNotEmpty) {
List<DocumentSnapshot> snapList = snapshot.documents;
return ListView.builder(
padding: EdgeInsets.only(right: 10, left: 10),
physics: ScrollPhysics(),
itemCount: snapList.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return OrderListItem(
order: Order.fromJson(snapList[index].data),
);
},
);
} else {
return Center(
child: Text(
"No ${current['status'].toString().trim()} Order Available...!",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
})
It works absolutely fine for mobile appilcaiton but when I try to run it for web, It doesn't work.
Actually it shows data once for a second and again disappears. I got following log in console by printing snapshot data using print(snap.data.toString());
js_primitives.dart:30 null
js_primitives.dart:30 Instance of 'QuerySnapshot'
js_primitives.dart:30 null
Why is this happening? Why it shows data once and again disappear it?
If I remove either .orderBy('logs.0', descending: true) or .where('status', whereIn: current['id']) then it works fine.
I am having the same problem with flutter web. I am trying to use two chained where() methods and it doesn't give me any results:
StreamBuilder(
stream: fire
.collection('thiHistory')
.where("device", "==", _device)
.where("ts", '>=', ts)
.onSnapshot,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return Text('Loading....');
final docs = snapshot.data.docs;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: docs.length,
itemBuilder: (context, index) {
final doc = docs[index].data();
return doc["device"] == _device ? Column(
children: [
Text(doc['device']),
Text("${doc["hour"]}"),
Text("${doc["temp"]}"),
Text("${doc["humi"]}"),
Text("${doc["thi"]}"),
],
) : Container();
});
},
)
If I only use one where() or the other it works fine. If I use both, I always get "Loading....", which tells me the snapshot has no data.
I also tried chaining orderedBy() with where() like Shahzad Akram, and same thing... Using one or the other works great, combining them does not return data.
I am using the firebase 7.3.0 package and version 7.19.1 of the libraries:
<script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-database.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-firestore.js"></script>
Thanks for your help.

How to get a document snapshot index

I'm new to flutter and I'm trying to pass a firestore document snapshot to another class.
I passed to the Profile class a snapshot document, and I want to indicate the index of my document, but I don't know how to get it
I have this
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: ((searchString != null) &&
(searchString.trim() != ""))
? Firestore.instance
.collection('pazienti')
.where("searchIndex",
arrayContains: searchString)
.snapshots()
: Firestore.instance
.collection('pazienti')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
return ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return Card(
elevation: 10.00,
margin: EdgeInsets.all(0.50),
child: ListTile(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[????])));
}
,
leading: CircleAvatar(
backgroundColor:
Colors.blueGrey.shade800,
),
title: Text(document['cognome'] +
" " +
document['nome']),
subtitle: Text(document['cognome'] +
" " +
document['nome']),
),
);
}).toList(),
);
}
})),
],
),
)
My problem is essentially here
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[XXXX]))
How can I get the index of the document from the map I used?
Thank you very much for your help
You just want to pass document on which tap, so you can simply pass document which you are getting from map method.
Snapshots from a query don't have a numeric index. The results from a query could change at any time between queries, and the system can not guarantee that any sort of index would be stable.
If you want to pass a document to another function, pass its unique document ID. The receiver can then query the document directly, perhaps from local cache without requiring a billed read operation at the server.
var listIndex= snapshot.data.documents.map((e) => e.data['key']);
var passIndex = listIndex.toList().indexOf(doc.data['key']);
print(passIndex);
You can simply pass the index when assigning the list
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildItem(context, data, snapshot.indexOf(data))).toList(),
);
}