I've just started learning flutter weeks back. I'm building a project with Go router and Bloc & get_it for dependency injection & also making use of flutter_native_splash for my splash screen , I'm trying to solve my authentication navigation
I have 3 states - Authenticated, Unauthenticated, FirstTime (Will see onboarding screens)
This is my current implementation, the state released is always Unauthenticated, whenever I open the app. Ideally the first time it should be FirstTime
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => serviceLocator<AuthBloc>()..add(AppStarted()),
child: MaterialApp.router(
title: "Fittro",
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
routerDelegate: _router.routerDelegate,
routeInformationParser: _router.routeInformationParser,
routeInformationProvider: _router.routeInformationProvider,
),
);
}
}
final GoRouter _router = GoRouter(
redirect: (context, state) {
// Assuming your AuthBloc is accessible here and provides the current state
final authState = serviceLocator<AuthBloc>().state;
debugPrint(authState.toString());
// Determine if the current state is FirstTime, Unauthenticated, or Authenticated
if (authState is FirstTime) {
// Redirect to onboarding for FirstTime state
return '/onboarding';
} else if (authState is Unauthenticated) {
// Redirect to login if unauthenticated and not on login or onboarding page
return '/login';
} else if (authState is Authenticated) {
return '/home';
}
// No redirection needed
return null;
},
routes: <RouteBase>[
GoRoute(
name: "home",
path: "/home",
builder: (context, state) => const HomePage(),
),
GoRoute(
name: "onboarding",
path: "/onboarding",
builder: (context, state) => const OnboardingView(),
),
GoRoute(
name: "login",
path: "/login",
builder: (context, state) => const LoginScreen(),
),
],
);
If I don't use the GoRouter then things work fine i.e my main widget looks like this
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => serviceLocator<AuthBloc>()..add(AppStarted()),
child: MaterialApp(
title: "Fittro",
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {},
builder: (context, state) {
if (state is Authenticated) {
return const HomePage();
} else if (state is Unauthenticated) {
return const LoginScreen();
} else {
return const OnboardingView();
}
},
)),
);
}
My auth_bloc.dart
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final LoginUser _loginUserUseCase;
AuthBloc({required LoginUser loginUserUseCase})
: _loginUserUseCase = loginUserUseCase,
super(Unauthenticated()) {
on<AppStarted>(_onAppStarted);
on<LoggedIn>(_onLoggedIn);
on<LoggedOut>(_onLoggedOut);
}
void _onAppStarted(AppStarted event, Emitter<AuthState> emit) async {
final prefs = await SharedPreferences.getInstance();
final isFirstTime = prefs.getBool('onboarding') ?? true;
final token = prefs.getString("access_token") ?? "";
if (isFirstTime) {
emit(FirstTime());
} else {
// Check login status and emit either Authenticated or Unauthenticated
if (token != "") {
emit(Authenticated());
} else {
emit(Unauthenticated());
}
}
}
void _onLoggedIn(LoggedIn event, Emitter<AuthState> emit) async {
final prefs = await SharedPreferences.getInstance();
final email = event.email;
final password = event.password;
final token = await _loginUserUseCase.call(
LoginParams(
email: email,
password: password,
),
);
token.fold((l) => debugPrint('error'), (tk) {
prefs.setString("token", tk);
emit(Authenticated());
});
}
void _onLoggedOut(LoggedOut event, Emitter<AuthState> emit) async {
final prefs = await SharedPreferences.getInstance();
prefs.clear();
emit(Unauthenticated());
}
}