How to reduce/optimize api call when initial a bloc - flutter

I have a navbar, change between 2 screens nested with Provider create blocs. Whenever bloc constructor called. It's call api to get data and add to the stream. So the problem here, user can spam switch between 2 screens and make the bloc dispose and init => api was called multiple times.
Class Bloc1 {
const Bloc1(){
data = await fetch() //Call api
stream1.add(data) //Then add to stream
}
}
I have tried the lock. But it does not work because when recreate, the lock is recreate too -> useless.
Class Bloc1{
var lock = false;
const Bloc1(){
if(lock == false) {
data = await fetch() //Call api
stream1.add(data) //Then add to stream
}
}
}

In my opinion, the issue you are mentioning is a kind of a border case where the user will have an aggressive behavior with the switch.
That being said, I think you should clarify a few points:
Do you really want to change the switch behavior to avoid this scenario?
Does it make sense to fetch new data every time the user switches tabs? I mean, does the data change that often? Because if the data does not change, maybe it does not makes sense to make a new request to the API at that point, and you should consider fetching this data at some other point.
I think there is no way to avoid this scenario if you instantiate/dispose your bloc every time the user switches screens (unless you save the lock or previously retrieved data outside the bloc, so next time you instantiate this bloc you can provide this value via the constructor, and this way you can avoid making the API request).
Depending on your Bloc logic, a solution could be that you instantiate the Bloc above these two screens, by doing this the Bloc will not be disposed once you switch screens and therefore it won't be instantiated again. Take into account that this approach will make the Bloc to be alive no matter how many times the user switches screens, and it is possible that this is something that you want to avoid.

Related

Update Flutter state with GetX when the routing changes

