How to use StreamZip with StreamBuilder? - flutter

I have two stream
stream1
stream2
I can give one to StreamBuilder and it work. For example:
return StreamBuilder(
stream: stream1,
But when I combine with StreamZip it now give error:
StreamZip combinedStream() {
return StreamZip(stream1, stream2]);
}
return StreamBuilder(
stream: combinedStream,
How I can combine stream1 and stream2 and give to StreamBuilder?

Stream<List<QuerySnapshot>> combineStream() {
return StreamZip([stream1, stream2]);
}
return StreamBuilder(
stream: combineStream(),
builder: (context, snapshot) {
List<DocumentSnapshot> documentSnapshot = [];
List<dynamic> querySnapshot = snapshot.data.toList();
querySnapshot.forEach((query) {
documentSnapshot.addAll(query.docs);
})
}
);
Your documentSnapshot now contains your combined streams

You can use StreamGroup.merge to merge two Streams into a single Stream:
StreamBuilder(
stream: StreamGroup.merge(stream1, stream2),
Package dart:async

import 'package:rxdart/rxdart.dart';
import 'package:tuple/tuple.dart';
final Stream<A> stream1;
final Stream<B> stream2;
final Stream<Tuple2<A, B>> stream = Rx.zip2(
stream1,
stream2,
(A a, B b) => Tuple2(a, b),
);
StreamBuilder<Tuple2<A, B>>(
stream: stream,
builder: (ctx, snapshot) { ... }
);

Related

how to create UDF for Strembuilder's stream value in flutter

I am trying to create a UDF for stream builder's stream value
this is my code without UDF
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("chatrooms")
.snapshots(),
builder: (context, snapshot) {
now I want to change with udf but don't know what's return type should i take...
want to convert as following
child: StreamBuilder(
stream: myudf(),// this is what I want to create
builder: (context, snapshot) {
void myudf()//pls correct me ,udf type
{
//what return type should I apply for this myudf function and what to implement in body
FirebaseFirestore.instance
.collection("chatrooms")
.where("participants.${widget.usermodel.uid}", isEqualTo: true)
.snapshots(),
}
Try to use Stream return type
Stream myudf()//pls correct me ,udf type
{
//what return type should I apply for this myudf function and what to implement in body
return FirebaseFirestore.instance
.collection("chatrooms")
.where("participants.${widget.usermodel.uid}", isEqualTo: true)
.snapshots(),
}

how to perform query to Firestore and order result Randomly in Flutter

I am retrieving specific documents from firestore collection using flutter stream builder.
the issue is I would like to display the results every single time in a different order (Randomely).
the stream is the below:
stream: FirebaseFirestore.instance
.collection('BusinessProfilesCollection')
.where('Profile_direct_category',
isEqualTo: selecteddirectcategory)
.where('Profile_status', isEqualTo: "Active")
.where('Profile_visibility', isEqualTo: "Yes")
.where('Profile_city',
isEqualTo: globaluserdefaultcity)
.where('Profile_pinning_status',
isEqualTo: "No")
.snapshots(),
the problem is everytime the user do the query the data is returned in the same order. I would like to shuffle it somehow so I remove any advantage from any profile. (document)
I assume you have a list somewhere, where you display your documents? If so, you can use the .shuffle() operator on it! Example:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
String selecteddirectcategory = 'selecteddirectcategory';
String globaluserdefaultcity = 'globaluserdefaultcity';
class RandomResultsScreen extends StatefulWidget {
#override
_RandomResultsScreenState createState() {
return _RandomResultsScreenState();
}
}
class _RandomResultsScreenState extends State<RandomResultsScreen> {
Stream<QuerySnapshot> myStream = FirebaseFirestore.instance
.collection('BusinessProfilesCollection')
.where('Profile_direct_category', isEqualTo: selecteddirectcategory)
.where('Profile_status', isEqualTo: "Active")
.where('Profile_visibility', isEqualTo: "Yes")
.where('Profile_city', isEqualTo: globaluserdefaultcity)
.where('Profile_pinning_status', isEqualTo: "No")
.snapshots();
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: myStream,
builder: (context, asyncSnapshot) {
List<Widget> docs = [];
QuerySnapshot? foundResults = asyncSnapshot.data;
if (foundResults == null) {
//It always wants to be null at first, and then you get errors for calling on null.
return Center(child: CircularProgressIndicator());
} else {
for (QueryDocumentSnapshot doc in foundResults.docs) {
Map<String, dynamic> docData = doc.data() as Map<String, dynamic>;
docs.add(
MyWidget(docData) // Some Widget that you use to display your data
);
}
docs.shuffle(); // <- Where the magic randomization happens!
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
return docs[index];
},
);
}
},
),
);
}
}

