Navigation is a fundamental aspect of mobile application development. In Flutter, managing routes efficiently and safely is crucial for a smooth user experience. AutoRoute
is a powerful routing library that offers type-safe navigation, reduces boilerplate, and provides a clean, declarative approach to defining routes. This comprehensive guide covers how to effectively utilize AutoRoute
for type-safe routing in Flutter.
What is AutoRoute?
AutoRoute is a declarative, type-safe navigation library for Flutter. It simplifies route management by generating the necessary routing code based on route definitions. AutoRoute focuses on reducing boilerplate, providing compile-time safety, and improving the overall developer experience.
Why Use AutoRoute?
- Type-Safe Navigation: Ensures that navigation calls are type-safe, reducing runtime errors.
- Reduced Boilerplate: Generates routing code, eliminating manual route setup.
- Declarative Approach: Defines routes in a declarative manner, making the routing logic clear and maintainable.
- Deep Linking Support: Simplifies handling deep links for your application.
- Navigation Guards: Allows you to protect routes with guards, controlling access based on specific conditions (e.g., authentication).
How to Implement AutoRoute for Type-Safe Routing
Follow these steps to implement AutoRoute in your Flutter project:
Step 1: Add Dependencies
Add the necessary dependencies to your pubspec.yaml
file:
dependencies:
auto_route: ^7.0.0
dev_dependencies:
auto_route_generator: ^7.0.0
build_runner: ^2.0.0
Explaination:
auto_route
: The core AutoRoute library.auto_route_generator
: Generates the routing code based on your route definitions.build_runner
: A tool for running code generators, including AutoRoute’s generator.
After adding these dependencies, run flutter pub get
to install them.
Step 2: Define Your Routes
Create a file (e.g., app_router.dart
) to define your routes using the @AutoRoute
annotation:
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'pages/home_page.dart';
import 'pages/details_page.dart';
part 'app_router.gr.dart';
@AutoRouterConfig()
class AppRouter extends _$AppRouter {
@override
List<AutoRoute> get routes => [
AutoRoute(page: HomePageRoute.page, initial: true),
AutoRoute(page: DetailsPageRoute.page),
];
}
@RoutePage()
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
child: const Text('Go to Details'),
onPressed: () {
AutoRouter.of(context).push(const DetailsPageRoute());
},
),
),
);
}
}
@RoutePage()
class DetailsPage extends StatelessWidget {
const DetailsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details')),
body: const Center(
child: Text('Details Page'),
),
);
}
}
Step 3: Generate the Routing Code
Run the following command in the terminal to generate the routing code:
flutter pub run build_runner build --delete-conflicting-outputs
This command uses build_runner
to execute the auto_route_generator
, which parses your route definitions and generates the app_router.gr.dart
file.
Step 4: Use the Generated Router
Integrate the generated router into your Flutter application. In your main.dart
file:
import 'package:flutter/material.dart';
import 'app_router.dart'; // Import the file where you defined your AppRouter
// Import the generated router file
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final _appRouter = AppRouter();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _appRouter.config(),
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
);
}
}
Step 5: Navigate Between Routes
Use the generated AutoRouter
to navigate between routes:
ElevatedButton(
child: const Text('Go to Details'),
onPressed: () {
AutoRouter.of(context).push(const DetailsPageRoute());
},
)
Advanced Usage
Passing Parameters
AutoRoute simplifies passing parameters between routes with type safety. To pass parameters to a route, define them in the route’s page class:
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
part 'app_router.gr.dart';
@AutoRouterConfig()
class AppRouter extends _$AppRouter {
@override
List<AutoRoute> get routes => [
AutoRoute(page: HomePageRoute.page, initial: true),
AutoRoute(page: UserPageRoute.page),
];
}
@RoutePage()
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
child: const Text('Go to User Details'),
onPressed: () {
AutoRouter.of(context).push(UserPageRoute(userId: "42"));
},
),
),
);
}
}
@RoutePage()
class UserPage extends StatelessWidget {
const UserPage({Key? key, @PathParam() required this.userId}) : super(key: key);
final String userId;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('User Details')),
body: Center(child: Text("User ID is :$userId")) ,
);
}
}
Here, userId
parameter is marked with @PathParam
Now, from home screen we are passing UserPageRoute(userId: “42”) to navigate to UserPage.
Navigation Guards
Navigation guards allow you to protect routes based on certain conditions. For instance, you can implement an authentication guard to ensure that only authenticated users can access specific routes.
Step 1: Create a Navigation Guard
Implement a navigation guard by extending the AutoRouteGuard
class:
import 'package:auto_route/auto_route.dart';
import 'package:flutter/widgets.dart';
import 'auth_service.dart';
class AuthGuard extends AutoRouteGuard {
final AuthService authService;
AuthGuard(this.authService);
@override
void onNavigation(NavigationResolver resolver, StackRouter router) {
if (authService.isAuthenticated) {
resolver.next(true);
} else {
router.push(const LoginPageRoute());
}
}
}
Step 2: Use the Guard in Route Definition
Attach the navigation guard to a route using the guards
property:
@AutoRoute(
path: '/profile',
page: ProfilePage,
guards: [AuthGuard],
)
class ProfileRoute extends AutoRoute<void, ProfilePage> {
const ProfileRoute() : super(ProfilePage.page);
}
Nested Navigation
AutoRoute supports nested navigation, allowing you to define child routes for a specific route. This is useful for implementing tabbed interfaces or complex navigation patterns within a screen.
Step 1: Define Nested Routes
Define child routes within a parent route using the children
property:
@AutoRoute(
path: '/dashboard',
page: DashboardPage,
children: [
AutoRoute(path: 'home', page: HomeTabPage),
AutoRoute(path: 'settings', page: SettingsTabPage),
],
)
class DashboardRoute extends AutoRoute<void, DashboardPage> {
const DashboardRoute() : super(DashboardPage.page);
}
Step 2: Use the AutoTabsRouter
In your parent route widget, use the AutoTabsRouter
to manage the child routes:
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
class DashboardPage extends StatelessWidget {
const DashboardPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AutoTabsRouter(
routes: const [
HomeTabRoute(),
SettingsTabRoute(),
],
builder: (context, child, animation) {
final tabsRouter = AutoTabsRouter.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Dashboard'),
centerTitle: true,
leading: const AutoLeadingButton(),
),
body: child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
);
},
);
}
}
Conclusion
AutoRoute is an excellent choice for managing navigation in Flutter applications. It offers type-safe routing, reduces boilerplate, and provides advanced features such as deep linking, navigation guards, and nested navigation. By following the steps outlined in this guide, you can effectively utilize AutoRoute to create a robust and maintainable navigation system for your Flutter apps. Whether you’re building a simple application or a complex multi-screen app, AutoRoute simplifies route management and enhances the overall development experience.