Need I do pagination for flutter StreamBuilder? - flutter

I'm new to flutter, and now I'm creating an app which has a feed page, I'm using StreamBuilder + firestore to do this, the code is like this:
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('posts')
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(
color: primaryColor,
),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) => Container(
child: createPostViewFromSnapShot(snapshot.data!.docs[index]),// it returns a widget
),
);
},
);
You can see from the code I didn't do pagination, I want to know when the code runs, it fetch all the post from firestore ? Or it will fetch data by block or something like pagination ?
I want to know if it's necessary to do pagination for StreamBuilder?

Avoid creating Widgets in Methods. That way you cause your App to
become I'm-performant. Because the Widget is not sat directly in the
Tree, but a method, every build that methods gets called, no matter
if the resulting widget would have to be actually be rebuilt.
That stream will not always emit events where data is not null. You will most likely get an exception for using snapshot.data! bang operator
Take a closer look at the documentation: https://firebase.flutter.dev/docs/firestore/usage/
FlutterFire provides support for dealing with realtime changes to
collections and documents. A new event is provided on the initial
request, and any subsequent changes to collection/document whenever a
change occurs (modification, deleted or added).
For what you are trying to achieve, it would be better to initially fetch a limited set of documents from your collection.
collectionReference.limit(42).get()
You can than fetch the next elements by using https://firebase.flutter.dev/docs/firestore/usage/#start--end-cursors

Related

Flutter: Weird listview/stream builder behavior

I have a home_page.dart file whose body has a StreamBuilder it recieves the stream from my getChats() function. In the StreamBuilder's builder function I sort the all the documents after storing them in a list named docsList based on the lastMessageTime field so that the document with the latest message is at the top (first).
As the ListView is using the docsList for building the cards it is expected that the document which has the most resent message should be displayed first. Which only happens when the list is build for the first time. After that if I send a new message to the chat which is not at the top this happens:
Initially:
When I send a message which the text 'test' to the chat "Heah Roger" this is how the list gets updated:
As it can be seen the Time on the right and the subtext changes for the first tile but the image and name didn't (same for second tile). Even though the documents are updated in the docsList and are sorted in the desired manner (I printed it to check it). Somehow the photo and the name alone are not being updated in the UI alone.
Note: The correct fields are updated in the firestore. Also if I restart the app after killing it. It shows the desired result:
getChats()
Stream<QuerySnapshot<Map<String, dynamic>>> getChats(User currentUser) {
return FirebaseFirestore.instance
.collection('chats')
.where('users', arrayContains: currentUser.id)
.snapshots();
}
home_page.dart
body: StreamBuilder(
stream: RepositoryProvider.of<FirestoreRepository>(context).getChats(BlocProvider.of<AuthenticationBloc>(context).state.user),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData && snapshot.data.docs.length > 0) {
List docsList = snapshot.data.docs;
docsList.sort((a, b) => b.data()['lastMessageTime'].compareTo(a.data()['lastMessageTime']));
return ListView.builder(
itemCount: docsList.length,
itemBuilder: (context, index) {
return SingleChatCard(chatRoom: ChatRoomModel.fromMap(docsList[index].data()));
},
);
} else {
return ...
}
},
),
Can anyone help me figure out the underlying problem that is causing this weird behavior?
Looks like an key issue to me,
When you're using your custom Widget to render in a listview, with some complicate data flow,
Flutter react to these changes one level at a time:
You can refer: https://www.youtube.com/watch?v=kn0EOS-ZiIc
In your example, you can do something like this:
return SingleChatCard(
key: ValueKey(index),
chatRoom: ChatRoomModel.fromMap(docsList[index].data()));
},

How to create chatRoom with flutter and firestore