Getting QuerySnapshot instead of DocumentSnapshot [FLUTTER]

StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
FirestoreUser firestoreUser =
FirestoreUser.fromDocument(snapshot.data); // Here snapshot.data is retrieving QuerySnapshot.
// How I can convert it to DocumentSnapshot
...
Hello StackOverflow users
I need to give to my new firestoreUser variable, which type of DocumentSnapshot. But I can't get it.
After writing snapshot.data it gives me an error called "QuerySnapshot is not subtype of DocumentSnapshot"
P.s I'm using StreamBuilder as you can see. Thank you
Your stream is FirebaseFirestore.instance.collection('users').snapshots() which is a QuerySnapshot, meaning, a List of QueryDocumentSnapshot which Extends DocumentSnapshot.
So if you want the documentSnapshot of every users in your 'users' collection, you will have to iterate over snapshot.data.docs:
But if you want to get the document of a particular user, then you can do:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').doc(userID).snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
FirestoreUser firestoreUser =
FirestoreUser.fromDocument(snapshot.data);
...

How can you nest StreamBuilders in Flutter?

I have 2 Streams that I need to combine to build a widget, but unlike other questions I have seen I need to nest my streams.
I have a stream that gets a collection of documents from Firestore, and a stream that depends on data from the first to get a subcollection of documents. I would like to combine these into one stream, but they need to be nested since each document has its own subcollection of documents.
Stream 1 (Gets a collection of habits from FireStore):
Stream<List> getHabits(){
final Stream<QuerySnapshot> documents = Firestore.instance
.collection("users")
.document('VtL1sxOoCOdJaOTT87IbMRwBe282')
.collection("habits")
.snapshots();
Stream<List> data = documents.map((doc) {
List data;
final documents = doc.documents;
///Maybe this would work to get history of each doc?
for(int i = 0; i < documents.length; i++){
///not sure what to do
getHistory(documents[i].documentID, DateTime.utc(2019,7,7), DateTime.now());
}
data = documents.map((documentSnapshot) => documentSnapshot).toList();
return data;
});
return data;
}
Stream 2 (Called in Stream 1, Takes DocumentID as a parameter, gets sub-collection of documents):
Stream<List> getHistory(String id, DateTime start, DateTime end) async* {
await for (QuerySnapshot querySnapshot in Firestore.instance
.collection("users")
.document('VtL1sxOoCOdJaOTT87IbMRwBe282')
.collection("habits")
.document(id)
.collection("history")
.where('day', isGreaterThanOrEqualTo: start)
.where('day', isLessThanOrEqualTo: end)
.snapshots()) {
List history;
final documents = querySnapshot.documents;
history = documents.map((documentSnapshot) => documentSnapshot).toList();
yield history;
}
}
Any help on how I can combine these streams in a nested format into one stream to be used with StreamBuilder in flutter would be appreciated!'
EDIT
I am not sure if I am working in the right direction or not but I have tried to implement the solution from spenster and this is what I have at the moment in addition to the functions above.
StreamBuilder<List>(
stream: getHabits(),
initialData: [],
builder: (context, snapshot) {
List<UserHabit> habits = [];
List<Widget> test = List.generate(snapshot.data.length, (index){
List<History> history = [];
DocumentSnapshot doc = snapshot.data[index];
return StreamBuilder(
stream: getHistory(doc.documentID, DateTime.utc(2019,7,7), DateTime.now()),
builder: (context, snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
if(!snapshot.data.isEmpty){ //history collection exists
for(int i = 0; i < snapshot.data.length; i++){
//add to history
history.add(History(
day: snapshot.data[i]['day'].toDate(),
dateCompleted: snapshot.data[i]['dateCompleted'].toDate(),
morning: snapshot.data[i]['morning'],
afternoon: snapshot.data[i]['afternoon'],
evening: snapshot.data[i]['evening'],
anytime: snapshot.data[i]['anytime'],
));
}
}
habits.add(UserHabit(
name: doc['habit'],
color: doc['color'],
icon: doc['icon'],
repeat: doc['repeat'],
daily: doc['daily'],
weekly: doc['weekly'],
monthly: doc['monthly'],
time: doc['time'],
history: history,
));
print(habits); //returns each iteration of assembling the list
return Text("i dont want to return anything");
}
},
);
}
);
print(habits); //returns empty list before anything is added
return Column(
children: test,
);
},
),
The Class for UserHabits and History can be shared, but they are just basic classes that assign types and allow easy access.
I have done something similar simply using nested StreamBuilders. Depending on how you want your Widgets organized, you can create streams within the outer StreamBuilder. Based on your clarifying comments, this is one possibility:
#override
Widget build(BuildContext context) {
var habits = Firestore.instance
.collection("users")
.document('VtL1sxOoCOdJaOTT87IbMRwBe282')
.collection("habits")
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: habits,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Text("Loading habits...");
return ListView(children: snapshot.data.documents.map((document) {
var query = Firestore.instance
.collection("users")
.document('VtL1sxOoCOdJaOTT87IbMRwBe282')
.collection("habits")
.document(document.documentID)
.collection("history")
.where('day', isGreaterThanOrEqualTo: start)
.where('day', isLessThanOrEqualTo: end)
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: query,
builder: (context, snapshot) {
if (!snapshot.hasData) return Text("Loading...");
// right here is where you need to put the widget that you
// want to create for the history entries in snapshot.data...
return Container();
},
);
}).toList());
},
);
}
Try merging your streams with something like Observable.zip2(stream1,stream2,zipper) or Observable.combineLatest2(streamA, streamB, combiner).
For more info, check this post

