Tocklewo
Tocklewo

Reputation: 11

Flutter Guest Access with Bloc and go_router

Trying to implement guest access to my app,

The app flow goes as this: Walkthrough screen (/onboarding) => user can either login/signup/continueAsguest after the first installation as the user return always can access the /home route and some other protected routes need the user to login I want to reuse the same routes after he login he will return the actual protected route

  1. AppRouter class:
class AppRouter {
  late GoRouter _router;
  late GlobalKey<NavigatorState> _rootNavigatorKey;
  late GlobalKey<NavigatorState> _homeNavigatorKey;
  late GlobalKey<NavigatorState> _exploreNavigatorKey;
  late GlobalKey<NavigatorState> _realEstateNavigatorKey;
  late GlobalKey<NavigatorState> _profileNavigatorKey;

  AppRouter() {
    _rootNavigatorKey = GlobalKey<NavigatorState>();
    _homeNavigatorKey = GlobalKey<NavigatorState>();
    _exploreNavigatorKey = GlobalKey<NavigatorState>();
    _realEstateNavigatorKey = GlobalKey<NavigatorState>();
    _profileNavigatorKey = GlobalKey<NavigatorState>();
    _router = GoRouter(
      debugLogDiagnostics: true,
      initialLocation: '/home',
      navigatorKey: _rootNavigatorKey,
      routes: [
        GoRoute(
          name: 'Onboarding',
          path: '/onboarding',
          pageBuilder: (context, state) => const MaterialPage(
            child: OnboardingScreen(),
          ),
        ),
        GoRoute(
          parentNavigatorKey: _rootNavigatorKey,
          name: 'Login',
          path: '/login',
          redirect: (context, state) async {
            final onBoarding = context.read<OnboardingCubit>().state;
            final authState = context.read<AuthBloc>().state;
            if (onBoarding is OnboardingInitial) {
              return '/onboarding';
            } else if (authState is Unauthenticated) {
              return null;
            } else if (authState is Guest || authState is Authenticated) {
              return '/home';
            } else {
              return null;
            }
          },
          pageBuilder: (context, state) => MaterialPage(
            child: LoginScreen(
              showGuestButton: state.extra as bool? ?? true,
            ),
          ),
          routes: [
            GoRoute(
              name: 'ResetPassword',
              path: 'reset-password',
              pageBuilder: (context, state) => const MaterialPage(
                child: ResetPasswordScreen(),
              ),
              routes: [
                GoRoute(
                  name: 'ResetPasswordOtp',
                  path: 'reset-password-otp/:email',
                  pageBuilder: (context, state) {
                    print('state.extra: ${state.extra}');
                    return MaterialPage(
                      child: ResetPasswordOtpScreen(
                        email: state.pathParameters['email'] as String,
                      ),
                    );
                  },
                ),
.....
        // main screen
        StatefulShellRoute.indexedStack(
          parentNavigatorKey: _rootNavigatorKey,
          builder: (context, state, navigationShell) {
            return MainScreen(
              navigationShell: navigationShell,
            );
          },
          pageBuilder: (context, state, navigationShell) => MaterialPage(
            child: MainScreen(
              navigationShell: navigationShell,
            ),
          ),
          redirect: (context, state) {
            final authState = context.read<AuthBloc>().state;
            if (authState is Unauthenticated) {
              return '/login';
            } else {
              return null;
            }
          },
          branches: [
            // home
            StatefulShellBranch(
              navigatorKey: _homeNavigatorKey,
              initialLocation: '/home',
              routes: [
                GoRoute(
                  name: 'Home',
                  path: '/home',
                  pageBuilder: (context, state) => const NoTransitionPage(
                    child: HomeScreen(),
                  ),
                  redirect: (context, state) {
                    final authState = context.read<AuthBloc>().state;
                    final user = context.read<AuthBloc>().user;
                    if (authState is Authenticated && !user!.isValid()) {
                      Logger().i('states is Authenticated');
                      return '/signup/user-infos';
                    } else {
                      return null;
                    }
                  },
                ),
              ],
            ),
            // explore
            StatefulShellBranch(
              navigatorKey: _exploreNavigatorKey,
              initialLocation: '/explore',
              ......
GoRoute(
          name: 'StaysPayment',
          path: '/stays-payment',
          pageBuilder: (context, state) => MaterialPage(
            child: RealestatePaymentScreen(
              property: state.extra as Property,
            ),
          ),

the route I'm checking the auth state is /stays-payment and in MyApp widget

  1. MyApp widget
BlocBuilder<LanguageCubit, LanguageState>(
          builder: (context, state) {
            return BlocListener<AuthBloc, AuthState>(
              listener: (context, state) {
                debugPrint(state.toString());
                sl<AppRouter>().router.refresh();
              },
              child: MaterialApp.router(
  1. AuthBloc
  UserModel? _user;
  final _userController = StreamController<UserModel?>.broadcast();
  AuthBloc({
    required ConfirmInfos confirmInfos,
    required GetUser getUser,
  })  : _confirmInfos = confirmInfos,
        _getUser = getUser,
        super(AuthInitial()) {
    on<OnLoggedIn>((event, emit) {
      _user = event.user as UserModel;
      emit(Authenticated(
          user: event.user,
          credentials: event.credentials,
          currentPath: event.currentPath));
    });
    on<OnLoggedOut>((event, emit) {
      print("OnLoggedOut");
      final sharedPrefs = sl<SharedPreferences>();
      sharedPrefs.remove('accessToken');
      sharedPrefs.remove('refreshToken');
      sharedPrefs.remove('user');
      emit(Guest());
    });

    on<OnAppStarted>((event, emit) async {
      print("OnAppStarted getting user");
      final sharedPrefs = sl<SharedPreferences>();
      final UserModel? user = sharedPrefs.containsKey('user')
          ? UserModel.fromJson(sharedPrefs.getString('user')!)
          : null;
      final AuthCredentials? credentials =
          sharedPrefs.containsKey('accessToken') &&
                  sharedPrefs.containsKey('refreshToken')
              ? AuthCredentials(
                  accessToken: sharedPrefs.getString('accessToken')!,
                  refreshToken: sharedPrefs.getString('refreshToken')!,
                )
              : null;
      if (user != null && credentials != null) {
        _user = user;
        emit(Authenticated(
            user: user, credentials: credentials, currentPath: '/home'));
      } else if (sharedPrefs.containsKey('isGuest') &&
          sharedPrefs.getBool('isGuest')!) {
        emit(Guest());
      } else {
        emit(Unauthenticated());
      }
    });

    on<OnUserUpdated>(_updateUser);

    on<ContinueAsGuest>((event, emit) {
      final sharedPrefs = sl<SharedPreferences>();
      sharedPrefs.setBool('isGuest', true);
      emit(Guest());
    });

    on<LoginAsGuest>((event, emit) {
      emit(AuthInitial());
    });

    on<GetUserEvent>(_fetchUser);
  }

I tried to pass the redirect path as a params as login/?redirect.. didn't work due to "Multiple widgets used the same GlobalKey." error

Upvotes: 1

Views: 18

Answers (0)

Related Questions