Mahmoud
Mahmoud

Reputation: 520

Navigation after a future when using riverpod

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...)

Upvotes: 1

Views: 428

Answers (1)

Armagan GOK
Armagan GOK

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

Related Questions