flutter:navigatorObservers与RouteObserver

navigatorObservers 是什么

在 Flutter 中,MaterialAppCupertinoApp 都有一个参数:

MaterialApp(
  navigatorObservers: [/* 一组 NavigatorObserver */],
)

作用:它允许你监听 Navigator(路由栈管理器)的各种路由变化,比如:

  • 页面 push(进入)
  • 页面 pop(退出)
  • 页面 replace(替换)

要求传入的类型:final List<NavigatorObserver> navigatorObservers;

  • 它是一个 NavigatorObserver 的列表,你可以放多个,比如 RouteObserver、自定义的 NavigatorObserver

NavigatorObserver 基类

Flutter 提供了一个基类 NavigatorObserver,定义了一些回调方法。只要页面路由发生变化,就能在这里监听。

/// An interface for observing the behavior of a [Navigator].
class NavigatorObserver {
  /// The navigator that the observer is observing, if any.
  NavigatorState? get navigator => _navigators[this];

  static final Expando<NavigatorState> _navigators = Expando<NavigatorState>();
  
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {}
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {}
  void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {}
  void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {}
  void didChangeTop(Route<dynamic> topRoute, Route<dynamic>? previousTopRoute) {}
  void didStartUserGesture(Route<dynamic> route, Route<dynamic>? previousRoute) {}
  void didStopUserGesture() {}
}
回调函数 触发时机 常见应用场景
didPush 页面 push 进入 统计进入次数、初始化数据
didPop 页面 pop 退出 停留时长统计、资源释放
didRemove 路由被 直接移除 强制清理路由、登出处理
didReplace 路由被 替换 登录→首页、Splash→主页面
didChangeTop 栈顶路由变化 页面切换监听、控制资源
didStartUserGesture 手势导航开始 暂停动画、禁用交互
didStopUserGesture 手势导航结束 恢复动画、继续逻辑

Route<dynamic> route:新加入的路由

Route<dynamic>? previousRoute:之前处于栈顶的路由(可能是 null,如果是第一个页面)

Route<dynamic>? newRoute:替换后的新路由

Route<dynamic>? oldRoute:被替换掉的路由

didPopdidRemove区别

  • didPop用户或系统触发的页面退出
  • didRemove代码直接删除了某个路由(不会触发页面退出动画)
    • 当调用 Navigator.removeRoute 或者 Navigator.removeRouteBelow 等方法 直接移除路由 时触发。

didReplace:当调用 Navigator.pushReplacementNavigator.replace 时触发。

didChangeTop:当 栈顶路由发生变化 时触发(无论是 pushpopreplace)。

案例

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      navigatorObservers: [MyNavigatorObserver()], // 注册自定义观察器
      routes: {'/': (_) => FirstPage(), '/second': (_) => SecondPage()},
    ),
  );
}

// 自定义全局路由观察器
class MyNavigatorObserver extends NavigatorObserver {
  // didPush: 处理路由推送事件
  @override
  void didPush(Route route, Route? previousRoute) {
    debugPrint(
      "didPush: ${route.settings.name}, from: ${previousRoute?.settings.name}",
    );
  }

  // didPop: 处理路由弹出事件
  @override
  void didPop(Route route, Route? previousRoute) {
    debugPrint(
      "didPop: ${route.settings.name}, back to: ${previousRoute?.settings.name}",
    );
  }

  // didRemove: 处理路由移除事件
  @override
  void didRemove(Route route, Route? previousRoute) {
    debugPrint("didRemove: ${route.settings.name}");
  }

  // didReplace: 处理路由替换事件
  @override
  void didReplace({Route? newRoute, Route? oldRoute}) {
    debugPrint(
      "didReplace: ${oldRoute?.settings.name} -> ${newRoute?.settings.name}",
    );
  }

