In my app, in a certain screen, there are 3 ToggleButtons, which represent 3 types of products, and a list of products of that category which will be given by an API call to a provider class. the list can be viewed in 2 ways, each a seperate widget. note this is a simplification as there is at least 2 widget files between the screen widget and the togglebuttons widget or the listitem widget files.
What i want to achieve is that on first load, the API is called (a future) to provide a list of products of the 1st category, which is displayed by default, and when a user clicks on the ToggleButton of the 2nd or 3rd categories, a new API call is made and the list is rebuilt with the new data. What would be the correct provider to use in this case?
I have tried using a FutureProvider, but it didn't work as it would either not build the list at all, or rebuild it constantly. StreamProvider doesn't seem to work either as it's builder is only run once.
NotifyListeners is the easiest and simplest way to work with the Provider where we just need to call the notifyListeners() method to tell the “listeners” to rebuild and get the new value.
You can just call notifyListeners() when you click on the toggle button.
Note you can only notifyListener() in the class which extends ChangeNotifer.
So in that class create a function which will do your 2nd category API and write changeNotifier in it.
class CreditCardViewModel extends ChangeNotifier {
changeCategory(String category) {
//API Call for the new category
response = ApiData;
notifyListeners();
}
getAPIData(){
return response;
}
The best provider in the end was the changeNotifierProvider.
I initialize the list of items to an empty list at first, and in the provider's constructor I call a Future, but I don't await it. the Future calls the api and sets the list to be the incoming data, then calls notifyListeners(). The list, now changed, displays the new data.
Related
Im in a scenario that I need to receive data on a async task, then decide wether to navigate to the next page or not based on the replying.
But as far as I know, the state management in GetX support widget rebuild when value change in GetxController using .obs and obx().
What should I do to observe the value change then trigger the navigation?
It turns out a simple ever<T> method inherited from the GetxController class do the trick.
And it turns out that you shouldn't hybrid provider, get_it and get_x all together. Bring lots of unnecessary pain which will cause some of the feature in get_x not working.
You can us navigation in your async method or even in your method on complete call.
Suppose you call your async method and use dot then method you can put your response value in a varibale and can put conditions on it either it should navigate or not.
Future<int> getData()async{
print('Asynce Method');
return 1;
}
getData().then((result){
print(result);
if(result == 1){
// Navigate here
}
else{
// Don't Navigate
}
});
New to riverpod here. (Using Flutter and hooks_riverpod, btw).
Using Hive to store related lists of items. I need to call Hive.initFlutter and wait for Hive to be initialized, and do the same to open hive boxes, once when my app loads. After that, calls to Hive are synchronous.
My original idea (though I'm open to better ideas) was to create a StateNotifier that holds both the lists. This notifier could have a setUp function that's asynchronous. Here's what that looked like, simplified:
class ItemsNotifier extends StateNotifier<ItemsState> {
ItemsNotifier() : super(ItemsState([], []));
setUp() async {
await Hive.initFlutter();
// What is ItemDao? It's a data accessor object singleton used to house Hive logic.
await ItemDao().openBoxes();
// Putting a breakpoint here, I can see by calling `ItemDao().list1` etc that the lists have loaded with items as expected, but setting state here does not trigger a rebuild of the consumer widget.
state.list1 = ItemDao().list1;
state.list2 = ItemDao().list2;
}
...getters and setters and other functions omitted...
}
final itemsProvider = StateNotifierProvider<ItemsNotifier, ItemsState>((ref) {
final notifier = ItemsNotifier();
notifier.setUp(); // I've never seen anything to suggest that calling an async setUp method here is supported, it's just something I tried.
return notifier;
});
class ItemsState {
List<Item> list1;
List<Item> list2;
ItemsState(this.list1, this.list2);
}
As mentioned in the comments, I call an async setUp method while constructing itemsProvider. I put a breakpoint inside the setup method and inside my consumer widget. First the breakpoint inside the widget catches, and we see that list1 is empty, as expected. Next the breakpoint inside the setup method catches. We see that ItemDao().list1 is full of items, so loading from Hive succeeded. So I'd expect calling state.list1 = would cause the consumer to reload as it usually does. But it doesn't. The breakpoint in the widget doesn't catch again, the widget remains empty. Probably because Riverpod isn't expecting async methods to change state from inside the StateNotifierProvider constructor.
So a simple solution to this question might just be an answer to where in the app should I call setUp()? It would need to be somewhere that only runs once when the app starts. In an initState in a stateful widget somewhere? That doesn't quite feel right... as I said, I'm new to using riverpod.
Or if you have an alternate (better) way to architect this out, that would also be helpful.
Solutions considered:
I'll note that I also tried using riverpod's FutureProvider. It works for loading the list. But, as stated in the docs, FutureProvider can't be extended as StateNotifier can, so I'm not sure where I'd put custom setters and getters. And if I wrote one FutureProvider per each list, that wouldn't handle the fact that Hive.initFlutter should only be called once. I could see a ways around this, but it's a bit clunky, and thought I would be better off if someone with more experience advised me. StreamProvider seems basically the same as FutureProvider. Maybe there's a way to compose a FutureProvider inside a StateNotifierProvider? Really not sure what that would look like.
I have a class Communicator which where I've declared a few functions. Executing those function takes some time and an update of progress is required till all the functions get executed.
Now, I am using the functions of class Communicator in my stageful widget class HomeScreen. I want to pass the progress data from Communicator to HomeScreen and update the widget regularly upon receiving the progress data. How can this be achieved?
Communicator.addSomeListener(_handleSomeEvent); in init, and Communicator.removeSomeListener(_handleSomeEvent) in dispose.
void _handleSomeEvent(SomePayload data) => setState((){});
Your singleton can keep a list of these callbacks, and call them.
One way to that using callbacks like shawnblais explained. You can also do it by state management if you like. Provider will be a good place to start.
So i am writing a Flutter application that utilize the MVVM architecture. I have a viewModel for every screen(widget) with ValueNotifiers and i want to initiate the viewModel for that view.
Now most guides suggest a Provider approach, but why provide it when i can just normally initiate it.
Code:
class FooModel{
final ValueNotifier<bool> _active = ValueNotifier<bool>(false);
ValueNotifier<bool> get active => _active;
FooModel(){_active = false;}
doSomething(){_active=!_active}
}
What i want to do:
#override
Widget build(BuildContext context) {
_viewModel = FooModel();
return Scaffold(
body:ValueListenableBuilder<bool>(
valueListenable: _viewModel.active,
builder : (context,value,_){
if(value)return(Text("active");
return Text("unactive");
}
)
}
what is suggested:
Widget build(BuildContext context) {
return Provider<FooModel>(
create: (_) => FooModel(),
builder: (context, child) {
final vm = Provider.of<FooModel>(context);
return ValueListenableBuilder<bool>(
valueListenable: vm.active,
builder: (context, value) {
if (value) return Text("active");
return Text("unactive");
});
},
);
}
Now i understand that what i suggested creates the viewModel with every build, but that should only happen when screen is loaded thanks to ValueNotifier so its fine.
I guess i just don't understand the value of providing the viewModel.
Flutter has a different ideology.
yes, you can create Value Notifier and it's fine to do that but just thinking of the bigger picture.
Check this flow you want to call an API then perform parsing and filtering on that and you have 2 views on the screen to show the same data one is to showcase the data and the other one is to interact with data and this update needs to be reflected on showcased data.
to do this what we need to do?
create valuenotifier at class level that encloses both screen widgets.
Call API and filter code at the class level.
pass this valuenotifier to both screen widgets you may ask why right? well because one class need to update other class widgets. and that's only one way to push updates to the valuenotifier is the object itself. so you will need to pass this valuenotifier in both classes.
once you do that and update has been synchronized if any setState has been called to the main widget that encloses both of this widgets then you need to do all this again.
also there will be multiple instances of valuenotifier which is bad as valuenotifier is a stream and you need to close your streams once you're done with the stream so you will be needing logic to close your streams at any setState event on main widget.
What is provider exactly? and how it works? well, the provider is a change notifier class which calls setState when you call notifyDataChanged or notify method. this triggers the widget state change which is listening to that data changes.
and that widget gets rebuild. This is the same logic for each and every state management library out there Bloc, ScopedBloc, or any widget like streamBuilder or ValueListenableBuilder.
In Flutter if you want to change data you just need to call setState. Just to be testable, more readable and maintainable what we will be doing is to separate logic into different files just like we do in Android or iOS and that's where this type of libraries comes into the picture to reduce our headache of typing code all over again and focusing on the main task which is the functionality of the app.
See we can create loops in different formats
for(int i=0;i<length;i++)
while(i++<length)
for(i in 0...length)
It's up to us to provide clean and understandable code so that any other developer does not need to delete all code just because he isn't able to understand our code.
There's nothing right and wrong here in development. It's matter of what is more convenient or what makes more sense. like int i does not make sense but if we replace it with index or listIndex it will.
Also, one thing to mention is what you're doing is creating a model that is kind of the same as bloc pattern. so you're already halfway through. you just need to call state change from model check bloc and other patterns you will understand.
What I want
I have a simple model. The model extends from ChangeNotifier. If the ChangeNotifier calls notifyListeners() I want to "do" something like showing a SnackBar or Dialog. I provide the model with the Provider package to my widget tree.
What is it comparable to?
I used the flutter_bloc package before. This package offers BlocListener. With BlocListener I can "do" something on state changes. Example code:
BlocListener<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
In the above example, the child will not rebuild but I can still do something depending on the state.
Is there anything comparable to the provider package? I read in the documentation of the package that ListenableProvider would give more freedom to do stuff like "animations". But I do not know if I can use this Provider in some way to show a snack bar on a notify.
Edit: I asked Remi, the author of Provider, on Twitter. With a short amount of characters, he told me that I can use didChangeDependencies for this behavior.
Please be cautious about using didChangeDependencies for this. There are only a few circumstances where didChangeDepdnencies can be used for this, and https://github.com/flutter/flutter/pull/49527 will make it impossible even in those.
The basic problem is that didChangeDepdnencies is sometimes (or, after #49527, always) called at a point where the tree is locked against state changes. Before the pull request, it is only safe on calls that are:
Not the first time (which is called from within a build scope)
Not the time where an element is being deactivated and eventually unmounted, so is no longer in a valid spot in the tree (this call will no longer happen at all after the pull request).
A safer way to do this is:
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (Provider.of(context).whatever == someCondition) {
SchedulerBinding.instance.addPostFrameCallback(() {
// show modal or dialog
});
}
}
This code is safer to use because it is guaranteed to run at a point where state is safe to change in the tree, rather than only working in some very specific scenarios without the frame callback.
There are probably more elegant solutions than this (such as adding a callback directly on notifyListeners for your ChangeNotifier, assuming it only fires when the tree is in a mutable state).
try using listener on your provider
final myNotifier = context.read<MyNotifier>();
void listener() {
// Do something
}
myNotifier.addListener(listener);