Does Streambuilder store the data after app restarts? - flutter

I have a simple streambuilder that reads the users document, and I use it to show some of the user's data. My question is, would this streambuilder re-read the document everytime the user restarts the app? If, yes is there any way to prevent the streambuilder from re-reading it everytime the user restarts the app unless there is a change in the document?
StreamBuilder(
stream: _firestore
.collection('users')
.doc(_auth.currentUser!.uid)
.snapshots(),
builder:
(context, AsyncSnapshot<DocumentSnapshot<Object?>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(
color: isDarkMode ? Colors.white : Colors.black,
);
}
if (snapshot.hasData) {
if (snapshot.data!.exists) {
snapshot.data!['serviceEnabled'] == true
? startServices()
: null;
return Center(
child: Column(
This streambuilder is on the homepage of the app, I show some of the user's data on the homepage.

How would the database know whether there's a change in the document without reading that document?
If you can answer that, you can probably write a query to match that same condition.
For example, if each document has a lastUpdated field, you could just get the updated document with:
_firestore
.collection('users')
.where('lastUpdated', '>', timestampWhenYouLastReadDocuments)
.get()
Aside from that query to update the cache, you could then get the documents from the cache in other places in your app.

Related

Issue in firestore database

I want to show current user data in my flutter app. But it print on screen " No data found".
This my database data
That error also happened error
My security rule
enter image description here
Here is my code
Container(
child: StreamBuilder(
stream: FirebaseFirestore.instance.collection("user3").where("id",isEqualTo:FirebaseAuth.instance.currentUser!.uid).snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot){
if(!snapshot.hasData){
return Text("Loading please wait........");
}
if (snapshot.hasData && snapshot.data!.docs.length > 0) {
DocumentSnapshot userData = snapshot.data!.docs[0];
// Build the widget using the userData
} else {
return Center(child: Text("No data found"));
}
return Container();
},
),
),
The long numeric values (e.g. "167582...") in your database screenshot do not look like a UID that any of the Firebase Authentication providers would generate.
Add this code right before you query the database:
print(FirebaseAuth.instance.currentUser!.uid)
This will show you the value that you're querying for, which (given my opening statement) probably looks quite different from the value in your database.
If that is indeed the case, the problem starts when you write the document. At that point you'll want to make sure that you write the value of FirebaseAuth.instance.currentUser!.uid to the id field.

Need I do pagination for flutter StreamBuilder?

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

Flutter querying different levels of data in Streambuilder

I am trying to access two levels of information in Firebase. The top level collection is called messages and the second level is called chats. I am having trouble accessing the data snapshot retrieved of the Chats collection. Here is a screen print of my collections:
And here please find the code:
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('messages')
.where("senderId", isEqualTo: currentUserId)
.where("receiverId", isEqualTo: member.id)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messageHeaders = snapshot.data!.docs.reversed;
print("messages first = ${messageHeaders.first.id}");
List<MessageBubble> messageBubbles = [];
for (var messageHeader in messageHeaders) {
print(messageHeader["senderId"]);
for (var messageDetail in messageHeader["chats"]) {
print(messageDetail["text"]);
}
So in the print statements above. The one to print the "senderId" is working fine. But not the one to print the "text"
Any help with this would be gratefully received.
Firestore queries are shallow, meaning that you don't get the entire subtree when you query a document. In your case, chats is a subcollection, therefore it will not be queried with the document.
So after getting the document snapshot, you have to make another Firestore query to get chats subcollection for that specific document in messages collection.

Retrieve data with StreamBuilder from collection and subCollection in same time Flutter cloud Firestore

I have a collection called UserCollection,
into this collection I have a subcollection with other specific documents called 'UserSubCollection'
with StreamBuilder I am trying to get these collections unsuccesfully
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('UserCollection')
.doc(firebaseUser.uid).collection('UserSubCollection')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Center(child: Text("Error"));
else if (snapshot.data.docs.isEmpty) //also check if empty! show loader?
return Center(child: Text('Errors'));
else
return new ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
I am able to get just one or a collection or the subcollection.
there is a way to get them both?
First question: do your items need to update automatically over time? Otherwise, I suggest you a FutureBuilder instead of a StreamBuilder.
To get both I suggest you using a separate method like this:
Future<List</*INSERT RETURN TYPE*/>> _retrieveUsers() async{
//get all the users
QuerySnapshot query = await FirebaseFirestore.instance
.collection('UserCollection').get();
Map<String,Map<String,dynamic>> userMap = Map();
//add each user to the map
query.docs.forEach(doc => userMap.putIfAbsent(doc.id, doc.data());
FutureGroup<QuerySnapshot> queries = FutureGroup(); //add async package in pubspec.yaml [there might be version issues]
//retrieve sub collections and bind them to the corresponding user
query.docs.forEach((doc) => queries.add(_retrieveSubCollection(userMap,doc)));
//Close queries
queries.close();
//Await for all the queries to be completed
await queries.future;
//now you have for each user a structure containing its data plus everything in its sub collection. Do whatever you want and then return a list or a map according to what you need
return /*YOUR RETURN*/;
}
Future<Void> _retrieveSubCollection( Map<String,Map<String,dynamic>> userMap, DocumentSnapshot doc) async{
//retrieve the subcollection of a document using its id
QuerySnapshot query = query FirebaseFirestore.instance
.collection('UserCollection')
.doc(doc.id).collection('UserSubCollection').get();
//bind the subcollection to the relative user
Map<String,dynamic> userData = userMap[doc.id];
userData.add("subcollection", query.docs.map((subDoc) =>subDoc.data()).toList());
userMap.update(doc.id,(value)=> userData);
return;
}
Now just use a future builder to display the result of your queries.
FutureBuilder(future: _retrieveUsers(), builder: (context,snapshot) {/*YOUR CODE*/}
I actually didn't try the code in a real environment, so if you find issues on updating the user map there might be problems due to asynchronous updates. However, since every query target a specific element in the map, I don't think there will be issues

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.