  // didChangeTop: 处理路由栈顶变化事件
  @override
  void didChangeTop(Route topRoute, Route? previousTopRoute) {
    debugPrint(
      "didChangeTop: ${previousTopRoute?.settings.name} -> ${topRoute.settings.name}",
    );
  }
}

class FirstPage extends StatelessWidget {
  const FirstPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("First Page")),
      body: Center(
        child: ElevatedButton(
          child: Text("Go to Second"),
          onPressed: () => Navigator.pushNamed(context, '/second'),
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Second Page")),
      body: Center(
        child: ElevatedButton(
          child: Text("Back"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

// 日志:
I/flutter (12161): didPush: /second, from: /
I/flutter (12161): didChangeTop: / -> /second
W/WindowOnBackDispatcher(12161): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(12161): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
I/flutter (12161): didPop: /second, back to: /
I/flutter (12161): didChangeTop: /second -> /

Route 类

在 Flutter 里,Route 就是对一个页面(screen/page)的抽象表示

abstract class Route<T> extends _RoutePlaceholder 

比如当 Navigator.push(...) 时,本质上就是往路由栈里 压入一个新的 Route 对象

常见的 Route 类型:

  • MaterialPageRoute<T>:最常用的路由,带有 Material 动画效果(安卓风格)。

    Navigator.push(
      context,
      MaterialPageRoute(builder: (_) => MyPage()),
    );
    
  • CupertinoPageRoute<T>:iOS 风格的路由(右滑返回)。

  • PageRouteBuilder<T>:自定义路由动画,可以自己定义 transitionBuilder

  • ModalRoute<T>:抽象类,所有模态(覆盖式)页面路由的基类。MaterialPageRoute 就是继承自 ModalRoute

  • PopupRoute<T>:比如 DialogRoutePopupMenuRoute

总结: Route = 页面容器,它定义了页面的生命周期、动画、是否全屏、返回值等。

RouteObserver 类

class RouteObserver<R extends Route<dynamic>> extends NavigatorObserver {
  final Map<R, Set<RouteAware>> _listeners = <R, Set<RouteAware>>{};

RouteObserver<R extends Route<dynamic>> 是 Flutter 提供的一个 导航事件监听工具类,它继承自 NavigatorObserver。由于继承了NavigatorObserver,所有它监听 Navigator(路由栈管理器)的各种路由变化。

  • 页面 push(进入)
  • 页面 pop(退出)
  • 页面 replace(替换)

此外,它在 NavigatorObserver 的基础上,扩展了 订阅/取消订阅 机制,方便某些 Widget(通常是 StatefulWidget)在路由变化时收到通知。

class RouteObserver<R extends Route<dynamic>> extends NavigatorObserver {
  final Map<R, Set<RouteAware>> _listeners = <R, Set<RouteAware>>{};

  void subscribe(RouteAware routeAware, R route) {
    // 订阅某个路由
  }

  void unsubscribe(RouteAware routeAware) {
    // 取消订阅
  }

  @override
  void didPush(Route route, Route? previousRoute) {
    // 通知监听者
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    // 通知监听者
  }
}

案例:使用RouteObserver

  • RouteObserver 本身只有全局回调,你需要手动 if (route.settings.name == ...) 判断是哪一个页面。
  • 当页面越来越多时,你会写很多 if else 来判断 route.settings.name
  • 此外,这么写代码耦合度高,所有逻辑都堆在 LoggingRouteObserver 里。
import 'package:flutter/material.dart';

// 全局 RouteObserver
// final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

/// 自定义 LoggingRouteObserver
class LoggingRouteObserver extends RouteObserver<PageRoute<dynamic>> {
  @override
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute);
    debugPrint("didPush: ${route.settings.name}");

    // 判断逻辑
    if (route.settings.name == '/second') {
      debugPrint("👉 进入 SecondPage,做一些逻辑");
    } else if (route.settings.name == '/third') {
      debugPrint("👉 进入 ThirdPage,加载第三页数据");
    }
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    debugPrint("didPop: ${route.settings.name}");

    if (route.settings.name == '/second') {
      debugPrint("👉 离开 SecondPage,清理资源");
    } else if (route.settings.name == '/third') {
      debugPrint("👉 离开 ThirdPage,保存草稿");
    }
  }
}

void main() {
  runApp(
    MaterialApp(
      navigatorObservers: [LoggingRouteObserver()], // 注册自定义的 RouteObserver
      routes: {
        '/': (_) => FirstPage(),
        '/second': (_) => SecondPage(),
        '/third': (_) => ThirdPage(),
      },
    ),
  );
}

class FirstPage extends StatelessWidget {
  const FirstPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("First Page")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              child: Text("Go to Second"),
              onPressed: () => Navigator.pushNamed(context, '/second'),
            ),
            ElevatedButton(
              child: Text("Go to Third"),
              onPressed: () => Navigator.pushNamed(context, '/third'),
            ),
          ],
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Second Page")),
      body: Center(
        child: ElevatedButton(
          child: Text("Go to Third"),
          onPressed: () => Navigator.pushNamed(context, '/third'),
        ),
      ),
    );
  }
}

