You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GoRoute的onExit回调中获取导航返回结果?

如何在GoRoute的onExit回调中获取导航返回结果?

我完全懂你遇到的这个痛点——用go_router做模态弹窗路由时,明明GoRouterDelegate._handlePopPageWithRouteMatch里能拿到pop的返回结果,却不把它暴露给onExit回调,想在拦截误退出的时候区分是用户主动点击按钮关闭,还是点遮罩层误触,确实挺闹心的。

给你分享几个社区里常用的可行方案,亲测能用:

方案一:页面状态同步法

核心思路是在弹窗页面的状态里提前存储pop的结果,让onExit能通过全局Key访问到这个值:

  1. 首先在弹窗页面的State类里定义存储变量和状态标识:
class ModalPage extends StatefulWidget {
  const ModalPage({super.key});

  @override
  State<ModalPage> createState() => ModalPageState();
}

class ModalPageState extends State<ModalPage> {
  // 用来存储pop的结果
  bool? _popResult;
  // 标记是否有未保存更改
  bool _hasUnsavedChanges = false;

  // 当页面内容修改时,把这个标记设为true
  void _onContentChanged() {
    setState(() {
      _hasUnsavedChanges = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('模态弹窗')),
      body: Column(
        children: [
          TextField(onChanged: (_) => _onContentChanged()),
          Row(
            children: [
              // 确认按钮:先赋值再pop
              ElevatedButton(
                onPressed: () {
                  _popResult = true;
                  Navigator.of(context).pop(true);
                },
                child: const Text('确认'),
              ),
              // 取消按钮同理
              ElevatedButton(
                onPressed: () {
                  _popResult = false;
                  Navigator.of(context).pop(false);
                },
                child: const Text('取消'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}
  1. 定义GoRoute时,通过全局Key获取页面状态,在onExit里读取结果:
// 提前创建全局Key来获取页面状态
final modalPageKey = GlobalKey<ModalPageState>();

GoRoute(
  path: '/modal-page',
  pageBuilder: (context, state) => MaterialPage(
    fullscreenDialog: true,
    child: ModalPage(key: modalPageKey),
  ),
  onExit: (context) async {
    final result = modalPageKey.currentState?._popResult;
    // 结果为null,说明是点击遮罩层触发的退出
    if (result == null && modalPageKey.currentState?._hasUnsavedChanges == true) {
      // 弹出确认对话框,询问用户是否要放弃未保存内容
      final shouldExit = await showDialog<bool>(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('未保存的更改'),
          content: const Text('离开后未保存的内容会丢失,确定要离开吗?'),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(false),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () => Navigator.of(context).pop(true),
              child: const Text('确定'),
            ),
          ],
        ),
      );
      return shouldExit ?? false;
    }
    // 允许正常退出
    return true;
  },
);

方案二:自定义导航观察者法

通过NavigatorObserver监听全局的pop动作,捕获路由结果后存在全局存储里,onExit时直接读取:

  1. 自定义一个观察者类,专门用来记录指定路由的pop结果:
class PopResultObserver extends NavigatorObserver {
  final Map<String, dynamic?> _routeResults = {};

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    // 只记录目标模态路由的结果
    if (route.settings.name == '/modal-page' && route is MaterialPageRoute) {
      _routeResults['/modal-page'] = route.result;
    }
  }

  // 获取指定路由的pop结果
  dynamic? getResult(String routePath) => _routeResults[routePath];

  // 用完后清空,避免缓存干扰
  void clearResult(String routePath) => _routeResults.remove(routePath);
}
  1. 初始化GoRouter时添加这个观察者,然后在onExit里读取结果:
// 创建观察者实例
final popObserver = PopResultObserver();

final router = GoRouter(
  observers: [popObserver],
  routes: [
    GoRoute(
      path: '/modal-page',
      pageBuilder: (context, state) => MaterialPage(
        fullscreenDialog: true,
        child: const ModalPage(),
      ),
      onExit: (context) async {
        final result = popObserver.getResult('/modal-page');
        popObserver.clearResult('/modal-page');
        
        if (result == null) {
          // 遮罩层退出的情况,检查未保存内容
          // 这里可以根据实际情况获取页面的未保存状态,比如用状态管理工具
          final hasUnsaved = true; // 替换成你的未保存状态判断逻辑
          if (hasUnsaved) {
            final shouldExit = await showDialog<bool>(
              context: context,
              builder: (context) => AlertDialog(
                title: const Text('未保存的更改'),
                content: const Text('离开后未保存的内容会丢失,确定要离开吗?'),
                actions: [
                  TextButton(
                    onPressed: () => Navigator.of(context).pop(false),
                    child: const Text('取消'),
                  ),
                  TextButton(
                    onPressed: () => Navigator.of(context).pop(true),
                    child: const Text('确定'),
                  ),
                ],
              ),
            );
            return shouldExit ?? false;
          }
        }
        return true;
      },
    ),
  ],
);

其实关于官方为什么不把result传给onExit,社区里也有不少讨论,大概率是因为onExit的设计初衷是做退出拦截,而非处理返回结果,但确实这个场景非常普遍,说不定后续版本会补上这个参数。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:54:52