There is a Future/Provider that call an API and then return some data
@riverpod
Future<UserModel> updateProfile(
UpdateProfileRef ref, {
required String? firstName,
required String? lastName,
required String? birthDate,
}) async {
ref.keepAliveUntilNoListeners();
final repository = ref.read(profileRepositoryProvider);
final result = await repository.updateUserProfile(
UpdateProfileInputModel(
data: InputProfileInputUpdate(
birthDate: birthDate,
firstName: firstName,
lastName: lastName,
),
),
);
return result.fold(
Future.error,
(r) => r,
);
}
And this is the button that watch to this provider
Consumer(
builder: (context, ref, child) {
return LoadingButton(
onPressed: () {
if (formKey.currentState!.validate()) {
ref
.watch(
updateProfileProvider(
firstName: firstNameController.text,
lastName: lastNameController.text,
birthDate: dateValue,
),
)
.when(
data: (data) {
context.pop();
},
error: (error, stackTrace) => print,
loading: () => log('loading'),
);
}
},
height: 48.h,
title: l10n.saveEditedInfo,
);
},
),
This implementation has issue and the app doesn't navigate.
I have some question about it ...
Where is the correct place, and where should I listen to changes and show some toast for errors or navigate for success futures?
I know that there is a ref.listen method, but it calls the future and I can't use it in build method, because I want to call future when user presses the button.
New update
I changed the provider to this:
@riverpod
Future<UserModel> updateProfile(
UpdateProfileRef ref, {
required String? firstName,
required String? lastName,
required String? birthDate,
}) async {
final repository = ref.read(profileRepositoryProvider);
final result = await repository.updateUserProfile(
UpdateProfileInputModel(
data: InputProfileInputUpdate(
birthDate: birthDate,
firstName: firstName,
lastName: lastName,
),
),
);
return result;
}
Also use it in the UI like this :
Consumer(
builder: (context, ref, child) {
return LoadingButton(
onPressed: () async {
if (formKey.currentState!.validate()) {
changeLoadingValue(state: true);
await ref
.read(
updateProfileProvider(
firstName: firstNameController.text,
lastName: lastNameController.text,
birthDate: dateValue,
).future,
)
.then(
(value) {
ref.invalidate(homeWhoAmIProvider);
context.go(HomeScreen.path);
},
).catchError((e) {
changeLoadingValue(state: false);
showToast(context, title: e.toString());
});
}
},
isLoading: buttonLoading,
height: 48.h,
title: l10n.saveEditedInfo,
);
},
),
Actually I am waiting for the result or error of the future then navigate or handle the error. Now it works, but I don't know is it normal to handle this case like this or not.(I am looking for a better approach to handle navigation or error handling...)
I'm happy that you solved it like that. However, you can also use listener inside Consumer or ConsumerWidget, that Riverpod provides you
For instance;
Give me a feedback after you try. Cheers