class ThirdPage extends StatelessWidget {
  const ThirdPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Third Page")),
      body: Center(
        child: ElevatedButton(
          child: Text("Back"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

日志如下:

I/flutter (12420): didPush: /
I/flutter (12420): didPush: /second
I/flutter (12420): 👉 进入 SecondPage,做一些逻辑
W/WindowOnBackDispatcher(12420): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(12420): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
I/flutter (12420): didPush: /third
I/flutter (12420): 👉 进入 ThirdPage,加载第三页数据
I/flutter (12420): didPop: /third
I/flutter (12420): 👉 离开 ThirdPage,保存草稿
I/flutter (12420): didPush: /third
I/flutter (12420): 👉 进入 ThirdPage,加载第三页数据
I/flutter (12420): didPop: /third
I/flutter (12420): 👉 离开 ThirdPage,保存草稿
I/flutter (12420): didPop: /second
I/flutter (12420): 👉 离开 SecondPage,清理资源
I/flutter (12420): didPush: /third
I/flutter (12420): 👉 进入 ThirdPage,加载第三页数据
W/WindowOnBackDispatcher(12420): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(12420): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
I/flutter (12420): didPop: /third
I/flutter (12420): 👉 离开 ThirdPage,保存草稿

RouteAware 接口

这是和 RouteObserver 搭配使用的接口,页面 State 可以实现它来感知路由生命周期:

abstract mixin class RouteAware {
  void didPush();       // 当前页面 被 push 进来 时调用(即当前页面进入导航栈并显示)。
  void didPop();        // 当前页面 被 pop 出去 时调用(即当前页面被销毁)。
  void didPopNext();    // 当 新的页面 push 进来覆盖当前页面 时调用(当前页面进入“不可见”状态)。
  void didPushNext();   // 当 上层页面被 pop 掉后,当前页面重新可见时调用。
}

假设我们有 PageA → PageB → PageC 这三页。

  1. 开始页面:PageA
    • PageA.didPush()(A 第一次显示)
  2. PageAPageB
    • PageA.didPushNext()(A 被覆盖,不可见)
    • PageB.didPush()(B 显示)
  3. PageBPageC
    • PageB.didPushNext()(B 被覆盖,不可见)
    • PageC.didPush()(C 显示)
  4. PageC 被 Pop 回到 PageB
  • PageC.didPop()(C 被销毁)
  • PageB.didPopNext()(B 再次可见)
  1. PageB 被 Pop 回到 PageA
  • PageB.didPop()(B 被销毁)
  • PageA.didPopNext()(A 再次可见)

NavigatorObserver VS. RouteAware

全局的:你在 MaterialApp.navigatorObservers 里配置,所有路由变化都会经过它。

回调方法:didPushdidPopdidRemovedidReplacedidChangeTop 等。

问题:

  • 你在这里拿到的是 所有页面的路由变化
  • 如果你只想让某个页面知道「自己什么时候被覆盖、什么时候重新显示」,你就得写一堆逻辑去匹配 route,会比较麻烦。

RouteAware 的特点

局部的:页面(State)自己 with RouteAware,再手动订阅 RouteObserver,就能收到自己的路由生命周期。

回调方法:didPushdidPopdidPushNextdidPopNext

好处:

  • 页面只关心自己的可见性变化,不用在全局的 NavigatorObserver 里去做 if (route.settings.name == "XXX") 这种判断。
  • 封装性更好:逻辑留在页面内部,减少耦合。

比较

特点 NavigatorObserver RouteAware
作用范围 全局,监听所有路由变化 局部,页面自己订阅自己
回调对象 所有路由(需要自己过滤) 当前页面本身
使用复杂度 简单监听:适合统计、日志 页面内逻辑:更方便
应用场景 全局埋点、导航统计、统一处理 单页面生命周期(视频暂停/恢复、定时器管理)

案例1:使用RouteObserver+RouteAware

import 'package:flutter/material.dart';

/// 这个实例需要被:1) 注册到 navigatorObservers;2) 在页面中订阅
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

void main() {
  runApp(
    MaterialApp(
      navigatorObservers: [routeObserver], // 一定要注册这个实例
      routes: {
        '/': (_) => FirstPage(),
        '/second': (_) => SecondPage(),
        '/third': (_) => ThirdPage(),
      },
    ),
  );
}

class FirstPage extends StatelessWidget {
  const FirstPage({super.key});

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text("First Page")),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            child: const Text("Go to Second"),
            onPressed: () => Navigator.pushNamed(context, '/second'),
          ),
          ElevatedButton(
            child: const Text("Go to Third"),
            onPressed: () => Navigator.pushNamed(context, '/third'),
          ),
        ],
      ),
    ),
  );
}

