Question #97MediumFlutter Basics

Flutter auto route plugin

#flutter#route

Answer

Overview

AutoRoute is a code-generation package for Flutter navigation with type-safe routes, nested routing, deep linking, and guards. It's an alternative to Navigator 2.0 with less boilerplate.


Installation

Add to

text
pubspec.yaml
:

yaml
dependencies:
  auto_route: ^7.8.0

dev_dependencies:
  auto_route_generator: ^7.3.0
  build_runner: ^2.4.0

Setup

1. Define Routes

lib/app_router.dart:

dart
import 'package:auto_route/auto_route.dart';

()
class AppRouter extends $AppRouter {
  
  List<AutoRoute> get routes => [
    AutoRoute(page: HomeRoute.page, initial: true),
    AutoRoute(page: ProfileRoute.page),
    AutoRoute(page: SettingsRoute.page),
  ];
}

2. Annotate Pages

dart
import 'package:auto_route/auto_route.dart';

()
class HomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            context.router.push(ProfileRoute(userId: '123'));
          },
          child: Text('Go to Profile'),
        ),
      ),
    );
  }
}

3. Generate Code

bash
flutter pub run build_runner build --delete-conflicting-outputs

Generates

text
app_router.gr.dart
with route definitions.

4. Initialize Router in main.dart

dart
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final _appRouter = AppRouter();

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _appRouter.config(),
    );
  }
}

Navigation

Push Route

dart
// Programmatic navigation
context.router.push(ProfileRoute(userId: '123'));

// Named route
context.router.pushNamed('/profile/123');

Replace Route

dart
// Replace current route
context.router.replace(LoginRoute());

Pop Route

dart
// Go back
context.router.pop();

// Pop with result
context.router.pop('success');

Pop Until

dart
// Pop until condition met
context.router.popUntil((route) => route.settings.name == 'HomeRoute');

// Pop to root
context.router.popUntilRoot();

Route Parameters

Path Parameters

dart
()
class AppRouter extends $AppRouter {
  
  List<AutoRoute> get routes => [
    AutoRoute(page: ProfileRoute.page, path: '/profile/:userId'),
  ];
}

()
class ProfilePage extends StatelessWidget {
  final String userId; // Auto-injected from path

  const ProfilePage({required this.userId});

  
  Widget build(BuildContext context) {
    return Text('User ID: $userId');
  }
}

// Navigate
context.router.push(ProfileRoute(userId: '123')); // /profile/123

Query Parameters

dart
()
class SearchPage extends StatelessWidget {
  final String? query;
  final int? page;

  const SearchPage({this.query, this.page});

  
  Widget build(BuildContext context) {
    return Text('Query: $query, Page: $page');
  }
}

// Navigate with query params
context.router.pushNamed('/search?query=flutter&page=2');

Nested Navigation (Tabs)

dart
()
class DashboardPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return AutoTabsRouter(
      routes: [
        HomeRoute(),
        ProfileRoute(),
        SettingsRoute(),
      ],
      builder: (context, child) {
        final tabsRouter = AutoTabsRouter.of(context);

        return Scaffold(
          body: child,
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: tabsRouter.activeIndex,
            onTap: tabsRouter.setActiveIndex,
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
              BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
              BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
            ],
          ),
        );
      },
    );
  }
}

Guards (Route Protection)

Protect routes with authentication guards.

dart
class AuthGuard extends AutoRouteGuard {
  final AuthService authService;

  AuthGuard(this.authService);

  
  void onNavigation(NavigationResolver resolver, StackRouter router) {
    if (authService.isAuthenticated) {
      resolver.next(true); // Allow navigation
    } else {
      resolver.redirect(LoginRoute()); // Redirect to login
    }
  }
}

// Register guard
()
class AppRouter extends $AppRouter {
  final AuthService authService;

  AppRouter(this.authService);

  
  List<AutoRoute> get routes => [
    AutoRoute(page: LoginRoute.page),
    AutoRoute(
      page: ProfileRoute.page,
      guards: [AuthGuard(authService)], // Protected
    ),
  ];
}

Deep Linking

dart
()
class AppRouter extends $AppRouter {
  
  List<AutoRoute> get routes => [
    AutoRoute(page: HomeRoute.page, path: '/'),
    AutoRoute(page: ProductRoute.page, path: '/product/:id'),
  ];
}

// Deep link: myapp://product/123
// Navigates to ProductPage with id='123'

Route Observers

Track navigation events (analytics, logging).

dart
class MyObserver extends AutoRouterObserver {
  
  void didPush(Route route, Route? previousRoute) {
    print('Pushed: ${route.settings.name}');
  }

  
  void didPop(Route route, Route? previousRoute) {
    print('Popped: ${route.settings.name}');
  }
}

// Register observer
MaterialApp.router(
  routerConfig: _appRouter.config(
    navigatorObservers: () => [MyObserver()],
  ),
);

Redirects

dart
()
class AppRouter extends $AppRouter {
  
  List<AutoRoute> get routes => [
    AutoRoute(page: HomeRoute.page),
    RedirectRoute(path: '/old-path', redirectTo: '/new-path'),
  ];
}

Route Transitions

Custom page transitions.

dart
AutoRoute(
  page: ProfileRoute.page,
  transitionsBuilder: TransitionsBuilders.slideLeft,
  durationInMilliseconds: 300,
)

// Available transitions:
// - slideLeft, slideRight, slideTop, slideBottom
// - fadeIn
// - zoomIn
// - noTransition

Comparison: Navigator 2.0 vs AutoRoute

FeatureNavigator 2.0AutoRoute
BoilerplateHighLow ✅
Type safetyManualAuto-generated ✅
Deep linkingComplex setupBuilt-in ✅
Nested routingManualSimple ✅
GuardsManualBuilt-in ✅
Code generationNoYes

Full Example

lib/pages/home_page.dart:

dart
()
class HomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.router.push(ProfileRoute(userId: '123')),
          child: Text('Go to Profile'),
        ),
      ),
    );
  }
}

lib/pages/profile_page.dart:

dart
()
class ProfilePage extends StatelessWidget {
  final String userId;

  const ProfilePage({('userId') required this.userId});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Profile')),
      body: Center(child: Text('User: $userId')),
    );
  }
}

lib/app_router.dart:

dart
()
class AppRouter extends $AppRouter {
  
  List<AutoRoute> get routes => [
    AutoRoute(page: HomeRoute.page, initial: true),
    AutoRoute(page: ProfileRoute.page, path: '/profile/:userId'),
  ];
}

Build Commands

bash
# Generate routes
flutter pub run build_runner build --delete-conflicting-outputs

# Watch mode
flutter pub run build_runner watch

Best Practices

dart
// ✅ Use path parameters for required data
AutoRoute(page: ProfileRoute.page, path: '/profile/:userId')

// ✅ Use guards for protected routes
AutoRoute(page: DashboardRoute.page, guards: [AuthGuard()])

// ✅ Use nested routing for tabs
AutoTabsRouter(routes: [...])

// ❌ Don't mix AutoRoute with Navigator.push (use one system)

Learn more: AutoRoute Documentation