App Router Documentation
Complete navigation setup for the warehouse management application using GoRouter.
Overview
The app router implements authentication-based navigation with proper redirect logic:
- Unauthenticated users are redirected to
/login - Authenticated users on
/loginare redirected to/warehouses - Type-safe parameter passing between routes
- Integration with SecureStorage for authentication checks
App Flow
Login → Warehouses → Operations → Products
- Login: User authenticates and token is stored
- Warehouses: User selects a warehouse
- Operations: User chooses Import or Export
- Products: Display products based on warehouse and operation
Routes
/login - Login Page
- Name:
login - Purpose: User authentication
- Parameters: None
- Redirect: If authenticated →
/warehouses
/warehouses - Warehouse Selection Page
- Name:
warehouses - Purpose: Display list of warehouses
- Parameters: None
- Protected: Requires authentication
/operations - Operation Selection Page
- Name:
operations - Purpose: Choose Import or Export operation
- Parameters:
extra:WarehouseEntityobject
- Protected: Requires authentication
- Validation: Redirects to
/warehousesif warehouse data is missing
/products - Products List Page
- Name:
products - Purpose: Display products for warehouse and operation
- Parameters:
extra:Map<String, dynamic>containing:warehouse:WarehouseEntityobjectwarehouseName:StringoperationType:String('import' or 'export')
- Protected: Requires authentication
- Validation: Redirects to
/warehousesif parameters are invalid
Usage Examples
Basic Navigation
import 'package:go_router/go_router.dart';
// Navigate to login
context.go('/login');
// Navigate to warehouses
context.go('/warehouses');
Navigation with Extension Methods
import 'package:minhthu/core/router/app_router.dart';
// Navigate to login
context.goToLogin();
// Navigate to warehouses
context.goToWarehouses();
// Navigate to operations with warehouse
context.goToOperations(warehouse);
// Navigate to products with warehouse and operation type
context.goToProducts(
warehouse: warehouse,
operationType: 'import',
);
// Go back
context.goBack();
Named Route Navigation
// Using named routes
context.goToLoginNamed();
context.goToWarehousesNamed();
context.goToOperationsNamed(warehouse);
context.goToProductsNamed(
warehouse: warehouse,
operationType: 'export',
);
Integration with Warehouse Selection
Example: Navigate from Warehouse to Operations
import 'package:flutter/material.dart';
import 'package:minhthu/core/router/app_router.dart';
import 'package:minhthu/features/warehouse/domain/entities/warehouse_entity.dart';
class WarehouseCard extends StatelessWidget {
final WarehouseEntity warehouse;
const WarehouseCard({required this.warehouse});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(warehouse.name),
subtitle: Text('Code: ${warehouse.code}'),
trailing: Icon(Icons.arrow_forward),
onTap: () {
// Navigate to operations page
context.goToOperations(warehouse);
},
),
);
}
}
Example: Navigate from Operations to Products
import 'package:flutter/material.dart';
import 'package:minhthu/core/router/app_router.dart';
import 'package:minhthu/features/warehouse/domain/entities/warehouse_entity.dart';
class OperationButton extends StatelessWidget {
final WarehouseEntity warehouse;
final String operationType;
const OperationButton({
required this.warehouse,
required this.operationType,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// Navigate to products page
context.goToProducts(
warehouse: warehouse,
operationType: operationType,
);
},
child: Text(operationType == 'import'
? 'Import Products'
: 'Export Products'),
);
}
}
Authentication Integration
The router automatically checks authentication status on every navigation:
// In app_router.dart
Future<String?> _handleRedirect(
BuildContext context,
GoRouterState state,
) async {
// Check if user has access token
final isAuthenticated = await secureStorage.isAuthenticated();
final isOnLoginPage = state.matchedLocation == '/login';
// Redirect logic
if (!isAuthenticated && !isOnLoginPage) {
return '/login'; // Redirect to login
}
if (isAuthenticated && isOnLoginPage) {
return '/warehouses'; // Redirect to warehouses
}
return null; // Allow navigation
}
SecureStorage Integration
The router uses SecureStorage to check authentication:
// Check if authenticated
final isAuthenticated = await secureStorage.isAuthenticated();
// This checks if access token exists
Future<bool> isAuthenticated() async {
final token = await getAccessToken();
return token != null && token.isNotEmpty;
}
Reactive Navigation
The router automatically reacts to authentication state changes:
class GoRouterRefreshStream extends ChangeNotifier {
final Ref ref;
GoRouterRefreshStream(this.ref) {
// Listen to auth state changes
ref.listen(
authProvider, // From auth_dependency_injection.dart
(_, __) => notifyListeners(),
);
}
}
When authentication state changes (login/logout), the router:
- Receives notification
- Re-evaluates redirect logic
- Automatically redirects to appropriate page
Error Handling
Missing Parameters
If route parameters are missing, the user is redirected:
GoRoute(
path: '/operations',
builder: (context, state) {
final warehouse = state.extra as WarehouseEntity?;
if (warehouse == null) {
// Show error and redirect
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go('/warehouses');
});
return const _ErrorScreen(
message: 'Warehouse data is required',
);
}
return OperationSelectionPage(warehouse: warehouse);
},
),
Page Not Found
Custom 404 error page:
errorBuilder: (context, state) {
return Scaffold(
appBar: AppBar(title: const Text('Page Not Found')),
body: Center(
child: Column(
children: [
Icon(Icons.error_outline, size: 64),
Text('Page "${state.uri.path}" does not exist'),
ElevatedButton(
onPressed: () => context.go('/login'),
child: const Text('Go to Login'),
),
],
),
),
);
}
Setup in main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:minhthu/core/router/app_router.dart';
import 'package:minhthu/core/theme/app_theme.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// Get router from provider
final router = ref.watch(appRouterProvider);
return MaterialApp.router(
title: 'Warehouse Manager',
theme: AppTheme.lightTheme,
routerConfig: router,
);
}
}
Best Practices
1. Use Extension Methods
Prefer extension methods for type-safe navigation:
// Good
context.goToProducts(warehouse: warehouse, operationType: 'import');
// Avoid
context.go('/products', extra: {'warehouse': warehouse, 'operationType': 'import'});
2. Validate Parameters
Always validate route parameters:
final warehouse = state.extra as WarehouseEntity?;
if (warehouse == null) {
// Handle error
}
3. Handle Async Operations
Use post-frame callbacks for navigation in builders:
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go('/warehouses');
});
4. Logout Implementation
Clear storage and let router handle redirect:
Future<void> logout() async {
await ref.read(authProvider.notifier).logout();
// Router will automatically redirect to /login
}
Troubleshooting
Issue: Redirect loop
Cause: Authentication check is not working properly Solution: Verify SecureStorage has access token
Issue: Parameters are null
Cause: Wrong parameter passing format Solution: Use extension methods with correct types
Issue: Navigation doesn't update
Cause: Auth state changes not triggering refresh Solution: Verify GoRouterRefreshStream is listening to authProvider
Related Files
/lib/core/router/app_router.dart- Main router configuration/lib/core/storage/secure_storage.dart- Authentication storage/lib/features/auth/di/auth_dependency_injection.dart- Auth providers/lib/features/auth/presentation/pages/login_page.dart- Login page/lib/features/warehouse/presentation/pages/warehouse_selection_page.dart- Warehouse page/lib/features/operation/presentation/pages/operation_selection_page.dart- Operation page/lib/features/products/presentation/pages/products_page.dart- Products page