Flutter Provider: Should I create a new Provider for every class? - flutter

Lets say I have multiple settings a user can set. Should I have one Provider which manages all settings like so:
class Settings with ChangeNotifier {
SettingsA _settingsA;
SettingsB _settingsB;
List<String> _settingsC;
SettingsA get settingsA => _settingsA;
SettingsB get settingsB => _settingsB;
List<String> get settingsC => _settingsC;
// Setters
void updateA(SettingsA settingsA) {
_settingsA = settingsA;
notifyListeners();
}
void updateB(SettingsB settingsB) {
_settingsB = settingsB;
notifyListeners();
}
void addToC(String setting) {
_settingsC.add(setting);
notifyListeners();
}
}
Or should I rather make a Provider for every object like so:
class SettingsAProvider with ChangeNotifier {
SettingsA _settingsA;
SettingsA get settingsA => _settingsA;
// Setters
void update(SettingsA settingsA) {
_settingsA = settingsA;
notifyListeners();
}
}
What is the best practise of using ChangeNotifierProviders?

In my opinion, you should use SettingAProvider,SettingBProvider,...
If you use Settings Class...
When you call updateA, it will notify all value _settingA,_settingB,_settingC,... even if unecessary.

Related

Combining Riverpod Providers Bidirectionally

How can we access a method from the being wrapped riverpod provider?
ContentProvider can access user value from UserProvider by using "watch". There is no problem for this direction. On the other hand, UserProvider also needs access to the methods of ContentProvider. So bidirectional communication is required.
For this case, I need to call deleteContents method from UserProvider.
I don't prefer to merge them to keep logic safe.
class ContentProviderNotifier extends ChangeNotifier {
final User? currentUser;
ContentProviderNotifier({required this.currentUser});
addContent(Content content) {
content.user = currentUser?.name;
...
}
deleteContents() {
...
}
}
final contentProvider = ChangeNotifierProvider<ContentProviderNotifier>(
(ref) {
final user = ref.watch(userProvider).currentUser;
return ContentProviderNotifier(currentUser: user);
},
);
class UserProviderNotifier extends ChangeNotifier {
UserProviderNotifier();
User? currentUser;
deleteUsers(){
// here to call a method from contentProvider
deleteContents();
}
}
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
return UserProviderNotifier();
},
);
If I try to feed UserProvider with ContentProvider like this
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
final content = ref.watch(contentProvider); // <----
return UserProviderNotifier(content);
},
);
But I know, It won't make sense.
The type of 'userProvider' can't be inferred because it depends on itself through the cycle: contentProvider, userProvider.
Try adding an explicit type to one or more of the variables in the cycle in order to break the cycle.darttop_level_cycle
You can create UserProviderNotifier so it takes ref as an input, like this:
class UserProviderNotifier extends ChangeNotifier {
UserProviderNotifier(this.ref);
final Ref ref;
deleteUsers() {
// here to call a method from contentProvider
ref.watch(contentProvider.notifier).deleteContents();
}
}
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
return UserProviderNotifier(ref);
},
);
This section of the Riverpod docs mentions this is a common use-case.

Flutter GetX Provider & GetConnect Settings