I'm using GetX to manage the state of my Flutter app, and the structure of my pages consists of an ItemsListPage that lists some items, and an ItemDetailsPage that shows details about an specific item and lets the user change it or delete it.
I'm managing state using GetX's Reactive state management, which means that, in order to update the state of my ItemsListPage, I need to update an observable variable itemsList that is inside the ItemsListController. This variable is updated on the onInit() method of the controller, which means that whenever the controller is instantiated the list should be updated with the data fetched from the API.
The controller of the ItemsListPage looks like this:
class ItemsListController extends GetxController {
var itemsList = <AppEntity>[].obs;
#override
void onInit() {
super.onInit();
// Fetch items from the API and store them on itemsList:
getApps();
}
//...
}
And the controller of the ItemDetailsPage is like this:
class ItemsDetailsController extends GetxController {
void deleteItem() async {
final response = await deleteItem();
response.fold((failure) {
// Deal with a failure on the request
}, (response) {
if (response.success) {
// I want to update the state of the list from here
Get.toNamed('/main/');
Get.snackbar('Deleted', 'Item deleted successfully!');
}
});
}
The problem is that when the user changes or deletes an item (on the ItemDetails page), he gets redirected to the main page via Get.toNamed('/main'), and sees the old version of the list, because the onInit() method isn't called again.
Is there a way for me to trigger an action everytime the user is redirected to the ItemsList page? This would allow me to fetch the fresh data from the API and update the page. Another solution would be a way of updating the state of the ItemsListController from the ItemDetailsController, which would be even better, as it would not require another call to the back-end.
If you are using get_cli, then you can pretty much generate and use named routes along with your view, controller and bindings. But if in case you are just using the state management without binding, then this problem may occur.
The thing happening in this scenario is, your controller is building one time and then your onInit method is called, after that you do some operation on your list like deleting or adding some data, and then when you go to another screen, your controller doesn't get disposed(Strange).
You can solve this by using these ways,
Whenever you switch to another screen, let's say using Get.toNamed(route) or Get.offNamed(route) or Get.off(route), what you can do is before navigating to other screen, you can remove the controller form the memory like this,
Get.delete<ControllerName>();
In this way, your controller will be again reinstantiated when you open your screen again(Provided that you have put the Get.put(Controller()) In your widget.
If you are using named routes then you can pretty much directly use them, you don't need to do anything in order to work with them. In case you're facing the error even if you have bindings and named routes then you can perform step 1.
If you want to minimize the API calls then whenever the user deleted some item from the list, you can delete it locally and update the UI according to it. This way user won't have to see the loaders again and again.

How can I notify other screen to re-fetch when there is a mutation?

I have some api that are going to change the data in the backend.
Each screen has its own state, I start fetch the data in the initState, and store it to the state once available.
The problem is how can I notify the screen in the backstack to refetch when I know their data in the state is now outdated? They won't be refetch when I am back to them since the initState won't be called again.
I come from React Native, where there is react-query, which will refetch and rebuild the screen when I invalidate the corresponding cache. Is something similar available in flutter?
You can try this, please put this on your first screen.
Navigator.of(context).pushNamed('/someRoute').then((completion){
your initstate function
});
Ref : https://api.flutter.dev/flutter/widgets/Navigator/push.html
If you need to notify more then one screens, use https://pub.dev/packages/flutter_bloc (There are other options) instead.

How does BlocProvider.value works with added Event

What will happen if An event is added to the bloc being passed inside the BlocProvider.value?
Will the event gets added before or after i get to the page?
And Is Blocprovider.value an ideal way to avoid adding events to bloc in initState() method?
I'm trying to avoid initstate as I'm listening to Stream of data from firebase (this event need to be called only once).
I found a workaround for this,
Using the approach stated above will make the LoadProductFromStream event get called even before getting to /opening the StreamproductPage().
To avoid this a simple and efficient approach could be used instead
create a wrapper widget
create the body (the main widget to display results)
Below is an image attached to explain this
Wrapper widget is where the bloc will be created/initialized
and where LoadProductFromStream() event will be added to the bloc
In conclusion
This way we only add LoadFromStream() event to bloc when we get to StreamproductPage
Takeaway
BlocProvider(create:(context)=> xyzBloc()) // can automatically close bloc
while
BlocProvider.value() // doesn't close bloc automatically
(i stand to be corrected)

Is it okay to call provider functions in main.dart Flutter

in Flutter,
I am calling provider functions to load data from database in main.dart and I saw that main.dart is calling them continuously, so I added bool to check if they called or if they are loading now to stop continuous calls, after that it started to concern me. Is this way okay? What is the better way? I don't want to add it to the first pages initstate as if user has details in SharedPreferences, I wait and send token to get different data.
Here is the calling part in main.dart file.
Checks if user has details in SharedPreferences.
Loads chats if user has details in SharedPreferences.
Connects to socket if not connected before.
If user has details, starts loading and in the first step it makes isLoadingUserForMain = false so that until it loads and gets responds it won't be able to call.
Please tell me your opinion thanks..
Look the best way of doing what you want to achieve is...
Make an Splash or middle screen of loading.
push all the asynchronous task at that Widget.
You can handle anything there.
It is not recommended to do asynchronous tasks in main function. So you need to make Material app to do the tasks..
I would have a splash screen like #Akash mentions, but I would call these methods when your provider is initialized. For example, lets say you create your providers like this at the top of your app:
runApp(MultiProvider(providers: [
Provider<DatabaseService>(
create: (_) => YourService(),
lazy: false,
),
],
child: MyApp()));
Your init logic in the question would exist in the constructor of YourService.
Now you could have a stream of userDetails that your splash screen will listen to and once it emits details go to the appropriate screen.

Flutter BLoC: managing the state of primary data types

I am developing a mobile application using Flutter. I am new to Flutter. I am using BLoC for state management. I know that it is specially designed for management async execution. But also the purpose of BLoC is for state management. But I am a little bit confused and not quite sure how to handle the primary data types in BLoC.
Let's imaging the I have a button and an image. The functionality would be that when the button is clicked, the visibility of the image will be toggled. Literally, we just need to use a boolean variable to manage the state of the image. This is how I would implement it.
I have a bloc class called HomeBloc with the following implementation.
class HomeBloc {
bool _isImageDisplayed = true;
bool get isImageDisplayed => _isImageDisplayed;
void set isImageDisplayed(bool displayed) {
this._isImageDisplayed = displayed;
}
//the rest of the code for other functionalities goes here
}
final homeBloc = HomeBloc();
Then in the HomePage widget, I update the state of the image like this inside the setState method when the button is clicked.
this.setState(() {
homeBloc.isImageDisplayed = false;
});
My question is that "is it the standard way to manage primary data type in the BLoC in Flutter"? Is this the best practice? Do we need to use StreamBuilder? Do we even need to manage it inside the BLoC?
It's not the best practice I guess, as using setState becomes really hard on big applications and re-rendering widgets that don't change for no reason. Imagine making an e-commerce app and you just go to the product page, you add the product you like into the cart, but you have designed in your home page a cart icon with a red dot with a number inside it to specify how much products you got in your cart, so you handle the state of that icon in the main.dart file by passing a function that setState the home page route or maybe the whole application, it's hard, isn't it?.
Thankfully, BLoC and Provider patterns are basically using setState but in a better way so you don't have to re-render the whole page just for a small change in a text or something else, but you just re-render a specific widget in your widget tree.
I also recommend using BLoC Provider which is built on Provider and RxDart (Streams) as it makes great isolation between UI code and business code.
Check Provider and BLoC Provider.