Reputation: 520
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.
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...)
Upvotes: 1
Views: 428
Reputation: 181
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;
Consumer(
builder: (context, ref, child) {
ref.listen(
internetConnectionResultProvider,
(_, state) async {
LogHelper.shared.debugPrint(state.name);
switch (state) {
case ConnectivityStatus.notDetermined:
// You logic goes here
case ConnectivityStatus.connected:
// You logic goes here
case ConnectivityStatus.disonnected:
// You logic goes here
break;
}
},
);
return SomeRelatedWidget();
},
);
Give me a feedback after you try. Cheers
Upvotes: 0