What are Navigation Guards?
Navigation guards are mechanisms that control access to certain routes based on conditions like authentication status, user roles, or feature flags. They prevent unauthorized users from accessing protected screens and redirect them to login or permission-denied pages. In BLoC architecture, navigation guards are implemented by observing the auth state and conditionally navigating or using route observers to intercept navigation attempts.
Why Use BLoC for Navigation Guards?
- Centralized Auth State – BLoC holds the single source of truth for authentication.
- Reactive Navigation – Guards automatically respond to state changes (e.g., logout redirects to login).
- Separation of Concerns – Navigation logic stays in the UI layer; BLoC only manages state.
- Testability – Auth state changes can be tested independently of navigation.
- Consistency – Ensure all routes are protected using the same auth state.
Setup: Authentication BLoC
First, create an authentication BLoC that manages the user's auth state. This will be the source for all navigation decisions.
Navigation Guard with BlocListener (Imperative)
Place a BlocListener at the top of your widget tree to listen to auth state changes and redirect accordingly. This works with both Navigator 1.0 and 2.0.
Protecting Individual Routes
For individual routes that should be guarded, you can check the auth state before navigating or use a custom RouteGuard widget that wraps the route content.
Integration with GoRouter (Declarative Navigation)
GoRouter is a modern routing package that works seamlessly with BLoC. You can define a redirect function that reads the BLoC state and determines the appropriate route.
Role-Based Guards (Authorization)
You can extend guards to check user roles. For example, only admin users can access an admin panel.
Best Practices
- Use a single source of truth – Auth state should be managed in a single BLoC (or multiple if needed) and accessed via providers.
- Redirect early – In GoRouter, the
redirectfunction runs before building any route, preventing flicker. - Avoid side effects in builders – Use
BlocListeneroraddPostFrameCallbackfor navigation to avoid calling during build. - Test guards – Verify that unauthenticated users cannot access protected routes in widget tests.
- Combine with
hydrated_bloc– Persist auth state across app restarts to maintain login. - Show loading states – While checking auth, display a splash screen to prevent UI jumps.
Common Mistakes
- ❌ Calling
Navigator.pushinside abuildmethod – This will run multiple times, causing navigation loops. UseBlocListeneror a post-frame callback. - ❌ Not handling
unknownauth state – App may try to navigate before auth state is resolved, causing redirect loops. - ❌ Hardcoding routes in the BLoC – BLoC should not know about navigation; keep navigation logic in the UI layer.
- ❌ Forgetting to dispose of subscriptions – If using a stream to listen to auth changes, ensure it's closed.
- ❌ Not testing guard behavior – Always write tests to ensure protected routes redirect correctly.
Conclusion
Navigation guards are essential for securing your Flutter app. By combining BLoC's auth state with tools like BlocListener or GoRouter's redirect, you can implement robust authentication and authorization flows. These patterns keep your code maintainable and your app secure.