I have created a chat with flutter and I need to use into my app, where 1group of user will chat with another kind of user group.
At the moment my chat is developed in this manner and I create record in firebase so:
CollectionReference addMessage = FirebaseFirestore.instance.collection('Messages');
Future<void> createMessageRecord() {
return addMessage
.add(
{ 'id': firebaseUser.uid,
'text': textMessage,
'timeStamp': DateTime.now().millisecondsSinceEpoch.toString(),
},
)
I retrieve them:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('Messages').orderBy('timeStamp', descending: true).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
I display them:
ListView.builder(
reverse: true,
padding: EdgeInsets.only(top: 15.0),
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
final message = snapshot.data.docs[index];
final bool isMe = message['id'] == firebaseUser.uid;
return _buildMessage(message, isMe);
the chat works pretty good but all users of course will have the same chat.
What I want to do is limit the chat displaying just for the 2 users.
the first one who starts the chat and and second one who get.
At the moment I have created into firestore a collection called "Messages" and into I have the 3 documents id text timeStamp```
I dont know how to limit the displaying data just for this 2 users
Instead of having one single document for all of the messages create a document per conversaiton this way you will only retrive the corresponding messages.
Also as mentioned on the comment by #Uni you shoudl be using realtime database also you can take a look at several examples on the web on how to accomplish that. for example

How can I query another collection inside a streambuilder that queries a different collection?

I'm trying to query a document field from a different firestore collection than the one of the streambuilder. Right now, with the streambuilder I am able to query one of the documents i need, however I don't how i'm to query another collection in that same stream builder. I thought about using a global variable from another class where I had already queried the document I want but that didn't seem to work.
This is the stream builder
Container(
height: MediaQuery.of(context).size.height,
margin: EdgeInsets.only(top: 0),
padding: EdgeInsets.only(top: 0),
child: StreamBuilder(
stream: Firestore.instance
.collection('Tournaments')
.document('Fortnite Tournaments')
.collection('Fortnite Tourneys')
.document(docID)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
}
var tourneyDetails = snapshot.data;
return ListView(
children: <Widget>[
..........
The collection i'm trying to query is called "users"
Why i need to query a field in a different collection is because i'm writing an if statement that needs both of the fields
if (int.parse("Field_i_am_trying_to_get_from_another_collection") > int.parse(tourneyDetails[ 'tourneycost']))
print('You have the money');
else {
print('You're broke');
}
That condition is going to be executed when the raised button is clicked. Essentially, My problem is that I am currently not sure how I can query the data from that other collection.
Not sure if i explained that well but comment if you need more context or code, Thanks.
You can use provider instead of creating global variable.
see Provider package: https://pub.dev/packages/provider
if the current user is the user you want, then you can take the current user data from firestore when the app is loading and provide the user to where you want to use.
see this article if you don't know what provider is:
Flutter StateManagement with Provider

How to get access to a collection inside the start collection by using a StreamBuilder

I would like to know how can I get access to the fields of a collection that is inside the start collection? (see image below)
This is the streamBuilder I use to get access to the start(first) collection (posts) but I want a way to transform this streamBuilder so that I can get access to another collection inside the posts location. Thank you
StreamBuilder(
///IF YOU WANT TO CHANGE THE COLECCTION LIST THIS IS THE PLACE TO DO IT
stream: Firestore.instance.collection('posts').snapshots(),
builder: (context, snapshot) {
///THIS IS THE PLACE WHEN YOU CAN ADD AN ANIMATION
if (!snapshot.hasData) return const Text('Loading...');
return ListView.builder(
itemExtent: 100.0,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
Post post = Post.fromDoc(snapshot.data.documents[index]);
return _buildUserTile(post);
},
);
},[![enter image description here][1]][1]
),
I want to be able to get the data that is inside a colection inside the first colection (posts).
See below my firebase database
I am not sure but maybe you can get an idea from the below link:
https://groups.google.com/forum/#!topic/flutter-dev/ayIxrLiz1eQ

Display Firestore map data in Flutter

I am building a password manager and I'm trying to save on Firestore reads by displaying data in Map. The idea is to have a document (for each password category) and have the passwords stored as maps in it. See example screenshots here:
The idea is that users click on each password (map) and the app takes them to the relevant one using indexes. I then want the data from each map to be sorted separately so that they can live in their own Text widgets. But I haven't seen any possibility of displaying map data in Flutter yet.
I've tried some of the examples from here but haven't been able to display the data from the map - https://github.com/fireship-io/flutter-base/blob/master/lib/main.dart
Any suggestions would be much appreciated.
new StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(userDocSTRING.toString())
.collection(userPassSTRING.toString())
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// If no data is present, display a waiting indicator to the user
// If Null, red screen displays
return Center(child: CircularProgressIndicator());
} else {
// Debug print for Ryan
// print(snapshot.data[0].data.toString());
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshot.data.documents[index];
return ListTile(
leading: Container(
child: IconButton(
icon: Icon(Icons.content_copy),
color: lightCardCOLOR,
title: Text(documentSnapshot['Name']
As you can see from the code above, fetching fields from documents is easy enough but I don't want each password to have it's own document. I want a document to hold multiple Maps which corresponds to a password. So documentSnapshot['Name'] I want to be the name or value of a map
I'll suggest that your attempt to "save on Firestore reads" is making it more difficult for you to model and display the data. It also won't scale, since the maximum size of a document is 1MB. Consider storing each password in a different document to make this easier. Document reads are not really very expensive, and the Firebase SDK will cache them locally to avoid the cost of reads when queried.