convert this into streambuilder in flutter - flutter

I want to convert this function into Streambuilder, but somehow I could not figure out how I could do it. Any help would be greatly appreciated.
Future getReceiverChats() async {
var data = await FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.get();
setState(() {
_msgReceiverList =
List.from(data.docs.map((doc) => Message.fromMap(doc)));
});
}

Try this:
Stream<List<Message>> getReceiverChats(String id) {
return FirebaseFirestore.instance
.collection("message")
.doc(id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots()
.map((QuerySnapshot query) {
List<Message> dataList = [];
query.docs.forEach((doc) {
dataList
.add(Message.fromMap(doc));
});
return dataList;
});
}
Then:
StreamBuilder<List>(
stream: getReceiverChats(widget.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<Message>? dataList = snapshot.data;
if (dataList!.isEmpty) {
return Center(
child: Text('No results'),
);
}
return ListView.builder(
itemCount: dataList.length,
itemBuilder: (context, index) {
return MyWidget(dataList[index]);
});
}
if (snapshot.connectionState == ConnectionState.done) {
if (!snapshot.hasData) {
return Center(
child: Text('No results'),
);
}
}
return const Center(
child: CircularProgressIndicator(),
);
})

StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text("Loading...");
default:
return ListView(
children: snapshot.data.docs.map((doc) {
return Message.fromMap(doc);
}).toList(),
);
}
},
),

Related

Why when i add data to firebase, the data keep looping?

I'm new to flutter and firebase and I do not know why when I upload any data to firebase the data will keep repeating the same thing but when I hot restart the upload item back to 1, this is my code:
Future getDocId() async {
await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get()
.then(
(snapshot) => snapshot.docs.forEach(
(document) {
print(document.reference);
docIDs.add(document.reference.id);
},
),
);
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: docIDs[index]),
tileColor: Colors.purple[100],
),
);
},
);
},
),
),
This is my future builder
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text('Name:' +
'${data['name']}' +
"\n"
'Email:' +
'${data['email']}' +
"\n"
'Mobile Number:' +
'+' +
'${data['mobile']}' +
"");
}
return Text('Loading..');
}),
);
the way you are fetching your data is wrong, instead of pass the result into outside variable you need to return it like this, I assume docIDs is a list of string:
Future<List<String>> getDocId() async {
try {
var snapshot = await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get();
return snapshot.docs.map((document) => document.reference.id).toList();
} catch (e) {
return [];
}
}
then change your FutureBuilder to this:
FutureBuilder<List<String>>(
future: getDocId(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<String> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: data[index]),
tileColor: Colors.purple[100],
),
);
},
);
}
}
},
),

Modify Future Builder inside Stream Builder to avoid widget flickering

I am using a FutureBuilder inside a StreamBuilder that updates the UI every time a document is added to the activity collection, to get some aditional data from Firestore. The problem is that the FutureBuilder returns a SizedBox widget while the ConnectionState is waiting causing the all the cards to dissapear for a second. I would like to avoid this flickering since it causes a bad ui experience for users.
Is there a way to query the required user data in the activity stream so it all returns at once that way I can remove the FutureBuilder?
If not ... what would be a solution for this?
activityStream() {
return FirebaseFirestore.instance
.collection('activity')
.orderBy('timestamp', descending: true)
.limit(55)
.snapshots();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
StreamBuilder<QuerySnapshot>(
stream: activityStream(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
final activityContent = snapshot.data?.docs
.map((data) => ActivityModel.fromFirestore(data))
.toList();
return Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: activityContent[i].players)
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
final users = snapshot.data!.docs.map((e) {
return UserModel.fromFirestore(e);
}).toList();
return MyWidget(
users: users,
);
},
);
},
),
);
}
},
);

Fetching data from Firebase into listview in flutter

I'm trying to fetch data from firebase and I want that data to be printed in my listview.builder.
Here is my code:-
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("passwordProfile")
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
if (snapshot.data.documents.lenght == 0) {
return Text("no data");
}
return ListView.builder(
itemCount: snapshot.data.documents.lenght,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: snapshot.data.documents[index].data("url")),
title: snapshot.data.documents[index].data["url"],
);
});
}),
And below is a picture:-
From the picture you can observe that documents is not being recognized so help there.
Please specify SteamBuilder return type i-e Querysnapshot here's the example of your code
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("passwordProfile")
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
final userSnapshot = snapshot.data?.docs;
if (userSnapshot!.isEmpty) {
return const Text("no data");
}
return ListView.builder(
itemCount: userSnapshot.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: userSnapshot[index]["url"],
),
title: userSnapshot[index]["url"],
);
});
});
Once you check null, you can add ! like snapshot.data!
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
final data = snapshot.data as Map<String, dynamic>?;
if (data == null) {
return Text("no data");
}
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
// leading:
// CircleAvatar(child: data[index].data("url")),
title: Text("${data[index].data["url"]}"),
);
});
}),

