Implementing Route Interceptors, Lifecycle Hooks, and Global Navigation in Flutter
This article explains how to create and configure route interceptors, use lifecycle callbacks, and manage global navigation in Flutter applications, providing code examples for single‑page and global interceptors, route lifecycle states, extended route observers, and best‑practice recommendations.
Many developers complain that Flutter's routing lacks features such as route guards, lifecycle listeners, and data pre‑loading. This guide introduces a comprehensive solution using route interceptors, lifecycle hooks, and global navigation utilities.
Interceptor
Route interceptors are useful for scenarios like permission verification, data pre‑loading, and path redirection.
Single‑page interceptor
Implement RouteInterceptor for a specific page:
class LoginInterceptor extends RouteInterceptor {
const LoginInterceptor();
@override
Future
intercept(String routeName, {Object? arguments}) async {
if (!User().hasLogin) {
return RouteInterceptResult.complete(
routeName: Routes.fluttercandiesLoginPage.name,
);
}
return RouteInterceptResult.next(
routeName: routeName,
arguments: arguments,
);
}
}The possible actions are defined by the RouteInterceptAction enum:
enum RouteInterceptAction { abort, next, complete }Annotate a page with the interceptor list:
@FFRoute(
name: 'fluttercandies://PageA',
routeName: 'PageA',
description: 'PageA',
interceptors:
[LoginInterceptor()],
)
class PageA extends StatefulWidget {
const PageA({Key? key}) : super(key: key);
@override
State
createState() => _PageAState();
}Run the code generator ff_route to produce a mapping:
const Map
> routeInterceptors = {
'fluttercandies://PageA':
[LoginInterceptor()],
'fluttercandies://PageB':
[LoginInterceptor(), PermissionInterceptor()],
};Register the mapping in main() :
void main() {
RouteInterceptorManager().addAllRouteInterceptors(routeInterceptors);
runApp(const MyApp());
}Global interceptor
If you prefer not to annotate each page, you can add interceptors globally:
class GlobalLoginInterceptor extends RouteInterceptor {
const GlobalLoginInterceptor();
@override
Future
intercept(String routeName, {Object? arguments}) async {
if ((routeName == Routes.fluttercandiesPageB.name || routeName == Routes.fluttercandiesPageA.name) && !User().hasLogin) {
return RouteInterceptResult.complete(routeName: Routes.fluttercandiesLoginPage.name);
}
return RouteInterceptResult.next(routeName: routeName, arguments: arguments);
}
}
void main() {
RouteInterceptorManager().addGlobalInterceptors([
const GlobalLoginInterceptor(),
const GlobalPermissionInterceptor(),
]);
runApp(const MyApp());
}Navigation with interceptors
Use the extension method NavigatorWithInterceptorExtension.pushNamedWithInterceptor : Navigator.of(context).pushNamedWithInterceptor(Routes.fluttercandiesPageA.name);
Or call the static helper NavigatorWithInterceptor.pushNamed : NavigatorWithInterceptor.pushNamed(context, Routes.fluttercandiesPageB.name);
Lifecycle
By extending RouteLifecycleState , a page can react to various lifecycle events such as onPageShow , onPageHide , onRouteShow , and onRouteHide . These callbacks are only triggered when the widget is hosted by a PageRoute .
class _PageBState extends RouteLifecycleState
{
@override
void onForeground() { print('PageB onForeground'); }
@override
void onBackground() { print('PageB onBackground'); }
@override
void onPageShow() { print('PageB onPageShow'); }
@override
void onPageHide() { print('PageB onPageHide'); }
@override
void onRouteShow() { print('onRouteShow'); }
@override
void onRouteHide() { print('onRouteHide'); }
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page B')),
body: const Center(child: Text('This is Page B')),
);
}
}ExtendedRouteObserver
The ExtendedRouteObserver enhances Flutter's built‑in RouteObserver by tracking all active routes, providing getters like topRoute , and methods such as containsRoute() , getRouteByName() , as well as callbacks onRouteAdded and onRouteRemoved . It is useful for global route tracking, custom navigation logic, and breadcrumb implementations.
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers:
[ExtendedRouteObserver()],
);
}GlobalNavigator
The GlobalNavigator class offers a way to access the Navigator and BuildContext from anywhere using a global navigatorKey . While convenient, it bypasses Flutter's recommended context‑based navigation and can lead to maintainability and performance issues.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: GlobalNavigator.navigatorKey,
home: HomeScreen(),
);
}
}
GlobalNavigator.navigator?.push(
MaterialPageRoute(builder: (context) => SecondScreen()),
);
showDialog(
context: GlobalNavigator.context!,
builder: (b) {
return AlertDialog(
title: const Text('Permission Denied'),
content: const Text('You do not have permission to access this page.'),
actions: [
TextButton(
onPressed: () { GlobalNavigator.navigator?.pop(); },
child: const Text('OK'),
),
],
);
},
);In summary, the article provides a complete solution for route interception, lifecycle handling, and global navigation in Flutter, while warning against overusing global navigation patterns.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.