Query Regarding Stream Provider in Flutter - google-cloud-firestore

I'm currently trying out Stream Providers for noticing changes in Cloud Firestore Database and getting new data on change.
This is how I have created the Stream Provider :
child: StreamProvider.value(value: _firestoreAPI.getProjectsFromStream(_savedUser.user.name),child: PrimaryScene(toggleAnimation)),
As I am not able to call the provider from initstate ....Hence I'm calling is inside build :
_projectListSnapshotFromStream = Provider.of<QuerySnapshot>(context);
I have a doubt that if I rebuild the Widget containing the above call in its build function, will it charge me a read everytime I build that Widget because I called the provider? Or will it only charge a read when there is a change noticed in the database ?
Stream Definition :
Stream<QuerySnapshot> getProjectsFromStream(String username){
return databaseReference.collection("Projects").where("Members",arrayContains: username).snapshots();
}
Thankyou in advance for answering.

will only count as read if you receive a new document from firestore

Related

How the Firebase Firestore's snapshots() is made and how it can listen to the database updates with a stream inside the flutter app

in the snapshots() method that returns a Stream of document or collection snapshots from Firestore's database:
FirebaseFirestore.instance.collection("collection").snapshots();
How the stream is done, I mean what should I know and learn in order to make as an example a Stream that will listen to endpoint/database changes?
I have an idea about using web sockets but I don't think this is what it's used in the snapshots().
and I don't want some way to create a Stream that requests new data every n Duration.
I want something that does nothing when nothing happens in the backend, but once we change something the Stream should know about it and listen to it.
Thank you!
You have the first part right already. Store it in a variable.
final myStream = FirebaseFirestore.instance.collection("collection").snapshots();
Next, you need to use a StreamBuilder pass in your stream
StreamBuilder(
stream: myStream,
builder: (context, AsyncSnapshot response) {
if(response.connectionState==Connectionstate.waiting) return const CircularProgressIndicator();
final data = response.data();
return WidgetToDisplayData()
}
),
Pass all the other required arguments including the builder widget, which refers to what you want. Firebase has a caching mechanism that ensured a listener is only active if there are changes detected in the db

What is the recommend practice around StreamProvider create construction and keeping state in Flutter

So one of the recommended practices for futures and streams is not to create them in a stateless's widget build function because they would be recreated anytime when the build function is called.
An example is this video: Flutter Future Builder | The Right way
However, there is the provider's create function, specifically in this context StreamProvider.
StreamProvider({
Key? key,
required Create<Stream<T>?> create,
required T initialData,
ErrorBuilder<T>? catchError,
UpdateShouldNotify<T>? updateShouldNotify,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
})
My question is if I am using the create constructor, can I create the stream for the provider in a stateless widget build function and expect the provider to know not to recreate the stream every time the build function is called?
Thank you for your time.
Provider's create function can be used to create objects (including streams) and will correctly cache that value across rebuilds
That's kind of the whole point in fact. The Provider package was created in part because people forgot to cache their Futures/Streams/ChangeNotifiers. So using this create function, the Provider package dealt with caching for its users.
So yes. It is safe to create anything within the create function of a provider.

riverpod provider called once - why are subsequent calls to the provider "cached?"

I'm using Riverpod and I have a relatively simple provider that I'm using to get the number of pending writes from Firestore. This is basically used to help give users feedback as to whether or not their data is fully synced.
final pendingUploadsCounterProvider =
FutureProvider.autoDispose.family<int, List<Report>>((ref, reports) async {
final db = ref.read(firebaseServiceProvider).db;
final reportIds = reports.map((e) => e.id).toList();
final documents = await Future.wait([
for (final name in kReportTypes)
db
.collection(name)
.where('report_id', whereIn: reportIds)
.get(GetOptions(source: Source.cache))
]);
return documents.where((event) => event.metadata.hasPendingWrites).length;
});
As you can see, all I'm doing is reading some data from the Firestore cache and then filtering that list based on the hasPendingWrites property.
I'm attempting to use this provider on multiple screens as I need to provide the user feedback on their sync status in multiple locations.
When I use this provider on a single screen only, every time I enter the screen, the provider is triggered and I get an up to date status.
When I use this provider on multiple screens, the first time I enter a screen where it is used, the provider is triggered. Subsequently, entering any other screen where this provider is used, the provider is no longer triggered and therefore, the results are out of date.
I thought by using autoDispose, the provider would be disposed of when I leave the screen, but that doesn't appear to be the case.
I'm sure that there's a disconnect with my understanding of riverpod and how this works and hopeful someone can shed some light on my misunderstanding.
Thank you!
the provider will not be disposed while the widget in the tree you need to remove it from the back stack to be disposed
in your case, you need to reload it on other screens
by calling
context.refresh(pendingUploadsCounterProvider);

Dart: Get current value in Stream

I am making a Flutter App, where I use Firebase Auth and Cloud Firestore. For every user, I have a trainer document with the user uid as the document id.
When I update the data of my trainers, I want to make sure that only the document of the currently logged in user can be modified.
I have an AuthService class with the following method to get the current user:
Stream<FirebaseUser> get user {
return _auth.onAuthStateChanged;
}
So when I want to change the trainer document in my DatabaseService class, I want to get the current value of the stream to get the user uid. I do not think that I can use StreamBuilder or Provider, as this is not a Widget Tree, but a simple Dart class. However, I do not think that I can use .listen() either, as I only want to get current Stream value, not all of them.
How do I get the current value of a Dart Stream?
You could probably keep a local variable currentUser, listen to the Stream, and update currentUser everytime the Stream gives a new value. Read currentUser, whenever you want to get the current user.

How to get all documents from a collection without using StreamBuilder?

I want to get all documents from firestore collection without using stream builder, otherwise I will have nested streams and in the last stream I am getting a null. Initial data is not solving my problem.
Now I make a stream builder for that collection, when a snapshot is chosen I jump on other widget and there I also make a stream builder and here is the problem.
I want to make stream builder only in the last widget of the scenario, because only there realtime data update is needed.
You can use FutureBuilder with the following method
await Firestore.instance.collection("books").getDocuments()
Get Realtime data using this:
Firestore.instance
.collection('books').snapshots().listen((querySnapshot){
});