Kulpas
Kulpas

Reputation: 394

Flutter riverpod: How to call Navigator.push() inside ref.listen()?

I'm working on a login screen for my app and I'm using a riverpod provider for the user auth state:

// Auth state union class
@freezed
class AuthState with _$AuthState {
  const factory AuthState.initial() = _Initial;

  const factory AuthState.loading() = _Loading;

  const factory AuthState.unauthenticated({required String message}) = _UnAuthentication;

  const factory AuthState.authenticated({required User user}) = _Authenticated;
}

// Auth state provider:
@Riverpod(keepAlive: true)
class AuthStateNotifer extends _$AuthStateNotifer {
  @override
  AuthState build() {
    return const AuthState.initial();
  }

  Future<void> login({required String email, required String password}) async {
    state = const AuthState.loading();
    final response = await ref.watch(authServiceProvider).login(email: email, password: password);

    state = response.fold(
      (error) => AuthState.unauthenticated(message: error),
      (user) => AuthState.authenticated(user: user),
    );
  }

  Future<void> signup(RegistrationData registrationData) async {
    state = const AuthState.loading();
    final response = await ref.watch(authServiceProvider).signup(registrationData);
    state = response.fold(
      (error) => AuthState.unauthenticated(message: error),
      (user) => AuthState.authenticated(user: user),
    );
  }

  void checkIfLoggedIn() {
    final response = ref.watch(authServiceProvider).checkIfLoggedIn();
    state = response.fold(
      () => const AuthState.unauthenticated(message: "User not logged in"),
      (user) => AuthState.authenticated(user: user),
    );
  }

  Future<void> signout() async {
    await ref.watch(authServiceProvider).signout();
    Future.delayed(const Duration(milliseconds: 500), () {
      ref.invalidateSelf();
    });
  }
}

Inside my login page I have a listener that listens for the auth state change and is supposed to go to home page when user is logged in.

 @override
  Widget build(BuildContext context) {
    ref.listen(
          authStateNotiferProvider,
          (previous, next) {
            next.maybeWhen(
              orElse: () => null,
              authenticated: (user) => 
    Navigator.of(context).pushReplacementNamed(HomePage.route),
              unauthenticated: (message) {
                showError(message);
              },
            );
          },
        );
///...

Unfortunately, this makes it so navigator gets called on every widget rebuild making it keep pushing the home page forever. How do I resolve this?

Upvotes: 0

Views: 830

Answers (1)

user14193305
user14193305

Reputation: 21

issueis due to the fact that you're calling Navigator.of(context).pushReplacementNamed(HomePage.route) inside the build the correct code:

class YourLoginPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final authState = watch(authStateNotiferProvider);

    // Use a flag to track whether navigation has already occurred
    bool hasNavigated = false;

    // Listen for auth state changes
    authState.maybeWhen(
      orElse: () {},
      authenticated: (user) {
        // Check if navigation has already occurred
        if (!hasNavigated) {
          // Navigate to the home page
          Navigator.of(context).pushReplacementNamed(HomePage.route);
          hasNavigated = true; // Set the flag to true
        }
      },
      unauthenticated: (message) {
        showError(message);
      },
    );

    // Your widget build logic 
  

    return YourLoginPageWidget();
  }
}

Amine K

Upvotes: 1

Related Questions