How to let Flutter stream in streambuilder get limited document from firebase and accept new document created at the same time? - flutter

I am building an instant messaging app with flutter and firebase. I have a view to display user messages with a StreamBuider. The stream of the StreamBuider is accepting data from firebase. I have limited the number of messages loaded at a time for lazy loading purposes. The current problem is that when the user creates a new messages
For example, the original view has 3 messages (m1,m2,m3). When the user writes a new message (m4), the view will display (m2,m3,m4). m1 has gone. What I want is to keep all 4 messages. Is there a way to limit the number of documents gotten while listening on new documents?
StreamBuilder<QuerySnapshot>(
stream: messageService.getMessagesByChatIdStream(chatId),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
padding: EdgeInsets.all(10),
itemBuilder: (context, index) {
return buildMessageTile(index, snapshot.data?.docs[index]);
},
reverse: true,
itemCount: snapshot.data?.docs.length,
);
}
the getMessagesByChatIdStream() function is like this:
Stream<QuerySnapshot> getMessagesByChatIdStream(String chatId, {int limit = 5}) {
CollectionReference colRef =
firebaseFirestore.collection("messages").doc(chatId).collection(chatId);
return colRef
.limit(limit)
.orderBy('timestamp', descending: true)
.snapshots();
}

Related

how to combine two Firestore collections?

Example code
toys = toysRef.where('postState', isEqualTo: 'recruiting').orderBy('createdAt', descending: true).snapshots();
fruits = fruitsRef.where('postState', isEqualTo: 'recruiting').orderBy('createdAt', descending: true).snapshots();
hi I'm trying to combine or merge two streams
I want to show toys and fruits order by desc with one StreamBuilder Widget
There is a common field ['createdAt']
I want to implement like this.
if some item added to firestore , it should show on realtime.
You can use StreamGroup from rxdart to merge your toys and fruits.
StreamGroup<List<QuerySnapshot>> combinedStreams = StreamGroup<List<QuerySnapshot>>.merge([
toysRef.where('postState', isEqualTo: 'recruiting').orderBy('createdAt', descending: true).snapshots(),
fruitsRef.where('postState', isEqualTo: 'recruiting').orderBy('createdAt', descending: true).snapshots()
]);
and to finally have them sorted in a stream, you could have:
StreamBuilder<List<QuerySnapshot>>(
stream: combinedStreams,
builder: (BuildContext context, AsyncSnapshot<List<QuerySnapshot>> snapshot) {
List<QueryDocumentSnapshot> combinedData = [];
for (QuerySnapshot snap in snapshot.data) {
combinedData.addAll(snap.docs);
}
combinedData.sort((a, b) => b['createdAt'].compareTo(a['createdAt']));
return ListView.builder(
itemCount: combinedData.length,
itemBuilder: (context, index) {
// Build your UI
}
);
}
)
You should use ListView.builder so that you have a performant list, and not render all the elements at once (would really lag your UI if you'd have a lot of elements).
Don't forget to check if snapshot has errors or is empty so that you can display a loader to the user or an error message.

How can i fetch data from Firestore (the cashed data) in flutter

i am trying to save data reads which have been not changed yet to avoid more and more the same repeated data that not changed yet ..
i have normal Future.Builder that get data from firstore (network side)
Widget build(BuildContext context) {
return FutureBuilder(
future: FirebaseFirestore.instance.collection('users').get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return const Expanded(child: SizedBox()) ;
}
return ListView.builder(
itemCount: snapshot.data!.docs.length ,
itemBuilder: (context, int index) {
DocumentSnapshot documentSnapshot = snapshot.data!.docs[index];
return ListView.builder(
itemCount: snapshot.data!.docs.length ,
itemBuilder: (context, int index) {
DocumentSnapshot documentSnapshot = snapshot.data!.docs[index];
return Text(documentSnapshot['products'])
}
);
}
}
and i have into every single document Timestamp and i need to use where('modify',isGreaterThen : HERE i need to put the old timestamp from cashe to chick if it not changed yet to decide to fetch the new ones
in flutter i cannot handle it as well .. How can i fetch the cashed data with the new ones from network in the harmonic index such as reading the whole data in normal way .. so i avoided these old ones to be reload again ..
i have read a lot of this topic but it was in old Firestore version also it was using java code ...
this following code that cannot handle in flutter
Source CACHE = Source.CACHE;
Source SERVER = Source.SERVER;
Query.Direction DESCENDING = Query.Direction.DESCENDING;
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference shoesRef = rootRef.collection("shoes");
Query lastAddedQuery = shoesRef.orderBy("lastModified", DESCENDING)
shoesRef.get(CACHE).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
boolean isEmpty = task.getResult().isEmpty();
if (isEmpty) {
shoesRef.get(SERVER).addOnCompleteListener(/* ... */);
}
}
});
Query query = shoesRef.orderBy("lastModified", DESCENDING)
.whereGreaterThan("lastModified", savedDate);
source code was written by Alex Mamo
https://medium.com/firebase-tips-tricks/how-to-drastically-reduce-the-number-of-reads-when-no-documents-are-changed-in-firestore-8760e2f25e9e
any support or example with latest version of Firbase and in dart or flutter code will be so thankful ..
best regards

