Is a Stream<List<Object>> from Firebase snapshot returning the entire node at every change in the node? Flutter - flutter

I'm starting learning Flutter as I want to port my iOS app to Flutter to also run on Android.
I use Firebase real time database for the back-end and I saw that the firebase_database package should suit my needs, allowing me to use the db I already have.
I'm also learning to use the BLoC pattern but all the bloc tutorials I found are for Firestore and all the Event/State/Bloc/Repository/Streaming is still quite confusing to me right now.
What should be the right implementation of the bloc pattern?
To a basic level I do understand it and I like it very much, but thinking of a way to implement it for my needs is resulting a bit overwhelming.
Following this diagram the 6 connections data flow should be:
(UI -> BLoC) An event will be sent to the bloc.
(BLoC -> Repository) The Bloc maps the event to a Repository method.
(Repository -> Database) The Repository methods are database Create/Update/Delete methods.
(Database -> Repository) The Repository database Read method gets database Data.
(Repository -> BLoC) The return from Repository database Read method takes the Data into BLoC.
(BLoC -> UI) The BlocProvider uses the Data to rebuild the UI.
Now what I'm not sure I figured out is the data going back to the UI.
At step 2 if I map the event to a method that returns the data as a Stream<List<Object>> then via a BlocProvider at step 6 the UI(stateless widget) gets rebuilt accordingly.
Now, as I see it, that would be a lot of unnecessary repeating data downloads if for any change, the whole node gets downloaded, also Google's bills you on downloads.
In the Firebase iOS SDK you can attach observers to a node, so you just get a snapshot with the .childAdded/.childRomovedetc etc, and use it to modify your UI.
To avoid useless downloads and keep my account safe, I'd rather make a query on a node with the keepSynced bool set to true ( https://pub.dev/documentation/firebase_database/latest/firebase_database/Query-class.html ). This should (as the iOS SDK) return at first firing, a snapshot with the whole node and than snapshots with just new/updated/delete when something changes right?
To implement this way instead, should I use a List<Object> that I update manually when getting the snapshot and a stateful widget? This is actually how my iOS app works at the moment.
Thank you very much for the help and sorry for the long question, making the switch from Swift is taking it's time.

Firebase listeners only transfer the minimal amount of data that actually changed at the node being listened to. It does NOT transfer the entire node and all of its children each time anything changes. So, it's not as expensive as you're imagining it to be.

Related

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.

Flutter is using provider to load data is the right option?

So i have a situation where i make an request from the server for one widget.
The widget is at the home page, lets take the worst case where the data is huge and the request take time.
Should i change the widget to stateless and make a provider which i will initialize before i run the app with all the initial data?
Should i contain all the data of the widgets at home page and deliver theme as props, i miss understood the concept of managing the state here, I'm coming from vue and i try to write my first app and I'm struggling how to structure my data through the routes.
I would like if some one explain or give a good source that show how to initialize data from third party
before the home page reload.
Which approach is better getting all the app data before the app reload or request data every time from db with cash
You might have seen this approach in other apps as well which is to show a splash screen until the data has been loaded and ready to be shown. This approach is mostly used by apps which got large data to load at the start. You could achieve this in your initState like the following.
#override
void initState() {
loadData();
splashTimer = Timer(Duration(seconds: 4), () {
_goToHome();
});
super.initState();
}
State management in flutter is a topic with hot debate, there is no best approach, but using one for sure is better than nothing. However there are exceptions to this, sometime adding a state management to a simple part of the app is not recommended. Regarding your case, it can be done without a full state management solution, by using a FutureBuilder for example. Or it can be also done with Provider, BloC, Redux...
As a naïve general rule, if the state is to be passed down the widget tree more than 1 or 2 levels, you should probably start looking for a state management solution depending on the use case. As I already have said, there is no one best state management solution.
Also, it is ok to use more than one as long as you know what you are doing but in general as a best practice it is not recommended to use more than one.
Regarding the second part of the question, it totally depends on the nature of the data and it's size. If the data is big and it is a small possibility that the user will be using all of it, it is better to load it on demand, also loading all the data upfront will increase the cost on the backend side.
However getting the data upfront, makes the experience more seamless to the user (Not waiting while using the app, but he will have to wait a little extra when the app is first loading).
So as you see it is a balance. Also it is good for the server and the app to do some type of caching since it helps reduce the work on the server side and decrease the bandwidth usage on the phone.
An example for caching images you can use Cached Network Image Link, example from flutter cookbook Link.

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.

Can iOS14 widget requests main app to update its data?

my app needs latest data but, possibly, the main app has old data.
so i want to request to the main app to update its data.
is it possible?
if it is not I would like to know any alternative way.
(should the widget requests latest data to the server??)
thanks
===
UPDATE
I found many tutorials and all of them use http request from getTimeline method for getting newest data (not from the main app). it is not what i want.
Someone said easest way for sharing data between main app and widgets is using UserDefaults. But the data can be old if the main app does not update userdefaults values or update values using old data. (beside server data is always latest data)
So what i want to know is the way 'widget makes main app updates its data and updates userdefault and call reloadTimelines. so widget can use latest data.
There's no way for a Widget to tell its parent App to refresh its data.
You can either:
fetch data directly in the Widget - see: How to refresh Widget data?
enable background notifications in the App (see How to run code when your app is terminated) and, when received one, force the Widget to refresh its timeline using:
WidgetCenter.shared.reloadAllTimelines()
In reality it's probably better to just fetch data directly in the Widget. This way you can fetch only the necessary data - Widget views usually display a lot less information than App views.
Yes, you can like this;
When you create a TimelineProvider for widget update, you have to implement this func,
func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> ())
This function waits to create your widget update timeline. You can sen a request, after the response, you can create a timeline and return func's completion.
You can configure an App Group and store the database in that shared group directory. That way, if you update data in the main app or in the widget, those new data are available for both, it's not necessary to call the other one to load the same data again. Just be aware that you should still use
WidgetCenter.shared.reloadAllTimelines()
after you updated the data in your main app to refresh the widget immediately. Just have some handling for when the last request was performed, so you don't create an unnecessary request in your widget, and instead fetch them from the database.
If you move your request-performing code into a modul, you can also use it in the widget, as you do in your app. Finally you'll just have another call of the same updating code you are used to from your main app.
Since I don't have any of your code to update and present here, I'll just link some helpful sources for the way to the described destination.
Apple Documentation: Configuring App-Groups
Sharing data within App-Groups

Does Firebase Realtime .once() still download the entire node, when .keepSynced(true)?

I am trying to reconcile two seemingly conflicting sources of information from Google.
In the official docs they say that 'value' from .once() is downloaded in full.
But here in a fascinating post from a Firebase expert at Google, Frank van Puffelen, he says that .keepSynced(true) uses 'delta-sync' to only sync the parts which have changed.
In this case, if I set keepSynced(true), and then call .once() on this same node, presumably it is not downloading the entire 'value' , only what has changed - is this correct?
If you call once() you will get the value as it is currently cached.
If on the same node you called keepSynced(true) the value in the cache should be up to date with the server.
By using the combination of both, there won't be a client-to-server call per once() call, but there will still be client-to-server call when the app starts (to attach the implicit listener of keepSynced(true)) and a server-to-client push whenever the data changes.
It sounds like you're trying to work around a problem, instead of addressing it head on though: if data can change, you should use on() listeners instead of once().