How to display list of data in application?

I have encountered an error where the data is printed out in terminal but do not display in the application.
Future<DocumentSnapshot> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("User");
var query = docRef.where("type", isEqualTo: "teacher").limit(10);
query.get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
print(doc["name"]);
print(doc["email"]);
});
});
}
body: new FutureBuilder(
future: getTeacher(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Expanded(child: Text(snapshot.data["name"])),
Expanded(child: Text(snapshot.data["email"])),
Expanded(
child: IconButton(
icon: Icon(Icons.chat), onPressed: () {}))
],
);
});
}
return Container();
}),
this is the output in the terminal
Your getTeacher()-Function does not return your Future. Because of this
the Nullpointer-Exception ist thrown. You should return query.get() instead of listen to it.
You should also not call getTeacher() in the build-Function because it will be called at every build.
EDIT:
Your method:
Future<DocumentSnapshot> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("User");
var query = docRef.where("type", isEqualTo: "teacher").limit(1);
return (await query.get()).docs[0];
}
Variable of your widget:
final Future<DocumentSnapshot> teacher = getTeacher();
Your FutureBuilder:
new FutureBuilder(
future: teacher,
builder: ...
)
You are not returning anything from future function. Please check below code
Future<List<DocumentSnapshot>> getTeacher() async {
var firebaseUser = await FirebaseAuth.instance.currentUser;
var docRef = FirebaseFirestore.instance.collection("users");
QuerySnapshot query =
await docRef.where("user_device_language", isEqualTo: "en").limit(10).get();
return query.docs;
}
And handle null value in list view like this
body: new FutureBuilder(
future: getTeacher(),
builder: (BuildContext context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Row(
children: <Widget>[
Expanded(child: Text(snapshot.data[index]["name"])),
Expanded(child: Text(snapshot.data[index]["email"])),
Expanded(child: IconButton(icon: Icon(Icons.chat), onPressed: () {}))
],
);
});
} else {
return Text('No Data');
}
}
return Container();
},
),

How to merge two streams and emit them simultaneously using StreamBuilder

I'm trying to make 2 simultaneous queries to Firebase and then stream their results as a single stream, emitting their results one at a time as they come through.
I've tried using this code below, but only one of the stream seems to work, even when both streams should have a result.
Stream<QuerySnapshot> searchQuery(String searchQuery) {
final firstStream = FirebaseFirestore.instance
.collectionGroup("newProduct")
.where('sWords', arrayContains: searchQuery)
.snapshots();
final secondStream = FirebaseFirestore.instance
.collectionGroup("usedProduct")
.where("sWords", arrayContains: searchQuery)
.snapshots();
final mergedStream = Rx.merge([firstStream, secondStream]);
return mergedStream;
}
This is the StreamBuilder where I'm making use of the merged stream
StreamBuilder(
stream: _marketDatabaseService.searchQuery(_searchQuery),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("An error has occurred!");
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.active) {
List<QueryDocumentSnapshot> querySnapshot =
snapshot.data.documents;
return ListView.builder(
itemCount: querySnapshot.length,
itemBuilder: (BuildContext context, int index) {
return Text(querySnapshot[index]["prN"]);
});
} else {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
},
),
Note: I'm making use of rxdart package in this example.
To query 2 collections in Firebase, use the following code snippet:
Stream<List<QuerySnapshot>> getData(String searchQuery) {
Stream stream1 = FirebaseFirestore.instance
.collectionGroup('newProdut')
.where('sWords', arrayContains: searchQuery)
.snapshots();
Stream stream2 = FirebaseFirestore.instance
.collectionGroup('usedProduct')
.where('sWords', arrayContains: searchQuery)
.snapshots();
return StreamZip([stream1, stream2]);
}
Then in your StreamBuilder Widget you the following code snippet to output the data:
StreamBuilder(
stream: getData(_searchQuery),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("An error has occurred!");
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.active) {
List<QuerySnapshot> querySnapshot = snapshot.data.toList();
List<QueryDocumentSnapshot> documentSnapshot = [];
querySnapshot.forEach((query) {
documentSnapshot.addAll(query.docs);
});
/// This "mappedData" will contain contents from both streams
List<Map<String, dynamic>> mappedData = [];
for (QueryDocumentSnapshot doc in documentSnapshot) {
mappedData.add(doc.data());
}
return ListView.builder(
itemCount: mappedData.length,
itemBuilder: (context, index) {
return Text(mappedData[index]["prN"]);
});
} else {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
},
),