Dart: Get current value in Stream - flutter

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.

Related

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);

Whats the best practice for object pool pattern in flutter/dart?

Imagine a very simple application with two pages, PostList and PostDetail. On the former page, we show a list of posts, and on the latter, the details of a single post.
Now consider the following scenario. Our user clicks on the first PostItem and navigates to the PostDetail page. We fetch the full post data from the server. The likes_count of this post gets increased by one. Now if our user navigates back to the first page, the PostItem should be updated and show the new likes_count as well.
One possible solution to handle this is to create a pool of posts. Now when we fetch some new data from the server, instead of creating a new post object, we can update our corresponding pool instance object. For example, if we navigate to post with id=3, we can do something like this:
Post oldPost = PostPool.get(3)
oldPost.updateFromJson(servers_new_json_for_post_3)
Since the same object is used on the PostDetail page, our PostItem on the PostList page will be updated as well.
Other approaches that do not use a unique "single instance" of our Post objects, across the application, would not be clean to implement and requires tricks to keep the UI sync.
But the ObjectPool approach also has its own problems and leads to memory leaks since the size of the pool gets bigger and bigger over time. For this problem to get solved we need to manually count the number of references for each pool object instance and discard them when this count is equal to zero. This manual calculation is full of bugs and I was wondering if there are any better ways to achieve this.
You can also solve this by using streams and StreamBuilders. First you create the stream and populates it with the initial data fetched from the API:
I like to use BehaviorSubject from rxdart but you can use normal streams too.
final BehaviorSubject<List<Post>> postsStream = BehaviorSubject<List<Post>>()..startWith(<Post>[]);
On the constructor body or initState function you would fetch the data and add it to the stream.
PostPage() {
_fetchPosts().then((posts) => postsStream.add(posts));
}
You can now subscribe to changes on this postsStream in both pages with StreamBuilder. Any update you need to do you would emit a new (updated) List<Post> to the stream triggering a rebuild on any StreamBuilder subscribed to the stream with the new and updated values.
You can latter dispose the StreamController.

Firebase/cloud firestore: onSnapshot() vs on()

I have been using onSnapshot successfully to alert my code to changes in underlying data, as in
// Set up to listen for changes to the "figures" collection, that is,
// someone has created a new figure that we will want to list on the screen.
setFiguresListener: function () {
// `figuresCR` is a collection reference defined elsewhere
return this.figuresCR.onSnapshot((iFigs) => {
iFigs.forEach((fSnap) => {
const aFigure = figureConverter.fromFirestore(fSnap, null);
const dbid = aFigure.guts.dbid; // ID of the "figure" in the database
nos2.theFigures[dbid] = aFigure; // update the local copy of the data
});
nos2.ui.update();
console.log(` Listener gets ${iFigs.size} figures`);
});
But I now read about on in the docs. It explains:
[The on() function] Listens for data changes at a particular location.
This is the primary way to read data from a Database. Your callback
will be triggered for the initial data and again whenever the data
changes. Use off( )to stop receiving updates. See Retrieve Data on
the Web for more details.
The syntax is a bit different, and on() seems to do much the same as onSnapshot().
So what is the real difference? Should we be using on() instead of onSnapshot()?
on() is an operation for reading from Firebase Realtime Database. That's a completely different database with different APIs than Firestore. They have essentially no overlap. There is no on() operation with Firestore.
If you're working with Firestore, ignore all the documentation about Realtime Database, and stick to using onSnapshot() for getting realtime updates.
Other tyros who fall into this tar pit: in the API doc pages, you might think that since firestore is a database under firebase, you could look for help under firebase.database. But no: look only in the next section, firebase.firestore.

Firestore How to access a value from a snapshot in a Document Snapshot stream

I'm new to dart/flutter and am building an app right now that keeps track of some data on each user in a firstore database. I have a 'Users' collection which contains a document for each user, and one of the fields in the user document is the "UID" received through firebase_auth.
That being said, to make sure I have access to the latest copy of a user document, I hold a Stream. I want to somehow access the "UID" field from the latest snapshot in the stream and then do some other operations with it.
Is there anyway to do this? Am I using/understanding streams incorrectly?
If you only ever need the UID to build other widgets, you can simply use a StreamBuilder which will rebuild its children whenever a new value is emitted from the stream (which you get a copy of). However, if you need to access the latest UID at some arbitrary point of time, check out RxDart's BehaviorSubject which provides constant-time synchronous access to the latest emitted value, if one exists. It is very helpful for handling state in general.

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){
});