Flutter Fire Store - Assigning individual document fields to variables

I'm attempting to show a user's profile image on their home page by pulling the user's 'imageUrl' from their Fire Store document. I already have the app setup to where the user can upload a new image which updates the 'imageUrl' in Fire Store, but I don't know how to have the 'imageUrl' as a variable so I can show it on the app screen.
I've been reading documentation online but It seems over simplified or out of date. I've tried using StreamBuilder, but it pulls the data from every user in the database instead of for a single user. I just need to know how to pull this one value and use it as a variable in my dart code using "getString()" with a document reference or the collection reference I already have, thank you.
class _UserPageState extends State<UserPage> {
User user = auth.currentUser!;
final CollectionReference collectionReference = FirebaseFirestore.instance.collection('users');
// Get profileImageUrl from users userDoc
String imageUrl = 'test'; // this should be the users imageUrl
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'${user.email}'), // this is being pulled from authentication not firestore
),
body: Center(
child: Column(
children: [
// --------------------------- I tried using a stream builder here ---------------------
StreamBuilder(
stream: collectionReference.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text(
'Something went wrong.'); // A: use incase the data does not load
}
final data = snapshot.requireData;
return ListView.builder(
shrinkWrap: true,
itemCount: data.size,
itemBuilder: (context, index) {
return Text(
// A: Stream builder will update with all of the users email addresses, I want this for one user exclusively
'My email is ${data.docs[index]['email']}');
},
collection('users')
.where("uid", isEqualTo: uid)
.snapshots(),
To filter the data in firestore collection use "where". Store the user uid in offline and query it by where using the stored uid
You can use the following function to get single data from stream.
Stream<UserModel> getSingleStreamData({String? uId}) {
return ref!.where(CommonKeys.id, isEqualTo: uId).snapshots().map((value) => value.docs.first.data());}

How to update the future in FutureBuilder to get new data while using GetX

I am using GetX and FutureBuilder to build a list of Cards from my DB.
Lets say I have 10 products in DB then 10 cards are shown. When I add one more product, the Cards on the HomeScreen aren't updated and I have to Navigate in and Out of page to show the 11th product.
How can I force the update i.e. probably make a "Refresh" button that may load the latest data.
PS: I do not want to use STREAM-BUILDER as I don't wish to listen to all changes actively but only when needed. I also cannot use SetState() as I am using GetX hence no StatefulWidget.
Here is my Card class:
FutureBuilder(
future: databaseController.getData()
builder: (context, snapshot) {
return StaggeredGridView.countBuilder(
itemCount: snapshot.data.length,
crossAxisCount: 2,
itemBuilder: (BuildContext context, int index) =>
GetX<FindDeviceLocation>(builder: (controller) {
return CreateTheCard(
lindex: index,
location: snapshot.data[index]["location"],
summary: snapshot.data[index]["summary"],
description: snapshot.data[index]["description"],
category: snapshot.data[index]["category"],
imageURL: snapshot.data[index]["adImage"],
onTapFunction: () => Get.to(DetailPage(
post: snapshot.data[index],
)));
}),
This is my method that fetches data from DB:
Future getData() async {
QuerySnapshot _firebaseDb = await FirebaseFirestore.instance
.collection("items")
.where("status", isEqualTo: true)
.orderBy("postTime", descending: true)
.get();
return _firebaseDb.docs;
}
The databaseController has a method call update(),so you can call databaseController.update() when you need to update your data.
Use the getx worker
Ever:
If data change you can update the view
ever(index, (value) {
// call your function here
// any code will be called any time index changes
});

How to mix stream with Provider?

I am use Provider. I want mix different data source with stream.
Use case: Chat app where some message are from system (date/error message) but other are from database (Firestore).
For example for just get message from database I now use StreamBuilder:
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('message').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return messageBuilder(snapshot.data.documents[index], xa);
});
But with StreamBuilder cannot mix data from other source.
I want inject message at messages[index] for different message type.
Possible solution is create separate messages List and feed into ListView.builder:
return new ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return messageBuilder(message, xa);
});
But how I can use Provider to mix stream from Firestore and also system message into messages List?
How I can bring together data source into final List messages?
Thanks for help!