class SecondPage extends StatefulWidget {
  const SecondPage({super.key});

  @override
  State<SecondPage> createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 用“同一个” routeObserver 订阅当前 PageRoute
    // ModalRoute.of(context): 获取当前路由
    // as PageRoute: 类型转换
    // void subscribe(RouteAware routeAware, R route)
    routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    // 销毁的时候取消订阅
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  // 当前页面被 push 到导航栈
  @override
  void didPush() => debugPrint("SecondPage didPush");
  // 当前页面被 pop 出导航栈
  @override
  void didPop() => debugPrint("SecondPage didPop");
  // 当前页面被覆盖(即将进入下一个页面)
  @override
  void didPushNext() => debugPrint("SecondPage 被覆盖(didPushNext)");
  // 当前页面重新显示(从下一个页面返回)
  @override
  void didPopNext() => debugPrint("返回到 SecondPage(didPopNext)");

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text("Second Page")),
    body: Center(
      child: ElevatedButton(
        child: const Text("Go to Third"),
        onPressed: () => Navigator.pushNamed(context, '/third'),
      ),
    ),
  );
}

class ThirdPage extends StatefulWidget {
  const ThirdPage({super.key});

  @override
  State<ThirdPage> createState() => _ThirdPageState();
}

class _ThirdPageState extends State<ThirdPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 用“同一个” routeObserver 订阅当前 PageRoute
    // ModalRoute.of(context): 获取当前路由
    // as PageRoute: 类型转换
    // void subscribe(RouteAware routeAware, R route)
    routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    // 销毁的时候取消订阅
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  // 当前页面被 push 到导航栈
  @override
  void didPush() => debugPrint("ThirdPage didPush");
  @override
  void didPop() => debugPrint("ThirdPage didPop");
  @override
  void didPushNext() => debugPrint("ThirdPage 被覆盖(didPushNext)");
  @override
  void didPopNext() => debugPrint("返回到 ThirdPage(didPopNext)");

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text("Third Page")),
    body: Center(
      child: ElevatedButton(
        child: const Text("Back"),
        onPressed: () => Navigator.pop(context),
      ),
    ),
  );
}

