I'm creating a chat screen. What I'm currently doing is that I'm using a Streambuilder to listen to the 'messages' collection and display the messages using ListView.builder().
Below is the code i'm using.
StreamBuilder<QuerySnapshot>(
stream: _fireStoreInstance
.collection('$collectionName/$docID/messages')
.orderBy('sentAt', descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: CircularProgressIndicator(),
);
List<Map> documents = snapshot.data.docs
.map((doc) => {'documentId': doc.id, ...doc.data()})
.toList();
return ListView.builder(
cacheExtent: MediaQuery.of(context).size.height,
reverse: true,
itemCount: documents.length,
padding:
const EdgeInsets.only(left: 15.0, right: 15.0, bottom: 5.0),
itemBuilder: (context, index) {
return MessageBubble(
...
);
},
);
},
),
My concern is, will the query fetch all the documents in the collection all at once? If yes then it will be a lot of reads each time the query is executed
_fireStoreInstance
.collection('$collectionName/$docID/messages')
.orderBy('sentAt', descending: true)
.snapshots();
Do I need to paginate by using limit ? If I paginate how do I listen to new messages ? Thank you for your help.
Yes, .snapshots() will read and keep listening to all documents that fit the query, if you want a subset of that you will have to paginate it using .limit().
I have found this article, with a video step by step on How to perform real-time pagination with Firestore with the use of an infinite scroll. I think this is exactly what you looking for, so I won't post any code since you can follow that example.
Related
I have a build on Firebase Firestore like this:
In the structure here, there are 2 ArrayLists. I want to display what will be done from 2 ArrayLists as ListTile.
I tried a code like this:
Expanded(
child: StreamBuilder <QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("users").doc(FirebaseAuth.instance.currentUser.uid).collection("yapilacaklar").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return Expanded(
child: _buildList(snapshot.data),
);
},
)
),
Widget _buildList(QuerySnapshot snapshot) {
return ListView.builder(
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
final doc = snapshot.docs[index];
return ListTile(
title: Text(doc["yapilacaklar"].toString()),
);
},
);
}
These codes I tried do not give an error, but they do not show in any Widget. How can I do what I want? Thanks in advance for the help.
I believe the issue is the '.collection("yapilacaklar")'. It will try to find a collection with the id "yapilacaklar" and your user doc doesn't have any collections. Yapilacaklar, in this case, is a field in the document. Try getting the snapshots of the actual document and then creating a ListView from the array in the yapilacaklar field.
The flutterfire documentation is very helpful for reading firestore data: https://firebase.flutter.dev/docs/firestore/usage/#read-data
I am having a trouble reading collection from firebase and saving values in a list.
I basically have a collection called 'brands' where I have car brands like this:
Firebase 'brands' collection screenshot
I need these car brands to be saved as a list like this, to be able to use it in a dropdown menu as items:
<String>[
'ferrari',
'mercedes',
'porsche',
]
I have tried using StreamBuilder (below) but it requires to return a widget and I do not actually need a widget to be returned, so below StreamBuilder is just an experiment "in progress".
Do you have any ideas?
final stream = FirebaseFirestore.instance
.collection('accounts')
.doc('dealers')
.collection(user!.uid)
.doc(dealerName)
.collection('brands')
.snapshots();
StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Text('Error in receiving snapshot: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
);
}
return ListView.builder(
padding: EdgeInsets.all(8),
reverse: true,
itemCount: snapshot.data.docs!.length,
itemBuilder: (BuildContext context, int index) {
return Text(
snapshot.data.docs[index]['brandName'],
);
},
);
},
);
Once you get the data from firebase, loop through it and add the car brands to your list. Try this:
List<String> myBrands = [];
final dataRef = await FirebaseFirestore.instance
.collection('accounts')
.doc('dealers')
.collection(user!.uid)
.doc(dealerName)
.collection('brands')
.get();
dataRef.docs.forEach((doc) {
myBrands.add(doc.data()['brandName']);
});
You should then be able to use the myBrands list for your dropdown menu.
I am querying a firestore collection in Flutter using where and arrayContains, for some reason it is not working as expected for me.
StreamBuilder(
stream: (_searchTerm.length >= 3)
? FirebaseFirestore.instance.collection("users").snapshots()
: FirebaseFirestore.instance
.collection('users')
.where('email', arrayContains: _searchTerm)
.snapshots(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final results = snapshot.data.docs;
print(results.length);
return ListView.builder(
shrinkWrap: true,
itemCount: results.length,
itemBuilder: (ctx, index) => Text(
results[index].data()['display-name'],
),
);
})
The _searchTerm variable is populated as I type in some values into the textfield and when it hits a length of three characters that's when the above query fires.
For example when I type in test the query should only return the values that contain test in it, but I am getting the whole collection with and without the value test.
Please advice!
EDIT - Posting a screenshot of my firestore data structure
When you do the following:
FirebaseFirestore.instance
.collection('users')
.where('email', arrayContains: _searchTerm)
.snapshots(),
You are looking for documents inside the users collection that have _searchTerm as an item of the email array, property of a user document.
There are two problems:
I don't think the email property of your users is an array.
Firebase does not perform substring searches
I think you will need to use a third-party application for searches on Firestore. A popular one is Algolia that comes with a quite powerful FREE plan.
I am trying to display a realtime chat-screen in flutter with with firebase-firestore (equal to the homescreen of whatsapp).
Working: Creating a list of all the contacts "peers". Have a Look at my Listview:
Container(
child: StreamBuilder(
stream:
//FirebaseFirestore.instance.collection('users').snapshots(),
FirebaseFirestore.instance
.collection('users')
.doc(currentUserId)
.collection('peers')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
);
} else {
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) =>
buildItem(context, snapshot.data.documents[index]),
itemCount: snapshot.data.documents.length,
);
}
},
),
),
not working: Loading specific data for each tile like last message or name. I cant query this at the time of creating my first list (first query returns peer-ids, second returns userdata of a peer-id). My buildItem method consists of another streambuilder, however, as soon as the first streambuilder makes changes, the app freezes.
Widget buildItem(BuildContext context, DocumentSnapshot document) {
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(document.data()['peerId'])
.snapshots(),
builder: ...
Is this the proper way to nest streams? Simple Listviews are documented quite well, but i couldn't find a good example on this on google. Any help is appreciated.
Try creating your stream just once in initState and pass it onto this method:
//in initState
peersStream = FirebaseFirestore.instance
.collection('users')
.doc(currentUserId)
.collection('peers')
.snapshots(),
Then use stream: peersStream in the StreamBuilder.
Also, it is recommended to use widget-classes over methods for widgets: https://stackoverflow.com/a/53234826/5066615
I need get snapshot metadata so can check if write to Firestore successful. I look at source and see there is SnapshotMetadata and boolean hasPendingWrites(). But there is no example how to implement and I cannot find anywhere.
I need implement so can show in my chat app if message is sent or still sending.
My code:
child: new FirestoreAnimatedList(
query: Firestore.instance.collection('Collection')
.orderBy('timestamp', descending: true)
.snapshots(),
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, DocumentSnapshot snapshot,
Animation<double> animation, int x) {
return new Chat(
snapshot: snapshot, animation: animation);
},
),
I cannot find how get SnapshotMetadata in query and feed into itemBuilder so I can show progressIndicator if sending.
Anyone know solution?
Thanks!
You can show progress bar using snapshot.connectionState
if(snapshot.connectionState == ConnectionState.waiting) {
return Text('Loading');
}