`Future<Stream>` is not a subtype of type `Stream`

I made simple singleton class to use on flutter StreamBuilder widget, for example:
StreamBuilder<User>(
stream: MydbModel.create().then((dao) => dao.getUserStream()) as Stream<User>,
builder: (_, snapshot) {
...
},
)
in that
MydbModel.create().then((dao) => dao.getUserStream())
return Future<Stream<User>> and my cast don't work to have only Stream<User>.
Error:
> type 'Future<Stream<User>>' is not a subtype of type 'Stream<User>' in type cast
how can i resolve this problem?
my completed class:
class MydbModel{
UserDao _userDao;
MydbModel._(this._userDao);
static Future<MydbModel> create() async => MydbModel._(await initialDatabase());
static Future<UserDao> initialDatabase() async {
var db = await $FloorAppDatabase.databaseBuilder('flutter_database.db').build();
return db.userDao;
}
Stream<User> getUserStream(){
return userDao.getUserInfo();
}
UserDao get userDao=>_userDao;
}
You need to await the Future first:
FutureBuilder(
future: MydbModel.create(),
builder: (BuildContext context, AsyncSnapshot<MydbModel> snapshot) {
if (!snapshot.hasData) return Container();
return StreamBuilder(
stream: snapshot.data.getUserStream(),
builder: ..,
);
},
);
I used a FutureBuilder to await the Future and to create a StreamBuilder when the future is loaded.