Flutter中页面拦截器的实现方法

在 Flutter 中,页面拦截器(路由拦截)可以通过多种方式实现,以下是主要的几种方法:

这是 Flutter 内置的标准方法,通过监听导航事件实现拦截。

dart 复制代码
class AuthNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    // 页面被推入时调用
    super.didPush(route, previousRoute);
    _checkAuth(route);
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    // 页面被弹出时调用
    super.didPop(route, previousRoute);
  }

  void _checkAuth(Route<dynamic> route) {
    if (route.settings.name == '/profile' && !UserService.isLoggedIn) {
      // 重定向到登录页
      Navigator.pushReplacementNamed(route.navigator!.context, '/login');
    }
  }
}

// 在 MaterialApp 中使用
MaterialApp(
  navigatorObservers: [AuthNavigatorObserver()],
  // 其他配置...
)

2. 使用 GetX 中间件(推荐用于 GetX 项目)

如果您使用 GetX 状态管理库,可以使用其内置的中间件系统。

dart 复制代码
class AuthMiddleware extends GetMiddleware {
  @override
  int? priority = 1; // 优先级,数字越小优先级越高
  
  @override
  RouteSettings? redirect(String? route) {
    // 检查用户是否登录
    if (!AuthService.isLoggedIn && route != '/login') {
      return RouteSettings(name: '/login');
    }
    return null;
  }
}

// 在路由配置中使用
GetPage(
  name: '/profile',
  page: () => ProfilePage(),
  middlewares: [AuthMiddleware()],
),

个人理解

dart 复制代码
middlewares: [AuthMiddleware()] 中间件可以设置多个,他们的执行顺序按照他们各自的priority优先级来;
返回的RouteSettings(name: '/login')必须是在
GetPage(
  name: '/login',
  page: () => OtherPageView(),
),中配置的
一旦某个中间件返回了重定向(RouteSettings),后续所有中间件都将不会执行。

前两种方式的性能差异:

dart 复制代码
GetX 的中间件(拦截器):只有你在路由配置里设置了 middlewares,对应页面跳转时才会执行这些中间件。如果没设置中间件,页面跳转就不会有拦截和额外逻辑,性能不会有影响。

Flutter 的 NavigatorObserver:这是 Flutter 原生的导航观察者,只要你在 MaterialApp 或 GetMaterialApp 里注册了,它会监听所有页面的 push、pop、replace 等导航事件。
它的回调(如 didPush、didPop)会在每次页面跳转时执行。

性能影响说明:

NavigatorObserver 本身只是事件监听,回调方法很轻量,通常不会影响性能。
只有你在这些回调里写了复杂或耗时的逻辑,才可能拖慢页面跳转。
一般用于埋点、统计、日志、权限等场景,合理使用不会有明显性能问题。
总结:

GetX 中间件只在你设置时才执行,不设置就没有影响。
NavigatorObserver 只要注册就会监听所有导航事件,但本身很轻量,性能影响极小,主要看你回调里写了什么。

3. 使用 onGenerateRoute 拦截

在 MaterialApp 的 onGenerateRoute 回调中实现路由拦截。

dart 复制代码
MaterialApp(
  onGenerateRoute: (RouteSettings settings) {
    // 检查权限
    if (settings.name == '/admin' && !UserService.isAdmin) {
      return MaterialPageRoute(builder: (_) => UnauthorizedPage());
    }
    
    // 正常路由
    switch (settings.name) {
      case '/':
        return MaterialPageRoute(builder: (_) => HomePage());
      case '/profile':
        return MaterialPageRoute(builder: (_) => ProfilePage());
      default:
        return MaterialPageRoute(builder: (_) => NotFoundPage());
    }
  },
)

1与3的差异:

dart 复制代码
NavigatorObserver 和 onGenerateRoute 在拦截导航行为上有根本性的区别:它们处于导航过程的不同阶段,因此拥有不同的能力和职责。
onGenerateRoute 是 路由生成器:它在路由被创建之前拦截,决定要创建什么。
NavigatorObserver 是 路由观察者:它在路由被推送或弹出之后拦截,用于知道发生了什么。

核心区别对比表

特性 onGenerateRoute NavigatorObserver
拦截时机 路由创建之前 路由已经执行之后
角色 生成者/决策者 观察者/记录者
主要能力 创建、替换、重定向路由。可以决定不创建目标路由,而是创建另一个(如登录页)。 监听、记录、分析导航事件。无法改变当前正在发生的导航行为。
访问 Context 可以 ,因为它是一个构建页面的函数,自然能拿到 context。 不可以 ,观察者方法里没有 context 参数。
典型用例 身份验证守卫:用户未登录时,访问个人中心页面的请求被重定向到登录页。 行为分析:记录用户浏览了哪些页面(Firebase Analytics等)。
修改导航 可以强干预 ,直接返回一个不同的 Route 对象。 不能直接干预 当前的 push/pop 操作。但可以通过监听结果触发新的导航(这可能导致体验不佳)。
依赖获取 可通过 ModalRoute.of(context) 获取路由参数,可通过 Provider 等访问全局状态。 无法直接通过 context 获取依赖,通常需要直接访问全局状态容器(如 Get.put() 的实例、全局的 bloc 等)。

