When should i use streams vs just accessing the cloud firestore once in flutter? - flutter

I want to create a groups functionality for my app, so far when i set up the profile page of each user, i've used something like this :
DocumentReference documentReference =
_firestore.collection("users").document("$email");
await documentReference.get().then((DocumentSnapshot datasnapshot) {
if (datasnapshot.exists) {
displayName=datasnapshot.data['displayName'].toString();
bio=datasnapshot.data['bio'].toString();
print(bio);
}
else {
print("No such user");
}
This works but im thinking if i want to create groups and record the changes that different users may make then i should probably use a stream is that correct? Generally i am unsure when to use which if anyone could provide some insight?

The technical difference is that get only gets the document once, while the stream will get the current data straight away, and then continue to listen for changes.
I typically use a stream (or its underlying onSnapshot()) when I display the data straight into the UI, because that means the UI updates whenever the data changes. This is one of the really cool Firebase features, because it makes your UI reactive to the data changes. I use get() for things that I only need once, such as configuration data (although it's also very cool if you use a stream for that), client-side joins, etc.

It all really comes down to whether you want the data to reload every time something changes in your database.
If you want it to update as it changes in your database, use a Stream (most likely with a StreamBuilder)
If you want it only to update when you reload the screen, use get() like you are in your example

Related

How to handle statemanagment in flutter to listen to document changes in firestore?

Im building an app with a backend on firestore.
Architecture:
This is the relevant route of screens the user can take in my app:
Home-> myOrdersList(FutureBuilder)-> OrderInfo(FutureBuilder)-> HandleOrder.
to get the futures and handle my order I have this class:
class FirestoreService
functions:
Future List GetOrders;
Future Order getOrder;
Future void changeOrderInfo;
Order class:
class Order
parameters:
String info;
This is the problem:
When the calls FirestoreService().changeOrderInfo on HandleOrder-screen the screen pops to OrderInfo-screen. The SpecificOrder-screen automaticly rebuilds with setState so thats works fine and the changed info is up to date here. But when the user navigates one screen backwards in the stack to myOrdersList (where all current orders are listed) that screen still holds the old data that was fetched before the order was handled. So not before the user manually updates the screen you can se the current state and the changed info on that screen.
My question:
So I wonder if there is a simple stateManagement technique to solve this problem in a simple way. One way I have thought of is to use changenotifier in FirestoreService, and notifyallisteners when the function changeOrderInfo is called to then notify all relevant screens and get them up to date but I dont really know if thats correct. Since there are many orders and new ones are continuously created I cant simply listen on one document and provide those changes.
One solution could also be to use a streambuilder in myOrdersList but i dont like that solotion since it would mean that data would be fetched everytime any order was changed
if you are after real-time updates stream builder is the only way to go. unless you want to refresh your future builder again and again every time there is an update which will kill your wallet because that means you are refetching all the documents again and again. Unlike stream builder, you only fetch the collection once and if there is an update, it will only refetch the document changed/updated not the whole collection. check this out for more explanation https://firebase.google.com/docs/firestore/query-data/listen

What are the best practices when working with data from multiple sources in Flutter/Bloc?

The Bloc manual describes the example of a simple Todos app. It works as an example, but I get stuck when trying to make it into a more realistic app. Clearly, a more realistic Todos app needs to keep working when the user temporarily loses network connection, and also needs to occasionally check the server for updates that the user might have added from another device.
So as a basic data model I have:
dataFromServer, which is refreshed every five minutes, and
localData, that describes what changes have been made locally but haven't been synchronized to the server yet.
My current idea is to have three kinds of events:
on<GetTodosFromServer>() which runs every few minutes to check the server for updates and only changes the dataFromServer,
on<TodoAdded>() (and its friends TodoDeleted, TodoChecked, and so on) which get triggered when the user changes the data, and only change the localData, and
on<SyncTodoToServer>() which runs whenever the user changes the todo list, or when network connectivity is restored, and tries to send the changes to the server, retrieves the new value from the server, and then sets the new dataFromServer and localData.
So obviously there's a lot of interaction between these three methods. When a new todo is added after the synchronization to the server starts, but before synchronization is finished, it needs to stay in the local changes object. When GetTodosFromServer and SyncTodoToServer both return server data, they need to find out who has the latest data and keep that. And so on.
Coming from a Redux background, I'm used to having two reducers (one for local data, one for server data) that would only respond to simple actions. E.g. an action { "type": "TodoSuccessfullySyncedToServer", uploadedData: [...], serverResponse: [...] } would be straightforward to parse for both the localData and the dataFromServer reducer. The reducer doesn't contain any of the business logic, it receives actions one by one and all you need to think about inside the reducer is the state before the action, the action itself, and the state after the action. Anything you rely on to handle the action will be in the action itself, not in the context. So different pieces of code that generate those actions can just fire these actions without thinking, knowing that the reducer will handle them correctly.
Bloc on the other hand seems to mix business logic and updating the state. API calls are made within the event handlers, which will emit a value possibly many seconds later. So every time you return from an asynchronous call in an event handler, you need to think about how the state might have changed while that call was happening and the consequences this has on what you're currently doing. Also, an object in the state can be updated by different events that need to coordinate among themselves how to avoid conflicts while doing so.
Is there a best practice on how to avoid the complexity that brings? Is it best practice to split large events into "StartSyncToServer" and "SuccessfullySyncedToServer" events where the second behaves a lot like a Redux reducer? I don't see any of that in the examples, so is there another way this complexity is typically avoided in Bloc? Or is Bloc entirely unopinionated on these things?
I'm not looking for personal opinions here, only if there's something I missed in the Bloc manual (or other authoritative source) about how this was intended to work.

riverpod conditional provider state updates

I'm porting my app to river_pod, it's been great so far but I always stumble upon the same problem. There is some situations where I need a provider to update its state only conditionally depending on the new value acquired by the ref.watch.
An example of this is my last road-block:
I have a ChangeNotifier provider that exposes the current user location. This provider is listened to by multiple other providers. One of them is a FutureProvider that fetches the trending posts nearby every time the location changes. The problem here is that this location updates very frequently (every 10s or so) so this fetch is done a very unnecessary amount of time.
What I would like to do in that situation is, in this FutureProvider, be able to get the new position but update only conditionally (here the condition being, if the last fetch was done more than 1km away) to avoid this unnecessary network call and all underlying UI updates it causes.
This implies two things, having access to the last state to make the comparison, and be able to cancel an update (because here even if I don't do the fetch and return the last value, the UI will still read that as an update).
I understand that those mechanisms are not built-in, so I was wondering, was is the river_pod way to approach this problem?
Cheers!
I was having same problem to solve. I had to compare old data and new data and change the state in provider only if there is a change.

Does setting listen to false on StreamProvider prevent cloud firestore read charge?

So I am using the provider architecture, more specifically, the StreamProvider, to get a list of documents
in a collection called 'Timeline Posts'. One of my goals is to minimize firestore reads and hence costs, so my question is:
If I set listen=false, I know this prevents my UI from updating when there's an update in the documents but does it also prevent firestore from reading that update and charging it as one read. Because I know everytime a document is updated and you're using stream, it counts as a read.
So does listen=false affect both my UI in flutter and the firestore read
From reading the documentation it will still read the changes in the stream. You could switch to a FutureProvider in order to prevent this from happening. If you share your code I would be happy to help you make that switch. A future is something you only would like to read one time and a stream is used for tracking real-time changes.
Why not use Futures instead? I generally use Futures instead of Stream for the situation you are describing.

How to make my pages in flutter persistent?

I want to make all my inherited pages persistent using flutter but I don't know how does anyone know how to accomplish this?
Given the fact that this cannot be answered properly without seeing your implementation, here is what a lot of developers do:
If you application is simple, store each state as a key-value pair. Then store that value and key inside shared preferences.
You would simply need to:
Add shared_preferences: "<newest version>" to pubspec.yaml
Synchronously update the key-value pair in-memory using prefs.setInt('currentState', value);. This persists the data to the disk.
Read your state data suing final counter = prefs.getInt('currentState') ?? 0; and accordingly update your setState method:
Now, this does infact assume that you have a simple application with simple states. If you have more complicated states and actions, then you should consider things like the BLOC pattern in order to organize your app in a way that enables a larger amount of control.