Manage App Navigation

Manage App Navigation

Routing is implemented using the most up to date Router and Page API from the Flutter SDK. This allows us to control the entire navigation stack and how it behaves. It also allows us to support URL synchronization and deep linking. Unlike using Navigator.push, this navigation solution is compatible for all platforms and allows for complex routing solutions.

This router is configured to work without context, using a RouterService.

Default Routes

The boilerplate comes with two routes which cannot be removed, or else navigation will break.

  • / - The home page.
  • /404 - The 404 page, when a route is not found.
route_config.dart
final routes = [
RouteEntry(path: '/', builder: (key, routeData) => const HomeView()),
RouteEntry(path: '/404', builder: (key, routeData) => const NotFoundView()),
];

Adding a New Route

To add a new route, you can use the RouteEntry constructor.

RouteEntry(path: '/new-route', builder: (key, routeData) => const NewRouteView()),

But you can also add dynamic routes. These must be formatted using : in front of the dynamic parameter within the path.

RouteEntry(path: '/new-route/:id', builder: (key, routeData) => const NewRouteView(id: routeData.pathParameters['id'])),

Using a Route

The RouterService is available app wide, and there are 6 different ways to navigate.

goTo

To navigate to a new route, you can use the goTo method.

locator<RouterService>().goTo(Path(name: '/new-route'));

replace

To replace the current route, you can use the replace method.

locator<RouterService>().replace(Path(name: '/new-route'));

back

To navigate back, you can use the back method.

locator<RouterService>().back();

backUntil

To navigate back until a specific route, you can use the backUntil method.

locator<RouterService>().backUntil(Path(name: '/new-route'));

replaceAll

To replace all routes, you can use the replaceAll method.

locator<RouterService>().replaceAll([Path(name: '/new-route'), Path(name: '/new-route-2')]);

remove

To remove a specific route, you can use the remove method.

locator<RouterService>().remove(Path(name: '/new-route'));

Passing Data to a Route

There are 3 different ways to pass data to a route.

Path Parameters

We metioned the first approach using path parameters in the Adding a New Route section.

// configure the route in route_config.dart
RouteEntry(path: '/todos/:id', builder: (key, routeData) {
final id = routeData.pathParameters['id'];
return TodoView(id: id);
}),
// go to the route
locator<RouterService>().goTo(Path(name: '/todos/123'));

Query Parameters

You can also pass data using queryParameter, which is a property when pushing to pass a custom object.

// configure the route in route_config.dart
RouteEntry(path: '/todos', builder: (key, routeData) {
final id = routeData.queryParameters['id'];
return TodoView(id: id);
}),
// go to the route
locator<RouterService>().goTo(Path(name: '/todos?id=123'));

Extra

The first 2 methods are utilizing the URL, but you can also pass the data directly to the route using extra, which allows you to pass a custom object.

// configure the route in route_config.dart
RouteEntry(path: '/todos', builder: (key, routeData) {
final todo = routeData.extra as Todo;
return TodoView(id: todo.id, name: todo.name);
}),
// go to the route
locator<RouterService>().goTo(Path(name: '/todos', extra: Todo(id: '123', name: 'Test')));

Automatic Routing Depending on Auth

A common use case is to automatically route to a different page depending on the authentication state. To achieve this, you would listen to the authenticated state and navigate from there. Here is a simple example.

auth_service.dart
class AuthService {
AuthService({required RouterService routerService, required FirebaseAuth firebaseAuth})
: _routerService = routerService, _firebaseAuth = firebaseAuth {
// synchronous check of routing
checkAuthState(firebaseAuth.currentUser?.uid);
// listen to user state for routing
firebaseAuth.authStateChanges().listen((User? user) {
checkAuthState(user?.uid);
});
}
void checkAuthState(String? uid) {
if (uid == null) {
routerService.replaceAll([
Path(name: '/login'),
]);
} else {
routerService.replaceAll([
Path(name: '/'),
]);
}
}
}

Note

Because the stream takes time to emit its first value, we need to set the route synchronously, otherwise you will have a transition to ”/” and then a transition to “/login”.