以下是日志:

// 日志
I/flutter (12638): SecondPage didPush
W/WindowOnBackDispatcher(12638): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(12638): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
I/flutter (12638): SecondPage 被覆盖(didPushNext)
I/flutter (12638): ThirdPage didPush
I/flutter (12638): 返回到 SecondPage(didPopNext)
I/flutter (12638): ThirdPage didPop
I/flutter (12638): SecondPage didPop

案例2:全局回调 + 转发给 RouteAware

在上面这个案例中,RouteObserver并没有重写方法,所有无法使用NavigatorObserver的回调函数。

所有我们可以自定义一个类AppRouteObserver,然后继承RouteObserver

接着在注册到navigatorObservers,这样就可以实现RouteAwareNavigatorObserver的特性。

import 'package:flutter/material.dart';

/// 全局单例:自定义的 RouteObserver(同时承担全局回调 + RouteAware 分发)
final AppRouteObserver appRouteObserver = AppRouteObserver();

class AppRouteObserver extends RouteObserver<PageRoute<dynamic>> {
  // 当有 route 被 push 时,First: 调用 super,确保 RouteObserver 通知订阅者
  // Second: 在这里写你的“全局”逻辑(埋点 / 统一处理)
  @override
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute); // <<< 必须,负责触发 RouteAware 回调
    debugPrint("-------------------------------------------------------");
    debugPrint(
      'AppRouteObserver.didPush: ${route.settings.name}, from: ${previousRoute?.settings.name}',
    );
    // 全局逻辑示例:针对某个路由做特殊处理
    if (route.settings.name == '/second') {
      debugPrint('🔔 全局处理:SecondPage 进入(例如埋点/预加载)');
    }
    debugPrint("-------------------------------------------------------");
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    debugPrint("-------------------------------------------------------");
    debugPrint(
      'AppRouteObserver.didPop: ${route.settings.name}, back to: ${previousRoute?.settings.name}',
    );
    if (route.settings.name == '/third') {
      debugPrint('🔔 全局处理:ThirdPage 离开(例如保存草稿)');
    }
    debugPrint("-------------------------------------------------------");
  }

  // 其它回调也同理(示例省略重复)
  @override
  void didChangeTop(Route<dynamic> topRoute, Route<dynamic>? previousTopRoute) {
    super.didChangeTop(topRoute, previousTopRoute);
    debugPrint("-------------------------------------------------------");
    debugPrint(
      'AppRouteObserver.didChange: ${previousTopRoute?.settings.name} -> ${topRoute.settings.name}',
    );
    debugPrint("-------------------------------------------------------");
  }
}

void main() {
  runApp(
    MaterialApp(
      navigatorObservers: [appRouteObserver], // 注册同一个实例
      routes: {
        '/': (_) => FirstPage(),
        '/second': (_) => SecondPage(),
        '/third': (_) => ThirdPage(),
      },
    ),
  );
}

class FirstPage extends StatelessWidget {
  const FirstPage({super.key});

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text("First Page")),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () => Navigator.pushNamed(context, '/second'),
            child: const Text('Go to Second'),
          ),
          ElevatedButton(
            onPressed: () => Navigator.pushNamed(context, '/third'),
            child: const Text('Go to Third'),
          ),
        ],
      ),
    ),
  );
}

/// SecondPage 使用 RouteAware:局部页面逻辑放在页面内部
class SecondPage extends StatefulWidget {
  const SecondPage({super.key});