4. 使用 RouteAware 混入

通过混入 RouteAware 类并配合 NavigatorObserver 实现页面级别的拦截。

dart 复制代码
class AuthPage extends StatefulWidget {
  @override
  _AuthPageState createState() => _AuthPageState();
}

class _AuthPageState extends State<AuthPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    RouteObserver.of(context).subscribe(this, ModalRoute.of(context)!);
  }

  @override
  void didPush() {
    // 页面被推入时
    _checkAuth();
  }

  @override
  void dispose() {
    RouteObserver.of(context).unsubscribe(this);
    super.dispose();
  }

  void _checkAuth() {
    if (!UserService.isLoggedIn) {
      Navigator.pushReplacementNamed(context, '/login');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 页面内容...
    );
  }
}

// 在 MaterialApp 中注册观察者
MaterialApp(
  navigatorObservers: [RouteObserver<PageRoute>()],
)

5. 使用第三方路由库

许多第三方路由库提供了更强大的拦截功能:

AutoRoute

dart 复制代码
@RoutePage()
class ProfilePage extends StatelessWidget {
  // 页面内容...
}

// 使用守卫
class AuthGuard extends AutoRouteGuard {
  @override
  void onNavigation(NavigationResolver resolver, StackRouter router) {
    if (UserService.isLoggedIn) {
      resolver.next(true);
    } else {
      router.push(LoginRoute());
      resolver.next(false);
    }
  }
}

Fluro

dart 复制代码
// 定义处理程序
var profileHandler = Handler(
  handlerFunc: (context, params) {
    if (!UserService.isLoggedIn) {
      return LoginPage();
    }
    return ProfilePage();
  },
);

// 配置路由
void defineRoutes(FluroRouter router) {
  router.define('/profile', handler: profileHandler);
}

6. 使用 BLoC/Cubit 模式结合导航

结合状态管理实现更复杂的拦截逻辑。

dart 复制代码
// 在 BLoC 中控制导航
class NavigationCubit extends Cubit<NavigationState> {
  final AuthCubit authCubit;
  StreamSubscription? _authSubscription;

  NavigationCubit({required this.authCubit}) : super(NavigationInitial()) {
    // 监听认证状态变化
    _authSubscription = authCubit.stream.listen((authState) {
      if (authState is Unauthenticated) {
        // 重定向到登录页
        emit(NavigationRedirect(route: '/login'));
      }
    });
  }

  @override
  Future<void> close() {
    _authSubscription?.cancel();
    return super.close();
  }
}

// 在 UI 中监听导航状态
BlocListener<NavigationCubit, NavigationState>(
  listener: (context, state) {
    if (state is NavigationRedirect) {
      Navigator.pushReplacementNamed(context, state.route);
    }
  },
  child: // 页面内容...
)

选择建议

  1. 简单项目 :使用 onGenerateRoute 或 NavigatorObserver
  2. GetX 项目 :使用 GetX 中间件
  3. 复杂路由需求 :考虑 AutoRoute 或 Fluro
  4. 状态驱动导航 :使用 BLoC/Cubit 模式

根据您的项目规模和需求选择最适合的方法。对于大多数应用,GetX 中间件或 NavigatorObserver 提供了良好的平衡 between 简单性和功能性。

相关推荐
红尘散仙8 小时前
谁说终端只能敲键盘?我用 Rust 给小说阅读器加上了鼠标
前端·后端·rust
陈随易10 小时前
C++26 草案被否,二十八年来头一回
前端·后端·程序员
ZhengEnCi10 小时前
Agent 工具怎么折叠海量输出才「爽」:Trae、Claude Code 的渐进式披露设计原理
前端·agent
IT_陈寒13 小时前
Redis内存暴涨,没想到是AOF重写捣的鬼
前端·人工智能·后端
ikoala13 小时前
让 Codex 自己查账:一个省 token 的小技巧
前端·后端·ai编程
ikoala13 小时前
AI 写页面像说明书,修 Bug 像贴膏药?把这两句话写进 AGENTS.md
前端·javascript·ai编程
好的佩奇14 小时前
Dart 之扩展方法
android·flutter·dart
去伪存真15 小时前
AI Agent Harness 深度测评与技术选型实战
前端·agent
好的佩奇16 小时前
蓝牙之经典蓝牙
android·flutter·蓝牙
FRing0318 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2