AutoRoute is a route generation library, where everything needed for navigation is automatically generated for you.
- Installation
- Setup and Usage
- Navigation
- Customization
- Passing Arguments to Routes
- Nested Navigators
- Route guards
- Handling Wrapped Routes
- Custom Route Transitions
dependencies:
# add auto_route to your dependencies
auto_route: [latest-version]
dev_dependencies:
# add the generator to your dev_dependencies
auto_route_generator: [latest-version]
# of course build_runner is needed to run the generator
build_runner:
First create a router config class then annotate it with @MaterialAutoRouter, @CupertinoAutoRoute or @CustomAutoRoute. It's name must be prefixed with $ to get a generated class with the same name minus the $.
$Router => Router
@MaterialAutoRouter(...config) //CustomAutoRoute(..config)
class $Router {
}
Only use the @MaterialRoute() or @CupertinoRoute() annotations to customize your route
@MaterialAutoRouter()
class $Router {
// use @initial or @CupertinoRoute(initial: true) to annotate your initial route.
@initial
HomeScreen homeScreenRoute; // your desired route name
SecondScreen secondScreenRoute;
//optional route Customization
@CupertinoRoute(fullscreenDialog: true)
LoginScreen loginScreenRoute;
}
Use the [watch] flag to watch the files system for edits and rebuild as necessary.
flutter packages pub run build_runner watch
if you want the generator to run one time and exits use
flutter packages pub run build_runner build
after you run the generator your router class will be generated containing all of your route names and the onGenerateRoute function implementation.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Tell MaterialApp to use our ExtendedNavigator instead of
// the native one by assigning it to it's builder
// instead of return the nativeNavigator we're returning our ExtendedNavigator
builder: ExtendedNavigator<Router>(router: Router()),
// ExtendedNavigator is just a widget so you can still wrap it with other widgets
builder: (ctx, nativeNavigator) => Theme(data:...,
child: ExtendedNavigator<Router>(router: Router())
,)
);
}
}
// a Routes class that holds all of your static route names
abstract class Routes {
static const homeScreen = '/';
static const secondScreen = '/second-screen';
}
// the onGenerateRoute function implementation
class Router extends RouterBase{
@override
Route<dynamic> onGenerateRoute(RouteSettings settings) {
switch (settings.name) {
// The generated code for LoginScreen is
case Routes.loginScreenRoute:
return CupertinoPageRoute<dynamic>(
builder: (_) => LoginScreen(),
settings: settings,
fullscreenDialog: true
);
default:
// autoRoute handles unknown routes for you
return unknownRoutePage(settings.name);
}
} }
// Argument holder classes if exist ...
You can either use context to look up your Navigator in your widgets tree or without context, using ExtendedNavigator.ofRouter()
// with context
ExtendedNavigator.of(context).pushNamed(Routes.secondScreen)
// without context
ExtendedNavigator.ofRouter<Router>().pushNamed(Routes.secondScreen)
// or if you're working with only one navigator
ExtendedNavigator.rootNavigator.pushNamed(..)
to generate extension methods set the generateNavigationHelperExtension property inside of MaterialAutoRouter() to true
This will generate
extension RouterNavigationHelperMethods on ExtendedNavigatorState {
Future pushHomeScreen() => pushNamed(Routes.homeScreen);
Future<bool> pushSecondScreen(
{@required String title, String message}) =>
pushNamed<bool>(Routes.secondScreen,
arguments: SecondScreenArguments(title: title, message: message));
}
Then use it like follows
ExtendedNavigator.of(context).pushSecondScreen(args...);
//or
ExtendedNavigator.ofRouter<Router>().pushSecondScreen(args...)
Property | Default value | Definition |
---|---|---|
generateRouteList [bool] | false | if true a list of all routes will be generated |
generateNavigationHelperExtension [bool] | false | if true a Navigator extenstion will be generated with helper push methods of all routes |
generateArgsHolderForSingleParameterRoutes [bool] | true | if true argument holder classes will always be generated for routes with parameters |
useLeadingSlashes [bool] | true | if true route names will be prefixed with '/' |
routesClassName [string] | 'Routes' | the name of the generated Routes class |
Property | Default value | Definition |
---|---|---|
transitionsBuilder [Function] | null | extension for the transitionsBuilder property in PageRouteBuilder |
opaque [bool] | true | extension for the opaque property in PageRouteBuilder |
barrierDismissible [bool] | false | extension for the barrierDismissible property in PageRouteBuilder |
durationInMilliseconds [double] | null | extension for the transitionDuration(millieSeconds) property in PageRouteBuilder |
Property | Default value | Definition |
---|---|---|
initial [bool] | false | mark the route as initial '\' |
name [String] | null | this will be assigned to the route variable name if provided (String homeScreen = [name]); |
fullscreenDialog [bool] | false | extension for the fullscreenDialog property in PageRoute |
maintainState [bool] | true | extension for the maintainState property in PageRoute |
Property | Default value | Definition |
---|---|---|
title [String] | null | extension for the title property in CupertinoPageRoute |
Property | Default value | Definition |
---|---|---|
transitionsBuilder [Function] | null | extension for the transitionsBuilder property in PageRouteBuilder |
opaque [bool] | true | extension for the opaque property in PageRouteBuilder |
barrierDismissible [bool] | false | extension for the barrierDismissible property in PageRouteBuilder |
durationInMilliseconds [double] | null | extension for the transitionDuration(millieSeconds) property in PageRouteBuilder |
Marks route as a custome route-not-found page. There can be only one unknown route per Router.
You don't actually need to do anything extra. AutoRoute automatically detects your route parameters and handles them for you, it will automatically generate a class that holds your screen arguments and keep them typed.
class WelcomeScreen extends StatelessWidget {
final String title;
final String message;
const WelcomeScreen({this.title = "Default Title",@required this.message});
@override
Widget build(BuildContext context)...
}
- Default values are respected.
- Required fields are also respected and handled properly.
//WelcomeScreen arguments holder class is generated
class WelcomeScreenArguments {
final String title;
final String message;
// you're not going to lose your default values;
WelcomeScreenArguments({this.title = "Default Title",@required this.message});
}
case Routes.welcomeScreenRoute:
// if your class holder contains at least one required field the whole argument class is considered required and can not be null
if (hasInvalidArgs<WelcomeScreenArguments>(args,isRequired: true))
return misTypedArgsRoute<WelcomeScreenArguments>(args);
final typedArgs =
args as WelcomeScreenArguments ?? WelcomeScreenArguments();
return MaterialPageRoute<dynamic>(
builder: (_) =>
WelcomeScreen(title: typedArgs.title, message: typedArgs.message),
settings: settings,
);
ExtendedNavigator.of(ctx).pushNamed(Router.welcomeScreenRoute,
arguments: WelcomeScreenArguments(
title: "Hello World!"
message: "Let's AutoRoute!"
)
);
Note: if you don't want auto_route to generate arguments holder class for single parameter routes set generateArgsHolderForSingleParameterRoutes to false in AutoRouter()
Create your nested router class and define your routes as before.
@MaterialAutoRouter()
class $MyNestedRouter {
@initial
NestedHomePage nestedHomePage;
NestedSecondPage nestedSecondPage;
}
ExtendedNavigator<MyNestedRouter>(router: MyNestedRouter()),
And That's that! Now use your nested router's navigator to navigate within your nested navigator as follows
// inside of widgets below your nested ExtendedNavigator()
ExtendedNavigator.of(context).pushNamed(...)
// or without context
ExtendedNavigator.ofRouter<MyNestedRouter>.pushNamed(...)
Implementing route guards requires a little bit of setup:
- Create your route guard by extending RouteGuard from the autoRoute package
class AuthGuard extends RouteGuard {
@override
Future<bool> canNavigate(
ExtendedNavigatorState navigator, String routeName, Object arguments) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('token_key') != null;
}
}
- Register your guards pass your guards to the guards property inside of ExtendedNavigator
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: ExtendedNavigator<Router>(
router: Router(),
guards: [AuthGuard()],
),
);
}
}
- Annotated the routes you want to guard with @GuardedBy([..guards]) and pass in your guards as types.
@GuardedBy([AuthGuard])
ProfileScreen profileScreen;
To wrap your route with a parent widget like a Provider or such, simply implement AutoRouteWrapper, and let wrappedRoute(context) method return (this) as the child of your wrapper widget.
class ProductsScreen extends StatelessWidget implements AutoRouteWrapper {
@override
Widget wrappedRoute(BuildContext context) => Provider(create: (ctx) => ProductsBloc(), child: this);
...
}
To use custom Transitions use the @CustomRoute() annotation and pass in your preferences.
The TransitionsBuilder function needs to be passed as a static/const reference that has the same signature as the TransitionsBuilder Function of the PageRouteBuilder class.
The included TransitionsBuilders Class contains a preset of common Transitions builders
@CustomRoute(transitionsBuilder: TransitionBuilders.slideBottom,durationInMilliseconds: 400)
LoginScreen loginScreenRoute;
Use @CustomAutoRouter() to define global custom route Transitions.
You can of course use your own transitionsBuilder function as long as it has the same function signature.
The function has to take in exactly a BuildContext, Animation[Double], Animation[Double] and a child Widget and it needs to return a Widget, typically you would wrap your child with one of flutter's transition Widgets as follows.
Widget zoomInTransition(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
// you get an animation object and a widget
// make your own transition
return ScaleTransition(scale: animation, child: child);
}
Now pass the reference of your function to @CustomRoute() annotation.
@CustomRoute(transitionsBuilder: zoomInTransition)
ZoomInScreen zoomInScreenRoute {}
Thanks to Peter Leibiger for his valuable advice.
Make sure you always Save your files before running the generator, if that doesn't work you can always try to clean and rebuild.
flutter packages pub run build_runner clean