Combining Riverpod Providers Bidirectionally - flutter

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.

Related

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

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.

Read provider inside a global or static method in Flutter

I have a question, regarding reading providers from inside static methods or global methods. I am using riverpod and awesome_notification packages, and I need to alter the state the app, from the action of the notification, for this, the package uses static methods inside a controller class.
class NotificationController{
...
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
...//some way to access a provider, to call methods on it
}
...
}
If there is another way of doing this that I am not seeing, please let me know.
I have not been able to find a way to do this.
You can:
Pass to the ref function as a parameter.
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction, Ref ref) async {
final some = ref.read(someProvider);
}
Create a class that accepts the ref field in the constructor.
final notificationProvider = Provider((ref) => NotificationController(ref));
// or use tear-off
final notificationProvider = Provider(NotificationController.new);
class NotificationController {
NotificationController(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
final some = _ref.read(someProvider);
}
}
An additional example:
import 'package:riverpod/riverpod.dart';
final valueProvider = Provider<int>((_) => 5);
final managerProvider = Provider(ManagerProvider.new);
class ManagerProvider {
ManagerProvider(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static int getValue() => _ref.read(valueProvider);
}
void main() {
final container = ProviderContainer();
container.read(managerProvider);
final value = ManagerProvider.getValue();
print(value); // 5
}
Either way, you should always have access to `Ref'.
Update:
As #OppositeDragon and #Eran Ravid pointed out, we really can't access _ref in a static method. However, if you define _ref in the constructor, it is possible. I think it's a terrible anti-pattern, though. Use method 1 and you will be fine.

How do I update Flutter's Riverpod values from business logic?

When using Flutter and Riverpod, how do I update its values from my business logic?
I understand that I can get and set values from the UI side.
class XxxNotifier extends StateNotifier<String> {
XxxNotifier() : super("");
}
final xxxProvider = StateNotifierProvider<XxxNotifier, int>((ref) {
return XxxNotifier();
});
class MyApp extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
// getValue
final String value = ref.watch(xxxProvider);
// setValue
context.read(xxxProvider).state = "val";
return Container();
}
}
This method requires a context or ref.
How do I get or set these states from the business logic side?
Passing a context or ref from the UI side to the business logic side might do that, but I saw no point in separating the UI and business logic. Perhaps another method exists.
Perhaps I am mistaken about something. You can point it out to me.
You can pass ref in your XxxNotifier class:
class XxxNotifier extends StateNotifier<String> {
XxxNotifier(this._ref) : super("");
final Ref _ref;
void setNewState() {
state = 'to setting';
// use `_ref.read` to read state other provider
}
}
final xxxProvider = StateNotifierProvider<XxxNotifier, int>((ref) {
return XxxNotifier(ref);
});
// or using tear-off
final xxxProvider = StateNotifierProvider<XxxNotifier, int>(XxxNotifier.new);
You can create methods in your XxxNotifier class to modify the state of your provider.
For example, your notifier class can look like this.
class TodosNotifier extends StateNotifier <List<Todo>> {
TodosNotifier(): super([]);
void addTodo(Todo todo) {
state = [...state, todo];
}
}
You can then read the provider in a callback.
ref.read(xxxProvider.notifier).addTodo(todo);

Flutter : Call value from second function class

I have Auth class which has sgin in function and inside of this function value for example user value , so how to get this value from Auth class to Home class ?
Here Auth class and Home Class :
class Auth {
void signIn(){
String user = "I LOVE PROGRAMMING";
}
}
class Home {
return Text("$user") //Iwant user value from clas AUTH
}
You would want to have the signIn function return the user.
class Auth {
String signIn() {
String user = "I LOVE PROGRAMMING";
return user;
}
}
Then you can call that from another class:
class Home {
Widget someFunction() {
final auth = Auth(); // create a new instance of Auth
final user = auth.signIn(); // call the function and put the return value in a `user` variable
return Text(user); // use the `user` String in the Text widget
}
}
I would advice you to have a look at the Dart language tour and learn about these things step by step. These are the basics you will need to get anything done. Good luck!
You should be implement below way
class Auth {
static String _user;
void signIn() {
_user = "dfsdfgdfgfdgdfgfdgfdgfdgfd";
}
static String getUser() => _user;
}
class Home {
return Text(Auth.getUser())
}
Firstly, you need to create a global variable for the String user that you have set in the Auth class and create a getter for it. You also would need to use some type of state management to simulate the value changes. In this case I'll use the provider package.
class Auth extends ChangeNotifier{
String _user = '';
void signIn() {
_user = 'SomethinInteresting';
}
String get userName => _user;
}
Whenever the signIn() function is being called from other component, the value for the user would be set. And finally if you want to access the value from other component you can use the getter to set the value in the Text component.
class Home {
final _auth = Provider.of<Auth>(context);
#override
Widget build(BuildContext context) {
return text(_auth.userName);
}
}
This would also facilitate dyanmic changes to the fields in the Auth class.
For accessing the value from the "Auth" class you first need to make the method type String and return the value when the method is called. In the Home class there must be main method as it is from where the execution of the program starts. Now, create an object of class "Auth" and call the method using the same object. You'll get the user value.
class Auth {
String signIn(){
String user = "dfsdfgdfgfdgdfgfdgfdgfdgfd";
return user;
}
}
class Home {
public static void main(String args[]){
Auth ob=new Auth();
System.out.println(ob.signIn());//I want user value from clas AUTH
}
}

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?