  @override
  State<SecondPage> createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 订阅“同一个被注册到 navigatorObservers 的实例”
    appRouteObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    appRouteObserver.unsubscribe(this);
    super.dispose();
  }

  // RouteAware 回调:页面自己负责播放/暂停/刷新等逻辑
  @override
  void didPush() => debugPrint('SecondPage: didPush (页面进入)');
  @override
  void didPop() => debugPrint('SecondPage: didPop (页面退出)');
  @override
  void didPushNext() => debugPrint('SecondPage: didPushNext (被覆盖)');
  @override
  void didPopNext() => debugPrint('SecondPage: didPopNext (下一个页面退出,自己可见)');

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('Second Page')),
    body: Center(
      child: ElevatedButton(
        onPressed: () => Navigator.pushNamed(context, '/third'),
        child: const Text('Go to Third'),
      ),
    ),
  );
}

class ThirdPage extends StatefulWidget {
  const ThirdPage({super.key});

  @override
  State<ThirdPage> createState() => _ThirdPageState();
}

class _ThirdPageState extends State<ThirdPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    appRouteObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    appRouteObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() => debugPrint('ThirdPage: didPush');
  @override
  void didPop() => debugPrint('ThirdPage: didPop');
  @override
  void didPushNext() => debugPrint('ThirdPage: didPushNext');
  @override
  void didPopNext() => debugPrint('ThirdPage: didPopNext');

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('Third Page')),
    body: Center(
      child: ElevatedButton(
        onPressed: () => Navigator.pop(context),
        child: const Text('Back'),
      ),
    ),
  );
}

以下是日志:

Restarted application in 1,559ms.
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didPush: /, from: null
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didChange: null -> /
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didPush: /second, from: /
I/flutter (13167): 🔔 全局处理:SecondPage 进入(例如埋点/预加载)
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didChange: / -> /second
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): SecondPage: didPush (页面进入)
W/WindowOnBackDispatcher(13167): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(13167): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
I/flutter (13167): SecondPage: didPushNext (被覆盖)
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didPush: /third, from: /second
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didChange: /second -> /third
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): ThirdPage: didPush
I/flutter (13167): SecondPage: didPopNext (下一个页面退出,自己可见)
I/flutter (13167): ThirdPage: didPop
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didPop: /third, back to: /second
I/flutter (13167): 🔔 全局处理:ThirdPage 离开(例如保存草稿)
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didChange: /third -> /second
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): SecondPage: didPop (页面退出)
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didPop: /second, back to: /
2
I/flutter (13167): -------------------------------------------------------
I/flutter (13167): AppRouteObserver.didChange: /second -> /
I/flutter (13167): -------------------------------------------------------

案例3:同时注册两个 observer

如果你想把“全局逻辑”写在一个独立的 MyNavigatorObserver 类里,同时又想使用 RouteAware,也可以 把两个实例都注册到 navigatorObservers。但订阅时必须订阅那个真正的 RouteObserver 实例(不能新建一个没有注册的实例)。

final RouteObserver<PageRoute> globalRouteObserver = RouteObserver<PageRoute>();
final MyNavigatorObserver myNavObserver = MyNavigatorObserver();

class MyNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route? previousRoute) {
    debugPrint('MyNavigatorObserver didPush: ${route.settings.name}');
    // 注意:此类不是 RouteObserver,不能替代 RouteObserver 的 subscribe/分发机制
  }
}

void main() {
  runApp(MaterialApp(
    // 注意:同时注册两个 observer
    navigatorObservers: [myNavObserver, globalRouteObserver],
    routes: {
      '/': (_) => FirstPage(),
      '/second': (_) => SecondPage(),
      '/third': (_) => ThirdPage(),
    },
  ));
}

// 页面订阅必须使用 globalRouteObserver(就是上面注册的实例)
class SecondPage extends StatefulWidget { /* ... same as above ... */ }
class _SecondPageState extends State<SecondPage> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    globalRouteObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
  }

  @override
  void dispose() {
    globalRouteObserver.unsubscribe(this);
    super.dispose();
  }
  // ...
}

×

喜欢就点赞,疼爱就打赏