I created a Flutter project with GetX CLI. Then i created a provider with get generate model. But i have problems with provider:
When i bind it with lazyPut, onInit() never works.
When i bind it with put, onInit() works but httpClient.defaultDecoder section does
not executed.
When i bind it with put, onInit() works and httpClient.baseUrl is adjusted but i can't read it in getFaculties. It seems like null in global.
When i put httpClient.defaultDecoder and httpClient.baseUrl in getFaculties, works well. But doesn't seem like the right way.
Am i doing something wrong or is it about GetConnect? Thanks in advance.
Provider:
class FacultyProvider extends GetConnect {
#override
void onInit() {
print('PROVIDER INIT!'); // Works with Get.put(FacultyProvider()) in Bindings
httpClient.defaultDecoder = (map) { // NEVER WORKS
if (map is Map<String, dynamic>) {
print('MAP!');
return Faculty.fromJson(map);
}
if (map is List) {
print('LIST!');
return map.map((item) => Faculty.fromJson(item)).toList();
}
};
httpClient.baseUrl = 'http://localhost:3000/public';
}
Future<List<Faculty>> getFaculties(bool cache) async {
print(httpClient.baseUrl); // ALWAYS NULL
final response = await httpClient.get('/faculties/true'); // NO HOST ERROR
return response.body; // TYPE ERRORS because of defaultDecoder section didn't run
}
Bindings:
class FacultiesBinding extends Bindings {
#override
void dependencies() {
Get.put(FacultyProvider());
// Get.lazyPut<FacultyProvider>(() => FacultyProvider());
Get.lazyPut<FacultiesController>(() => FacultiesController());
}

Flutter/Riverpod: Call method within StateNotifier from another StateNotifier

How can I call the method of one StateNotifier from another StateNotifier? I want to call addNewHabit (in the top class) from submitData (in the lower class).
Here are the bodies of the classes:
class HabitStateNotifier extends StateNotifier<List<Habit>> {
HabitStateNotifier(state) : super(state ?? []);
void startAddNewHabit(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (_) {
return NewHabit();
});
}
//this one right here
void addNewHabit(String title) {
final newHabit = Habit(title: title);
state.add(newHabit);
}
void deleteHabit(String id) {
state.removeWhere((habit) => habit.id == id);
}
}
and
class TitleControllerStateNotifier
extends StateNotifier<TextEditingController> {
TitleControllerStateNotifier(state) : super(state);
void submitData() {
if (state.text.isEmpty) {
return;
} else {
//call 'addNewHabit' from above class
}
}
}
What is the correct way to do this?
Technically, not a recommended pattern to use as StateNotifiers are controllers and you shouldn't really be calling controllers inside controllers.
But this is pretty easy to accomplish as you can read other providers inside a provider.
final habitProvider = StateNotifierProvider((ref) => HabitStateNotifier());
final userControllerProvider = StateNotifierProvider((ref) {
return UserController(
habitProvider : ref.read(habitProvider),
);
});
And then use the reference and call
class TitleControllerStateNotifier
extends StateNotifier<TextEditingController> {
TitleControllerStateNotifier(state, HabitStateNotifier habitProvider) :
habit = habitProvider,
super(state);
final HabitStateNotifier habit;
void submitData() {
if (state.text.isEmpty) {
return;
} else {
habit.addNewHabit(state.text);
}
}
}
Set up a StateNotifierProvider (from Riverpod), which will give you back a StateNotifier after running the create callback. This callback has a (ref) parameter on which you can call ref.read and ref.watch to fetch other providers in either non-depending or depending mode.

How to listen to variable changes in a provider class?

When using providers, how can I listen to a value change in the provider class?
In the code below, when _myData changes, I want to change _allData as well. (I know I can just change them both together, but the code below is a stripped version of what I have)
class CoolProvider extends ChangeNotifier {
List<String> _allData = [];
List<String> _myData = [];
List<OrdersResponse> get allData => _allData;
List<OrdersResponse> get myData => _myData;
void changeData() {
_allData = ['yo', 'bro'];
notifyListeners();
}
}
You can use addListener for instance of your class.
CoolProvider coolProvider = CoolProvider();
void f() {
// access new data
}
coolProvider.addListener(f);

When I should create separate Provider class?

I have class that process data. I have two options.
1. To registration it as Provider directly:
class GetServerData extends ChangeNotifier
{
String resultDate;
getData() {
resultDate = http.get(...);
notifyListeners();
}
}
Сreate wrapper-provider class. And use it as Provider.
class ServerDataProvider extends ChangeNotifier {
String resultDate;
GetServerData getServerData = GetServerData();
getData() {
resultDate = getServerData.getData();
notifyListeners();
}
}
When I should prefer to use first variant and when second?