One of the most powerful things we can do with route wrappers is to declaratively render our routes. For example, we could link a set of routes to state so that pages are automatically pushed and popped in response to the state - elminating the need to call router.push
or router.pop
altogether! This kind of routing is especially useful for flows (such as a Profile Information flow, or Sign Up flow), and hence may also be called Flow Routing.
@MaterialAutoRouter(
replaceInRouteName: 'Page,Route',
routes: <AutoRoute>[
// our new login routes are defined here!
AutoRoute(
path: "/login",
page: LoginWrapperPage, // we'll get to this LoginWrapperPage next
children: [
AutoRoute(page: EmailPage),
AutoRoute(page: PasswordPage),
]
),
... // our other routes
],
)
class $AppRouter {}
LoginWrapperPage
class LoginWrapperPage extends StatefulWidget {
final Function(bool isLoggedIn) onLogin;
const LoginWrapperPage({Key key, this.onLogin}) : super(key: key);
}
class _LoginWrapperPageState extends State<LoginWrapperPagePage> {
String email = "";
@override
Widget build(context) => AutoRouter.declarative( // use AutoRouter.declarative
routes: (_, __) {
// Declaratively define your routes here
return [
EmailRoute(onNext: (result) {
setState(() {
email: result;
});
}),
if (email.isNotEmpty) PasswordRoute(onNext: (result) async {
try {
// validate the email and password
await validateEmailAndPassword(email, result)
widget.onLogin(true);
} catch (e) {
// do something with the error
}
}),
];
},
);
}
EmailRoute
and PasswordRoute
. And our PasswordRoute
only gets pushed when our email
variable is not empty.Next, there is an onNext
callback in the EmailRoute
which is fired when the user enters their email and taps "next". This causes the email
variable to be assigned to the input email that was just entered by the user.When that happens, the email variable is no longer empty, which causes the PasswordRoute
to get pushed. Note how we didn't need to use router.push
at all!Finally, when the user enters their password and taps "next", this fires the onNext
callback of the PasswordRoute
. Here, we validate the email and password input. If successful, we trigger the onLogin
callback defined in the LoginWrapperPage
. This is important, because the onLogin
callback allows us to use the LoginWrapperPage
result in other areas of our code!Now, this is just a simple example of declarative routing, but you can get creative and use it however you want. Link your routes to a state management solution, or use a step count instead of checking if strings are empty - the possibilities are endless!To use our new login flow, just simply push the LoginWrapperPage
wherever you need it. This will open up the login flow, and once the flow is completed it will trigger onLogin
which we can then use to log the user in
context.router.root.push(LoginWrapperPage(onLogin: (result) {
// do something with the login result here, such as logging the user in
}));
context.router.root
. This is because our login flow was defined at the top level of the router, and not nested. It is generally good practice to put declarative flows at the root level.