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. 简单项目 :使用 onGenerateRouteNavigatorObserver
  2. GetX 项目 :使用 GetX 中间件
  3. 复杂路由需求 :考虑 AutoRoute Fluro
  4. 状态驱动导航 :使用 BLoC/Cubit 模式

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

相关推荐
快起来搬砖了6 小时前
Uniapp 图片前端上传功能实现与详解
前端·uni-app
南北是北北6 小时前
BufferQueue的环形队列是什么设计的
前端·面试
南北是北北6 小时前
Surface中的BufferQueue
前端·面试
willlzq6 小时前
Swift高级特性深度解析:@dynamicMemberLookup与@dynamicCallable在Builder库中的应用
前端
张旭超7 小时前
vue3 上传插件vue-file-agent-next
前端·vue.js
xianxin_7 小时前
CSS Opacity(透明度)
前端
渐行渐远君489017 小时前
AI Agent智能体与MCP Server开发实践
前端·人工智能
cowice7 小时前
Dart基础知识
flutter·dart
OEC小胖胖7 小时前
构建单页应用:React Router v6 核心概念与实战
前端·react.js·前端框架·web