I want to load some data from an SQLite database in my Flutter application and show the results in a ListView. I am currently using a FutureBuilder widget to fetch the data asynchronously from the database and then build the ListView.
However, if I want to do some operations on the data - e.g. modify some information and save it to database - and then show the updated data in the ListView, I think I have to store the data in a local variable first and then call the setState() method to make changes to the data and re-build the page.
Is there any other better/preferred way to achieve the same without using FutureBuilder?
initState gets called when your widget gets initialised for the first time. There you do some initialisation operations.
I think you're right to use FutureBuilder, since your UI gets generated after fetching the data from the Database.
I would do one of the following:
Keep using FutureBuilder, then when you edit the data make an async operation to update the database and after the database is updated call setState to reflect the changes on the UI.
Use a StreamBuilder, which is similar to FutureBuilder, but instead of building only once, it rebuilds each time there's a change on the stream it's attached to. This way you just need to update the datasource and the widget will update itself with the new data.
Talking about the second solution, I cannot go any further since I've never implemented a Stream that would allow to do this (I've always used StreamBuilder it with Firebase streams).
The first solution allows you to update the displayed data without asking again for the entire data set to the database.
I might appreciate some feedback from others regarding the second solution, since I'm not sure about how StreamBuilder manages a change of data.
I'll let you know if I discover more!
https://stackoverflow.com/a/52021385/11252673
build() might be called several times - that's why this kind of initialization goes better on initState.
Related
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
I was wondering what is the better option to get documents from firestore.
Should i run the getdocuments in initstate or should I run it as a future builder.
So far these are the pros and cons i have seen.
initstate - if I were to retrieve it initState, then everytime i click on a post to see its details, and when i go back, the initstate runs again, creating more documents read. However, this does help refresh certain values, incase of a user bookmarking a post, that way the bookmark value gets updated, however in the cost of more documents read.
futurebuilder - seems cost efficient in the sense that, when i click on a post and go back, no more documents are retrieved from firestore, however, if i were to bookmark the post in the postdetails page, the value does not get updated. However to work this around, i could just remove the bookmark value in the Post page.
I have however, used the getdocuments in the initstate when i go into the postdetails page by deseralizing it.
Please advise.
Calling getDocuments() is deprecated for get() as per the change log.
I would do one of the following:
If using initState, the easiest way to deal with this is to store a
Future variable in your state. In initState make the API call and
store the Future. You can then pass this Future to FutureBuilder.
Keep using FutureBuilder, then when you edit the data make an async
operation to update the database and after the database is updated
call setState to reflect the changes on the UI.
Use a StreamBuilder, which is similar to FutureBuilder, but instead
of building only once, it rebuilds each time there's a change on the
stream it's attached to. This way you just need to update the
datasource and the widget will update itself with the new data.
So my answer would be it depends on your use case. If your data updates every min/sec while you use the app, it needs updates asynchronously at a certain interval, in that case StreamBuilder is the best option but If your use case is to just get the data, and display it, then you can use FutureBuilder.
I'm somewhat new to flutter and my question is more philosophical. When using setState(), is it bad practice to call it without using new data in the method? Ex: I call it like this:
setState({});
If there's a better way to refresh the build function or is this the best way?
setState notifies that internal state of the object is changed, that reflect on entire subtree.
If you just change the state directly without calling setState, the framework might not schedule a build and the user interface for this subtree might not be updated to reflect the new state.
There are some other ways to trigger build method when data changes, but easiest one is setState() and for your purpose it is best.
No need to use new data in your case. Your way of doing this is exactly how the Flutter team intended it to be used.
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
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.