I follow source code from documentation :
class Counter extends StateNotifier<int> {
Counter(): super(0);
void increment() => state++;
}
final counterProvider = StateNotifierProvider((ref) => Counter());
// ...
@override
Widget build(BuildContext context) {
// Obtains Counter without listening to its state.
// Will not cause the button to rebuild when the counter changes.
final Counter counter = useProvider(counterProvider);
return RaisedButton(
onPressed: () => counter.increment(),
child: Text('increment'),
);
}
Then i want custom it , when i click button i want show circularprogressindicator , after 3 second showing the text. So i already custom the example documentation to this :
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:state_notifier/state_notifier.dart';
class IsLoading extends StateNotifier<bool> {
IsLoading() : super(false);
void toggleLoading(bool value) => state = value;
}
final isLoadingProvider = StateNotifierProvider((ref) => IsLoading());
class TestingApp extends HookWidget {
@override
Widget build(BuildContext context) {
final IsLoading isLoading = useProvider(isLoadingProvider);
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: isLoading.state ? CircularProgressIndicator() : Text('This Is Your Data'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
onPressed: () => showLoading(isLoading), child: Text('Toggle Loading')),
),
],
),
),
);
}
void showLoading(IsLoading isLoading) async {
isLoading.toggleLoading(true);
print("Proses");
await Future.delayed(const Duration(seconds: 3));
print("Done");
isLoading.toggleLoading(false);
}
}
But i got this warning and although i click button , it not showing the circularprogressindicator:
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.dart(invalid_use_of_protected_member)
I missed something ?
As said by StateNotifierProvider, if you want to listen to the state of a StateNotifier, use "useProvider(myStateNotifierProvider.state)"
@rrousselGit but i still confused , i should make 2 variable to define state what i want listen and method from StateNotifier ?
something like :
final IsLoading isLoading = useProvider(isLoadingProvider); // it for method toggleLoading();
final bool loading = useProvider(isLoadingProvider.state); // it for listen state
Because documentation example i attach above , not showing to listen state and only show call method counter.increment()
Yes, that is correct.
Although the counter example does counter.read(context).increment()
It's more clear for me. But it's have option/possible make it more simple ? something like i only make 1 variable final loading = useProvider(loadingProvider) . and this variable can use for calling method and listen state ?
final loading = userProvider(loadingProvider);
//Listen state
Text(loading.state);
//Calling method
loading.toggleLoading(value);
with above approach , will make warning like my thread . Because make 2 variable with almost same name , make me confused.
No this is expected
It allows reading the notifier without listening to the state, to avoid pointless rebuilds.
Mhm, I am trying to do exactly that (one for reading and one for methods), but I can't seem to get the method way to work?
final StateNotifierProvider themeProvider = StateNotifierProvider((ref) {
return ThemeManager();
});
class ThemeManager extends StateNotifier<ThemeData> {
ThemeManager() : super(darkTheme);
void setTheme(ThemeData theme) async {
state = theme;
}
}
Then now when I try to obtain toggle setTheme, I retrieve it like so:
final ThemeManager themeManager = useProvider(themeProvider);
But I get error of:
The argument type 'StateNotifierProvider
>' can't be assigned to the parameter type 'ProviderListenable '.
What am I doing wrong here?
EDIT: Nevermind, looks like I just had to remove the type annotation for:
- final StateNotifierProvider themeProvider = StateNotifierProvider((ref) {
+ final themeProvider = StateNotifierProvider((ref) {
return ThemeManager();
});
As I mentioned, the syntax is "useProvider(themeProvider.state)"
@rrousselGit i don't know it's should create new issue or not , i got this warning again but in FutureProvider i want know this is best practice or not.
I have simple Http get request to show all user from server, and i using manage it using FutureProvider
class UserGoogleApi {
Future<List<UserGoogleModel>> getAllUser() async {
final result = await reusableRequestServer.requestServer(() async {
final response =
await http.get('${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getAllUser');
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
}
class UserProvider extends StateNotifier<UserGoogleModel> {
UserProvider([UserGoogleModel state]) : super(UserGoogleModel());
Future<UserGoogleModel> searchUserByIdOrEmail({
String idUser,
String emailuser,
String idOrEmail = 'email_user',
}) async {
final result = await _userGoogleApi.getUserByIdOrEmail(
idUser: idUser,
emailUser: emailuser,
idOrEmail: idOrEmail,
);
UserGoogleModel temp;
for (var item in result) {
temp = item;
}
state = UserGoogleModel(
idUser: temp.idUser,
createdDate: temp.createdDate,
emailUser: temp.emailUser,
imageUser: temp.emailUser,
nameUser: temp.nameUser,
tokenFcm: temp.tokenFcm,
listUser: state.listUser,
);
return temp;
}
Future<List<UserGoogleModel>> showAllUser() async {
final result = await _userGoogleApi.getAllUser();
state.listUser = result;
return result;
}
}
final userProvider = StateNotifierProvider((ref) => UserProvider());
final showAllUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.showAllUser();
return result;
});
After that setup, i simply can call showAllUser like this :
Consumer((ctx, read) {
final provider = read(showAllUser);
return provider.when(
data: (value) {
return ListView.builder(
itemCount: value.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final result = value[index];
return Text(result.nameUser);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
}),
it's no problem if http request don't have required parameter, but i got problem if my http request required parameter. I don't know how to handle this.
Let's say , i have another http get to show specific user from id user or email user. Then API look like :
final showSpecificUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.searchUserByIdOrEmail(
idOrEmail: 'id_user',
idUser: usrProvider.state.idUser, // => warning on "state"
);
return result;
});
When i access idUser from userProvider using usrProvider.state.idUser , i got this warning again The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.
How can i get idUser from userProvider ? and i want know this is best practice using FutureProvider ?
Same answer as before: ref.read(userProvider.state)
On Wed, 22 Jul 2020, 03:56 Zeffry Reynando, notifications@github.com
wrote:
@rrousselGit https://github.com/rrousselGit i don't know it's should
create new issue or not , i got this warning again but in FutureProvider
i want know this is best practice or not.I have simple Http get request to show all user from server, and i using
manage it using FutureProvider
Apiclass UserGoogleApi {
Future> getAllUser() async {
final result = await reusableRequestServer.requestServer(() async {
final response =
await http.get('${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getAllUser');
final MapresponseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
}User Provider
class UserProvider extends StateNotifier
{
UserProvider([UserGoogleModel state]) : super(UserGoogleModel());Future
searchUserByIdOrEmail({
String idUser,
String emailuser,
String idOrEmail = 'email_user',
}) async {
final result = await _userGoogleApi.getUserByIdOrEmail(
idUser: idUser,
emailUser: emailuser,
idOrEmail: idOrEmail,
);
UserGoogleModel temp;
for (var item in result) {
temp = item;
}
state = UserGoogleModel(
idUser: temp.idUser,
createdDate: temp.createdDate,
emailUser: temp.emailUser,
imageUser: temp.emailUser,
nameUser: temp.nameUser,
tokenFcm: temp.tokenFcm,
listUser: state.listUser,
);return temp;}
Future
> showAllUser() async {
final result = await _userGoogleApi.getAllUser();
state.listUser = result;
return result;
}
}final userProvider = StateNotifierProvider((ref) => UserProvider());
final showAllUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.showAllUser();
return result;
});After that setup, i simply can call showAllUser like this :
Consumer((ctx, read) {
final provider = read(showAllUser);
return provider.when(
data: (value) {
return ListView.builder(
itemCount: value.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final result = value[index];
return Text(result.nameUser);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
}),it's no problem if http request don't have required parameter, but i got
problem if my http request required parameter. I don't know how to handle
this.Let's say , i have another http get to show specific user from id user or
email user. Then API look like :
User Providerfinal showSpecificUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);final result = await usrProvider.searchUserByIdOrEmail(
idOrEmail: 'id_user',
idUser: usrProvider.state.idUser, // => warning on "state"
);
return result;
});When i access idUser from userProvider using usrProvider.state.idUser , i
got this warning again The member 'state' can only be used within
instance members of subclasses of
'package:state_notifier/state_notifier.dart'.How can i get idUser from userProvider ? and i want know this is best
practice using FutureProvider ?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/rrousselGit/river_pod/issues/52#issuecomment-662212619,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AEZ3I3IMSIXLCQMA64GQ4Z3R4ZIPHANCNFSM4O4FKPVA
.
@rrousselGit ref.read(userProvider.state); give me error :
The argument type 'StateNotifierStateProvider<UserGoogleModel>' can't be assigned to the parameter type 'ProviderBase<ProviderDependency<dynamic>, Object>'.
I think method read on Consumer and FutureProvider it's different.
read on Consumer accept Res Function<Res>(ProviderBase<ProviderDependencyBase, Res>)
read on FutureProviduer accept ProviderBase<ProviderDependency<T>, Object>
Oh my bad, that's for the next changes
For now you can do ref.read(userProvider).addListener((state) {...}
@rrousselGit i have 2 problem with this approach .
UserProvider
final showSpecificUser = FutureProvider.autoDispose((ref) async {
UserGoogleModel temp;
final usrProvider = ref.read(userProvider);
usrProvider.addListener((state) async {
final result = await usrProvider.searchUserByIdOrEmail(emailuser: state.emailUser);
// print('listenern RESULT ${result.nameUser} || ${result.emailUser} || ${result.idUser}');
temp = result;
print('Temp RESULT ${result.nameUser} || ${result.emailUser} || ${result.idUser}');
});
return temp;
});
And i simple call it like this
Consumer((ctx, read) {
final provider = read(showSpecificUser);
return provider.when(
data: (value) {
if (value == null) {
return Text('Not have data');
}
// print('specific user ${value.nameUser}');
return Text(value.nameUser ?? 'null');
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text(error.toString()),
);
}),

2.With addListener((state)async{}) it will always listen ,although i already used FutureProvider.autoDisposed.
Although already used FutureProvider.autoDisposed , after logout it's still listen old value. please see below gif.

Can't you just add a getter for state in your class that extends StateNotifier? Assuming that your FutureProvider is being properly disposed after each use that should be a suitable workaround until the new changes to Riverpod are live. I did a quick test to see and it does work. Make sure you define a getter like this and don't override the default defined by StateNotifier.
class A extends StateNotifier<B> {
...
static final provider = StateNotifierProvider((ref) => A());
getState() => state;
...
}
final provider = FutureProvider.autoDispose((ref) async {
final a = ref.read(A.provider);
final t = a.getState();
print(t);
});
Not ideal but seems like a fine workaround. I believe the intention of state being inaccessible outside is to ensure state manipulations are handled by the StateNotifier itself, so using a getter in the meantime wouldn't be the end of the world.
@AlexHartford interesting, i will try it .
Not necessarily a good practice, as the value may change over time.
You may want a StreamProvider for that reason too
@rrousselGit based on my above code , what's the problem with my code ?
If "t" changes,this will not print the new value
@rrousselGit I think best option it's waiting you implement what you mention on it