Navigation
DWallet uses AutoRoute for declarative, type-safe routing in Flutter.
Why AutoRoute?
Section titled “Why AutoRoute?”- Type-safe: No more string-based route names
- Code generation: Routes are generated automatically
- Deep linking: Built-in support for URL-based navigation
- Guards: Protect routes with authentication checks
- Nested navigation: Support for complex routing scenarios
Configuration
Section titled “Configuration”Routes are defined in lib/app/router/_app_router.dart:
@AutoRouterConfig()class DAppRouter extends RootStackRouter { @override List<AutoRoute> get routes => [ // Onboarding flow (no guard) AutoRoute( page: OnboardRoute.page, path: '/onboard', children: [ AutoRoute(page: WelcomeRoute.page, path: ''), AutoRoute(page: CurrencySelectorRoute.page, path: 'currency-selector'), AutoRoute(page: AddInitialBalanceRoute.page, path: 'add-initial-balance'), ], ),
// Main app (guarded) AutoRoute( page: ClientRoute.page, path: '/client', guards: [OnboardRouteGuard], children: [ AutoRoute( page: BottomNavRoute.page, path: '', children: [ AutoRoute(page: HomeRoute.page, path: 'home'), AutoRoute(page: WalletListRoute.page, path: 'wallet-list'), AutoRoute(page: AnalyticsRoute.page, path: 'analytics'), AutoRoute(page: SettingsRoute.page, path: 'settings'), ], ), AutoRoute(page: AppearanceRoute.page, path: 'appearance'), AutoRoute(page: AppLockSecurityRoute.page, path: 'app-lock-security'), AutoRoute(page: AllTransactionListRoute.page, path: 'all-transaction-list'), AutoRoute(page: WalletDetailsRoute.page, path: 'wallet-details'), ], ), ];}Route Guards
Section titled “Route Guards”Protect routes with guards:
class OnboardRouteGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) async { final container = ProviderScope.containerOf(resolver.context); final authService = container.read(authUserServiceProvider);
// Check if user has completed onboarding final hasCompletedOnboarding = authService.value?.hasCompletedOnboarding ?? false;
if (hasCompletedOnboarding) { resolver.next(true); // Allow navigation } else { resolver.redirect(const OnboardRoute()); // Redirect to onboarding } }}Navigation Patterns
Section titled “Navigation Patterns”Basic Navigation
Section titled “Basic Navigation”// Push a new routecontext.router.push(const HomeRoute());
// Replace current routecontext.router.replace(const HomeRoute());
// Pop current routecontext.router.pop();
// Pop until specific routecontext.router.popUntil((route) => route.settings.name == HomeRoute.name);With Parameters
Section titled “With Parameters”// Define route with parameters@RoutePage()class WalletDetailsPage extends StatelessWidget { final String walletId;
const WalletDetailsPage({ required this.walletId, super.key, });}
// Navigate with parameterscontext.router.push(WalletDetailsRoute(walletId: '123'));Nested Navigation
Section titled “Nested Navigation”// Access child routerfinal tabsRouter = AutoTabsRouter.of(context);
// Switch tabstabsRouter.setActiveIndex(2); // Switch to 3rd tabWith Riverpod
Section titled “With Riverpod”class SomePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final router = ref.watch(dAppRouterProvider);
return ElevatedButton( onPressed: () => router.push(const HomeRoute()), child: Text('Go Home'), ); }}Route Wrappers
Section titled “Route Wrappers”Use wrapper pages for shared UI:
// Auth wrapper with custom transitions@RoutePage()class AuthWrapperPage extends StatelessWidget { const AuthWrapperPage({super.key});
@override Widget build(BuildContext context) { return Scaffold( body: AutoRouter(), // Child routes render here ); }}Deep Linking
Section titled “Deep Linking”AutoRoute supports deep linking out of the box:
// In AndroidManifest.xml<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="dwallet" android:host="wallet" /></intent-filter>Best Practices
Section titled “Best Practices”- Use const constructors for routes when possible
- Keep route definitions centralized in one file
- Use guards for authentication/protected routes
- Use nested routes for tab-based navigation
- Run build_runner after adding new routes:
Terminal window flutter pub run build_runner build --delete-conflicting-outputs
Common Patterns
Section titled “Common Patterns”Bottom Navigation
Section titled “Bottom Navigation”AutoTabsRouter( routes: const [ HomeRoute(), WalletListRoute(), AnalyticsRoute(), SettingsRoute(), ], builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return Scaffold( body: child, bottomNavigationBar: BottomNavigationBar( currentIndex: tabsRouter.activeIndex, onTap: tabsRouter.setActiveIndex, items: [...], ), ); },)Modal Routes
Section titled “Modal Routes”// Bottom sheetshowModalBottomSheet( context: context, builder: (_) => const ManageTransactionModal(),);
// Full screen modalcontext.router.pushModal(const SomeRoute());