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

Flutter Router如何同时处理系统返回键与AppBar返回并弹出确认模态框

你的实现思路的核心问题在于 onPopPage 是路由pop操作执行完成后的回调,只能处理pop后的收尾逻辑,无法在pop动作执行前做拦截,所以没办法在这里插入确认弹窗逻辑。

方案一:路由层统一拦截(推荐)

这种方案将所有返回拦截逻辑放在AppRouterDelegate中统一处理,系统返回键和AppBar返回按钮走完全相同的逻辑,不会出现两端行为不一致的问题。

步骤1:修改AppRouterDelegate的popRoute方法

这里是系统返回键的触发入口,我们先判断当前是否在需要拦截的页面,是的话先弹出确认框,用户确认后再执行pop操作:

@override
Future<bool> popRoute() async {
  final NavigatorState? navigator = navigatorKey.currentState;
  if (navigator == null) return SynchronousFuture<bool>(false);

  // 判断当前是否在需要拦截的新建产品页
  if (appNotifier.newItem) {
    // 弹出确认对话框
    final bool? confirm = await showDialog<bool>(
      context: navigator.context,
      builder: (context) => AlertDialog(
        title: const Text('确认退出?'),
        content: const Text('退出后已输入的内容将会丢失'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('确认退出'),
          ),
        ],
      ),
    );
    // 用户确认退出才执行后续逻辑
    if (confirm == true) {
      appNotifier.setNewItem(false);
      return SynchronousFuture(true);
    }
    // 用户取消,拦截返回操作
    return SynchronousFuture(false);
  }

  // 其他页面走默认返回逻辑
  return navigator.maybePop();
}

步骤2:简化onPopPage逻辑

因为我们已经在popRoute中处理了状态修改和拦截逻辑,onPopPage只需要保留基础逻辑即可:

onPopPage: (route, result) {
  if (!route.didPop(result)) return false;
  return true;
},

步骤3:修改ProductAddPage的返回按钮逻辑

让AppBar的返回按钮直接调用Router的pop方法,和系统返回键走同一套处理逻辑:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text("添加产品"),
      leading: IconButton(
        icon: const Icon(Icons.arrow_back),
        onPressed: () {
          // 触发RouterDelegate的popRoute逻辑,和系统返回键完全统一
          Router.of(context).pop();
        },
      ),
    ),
    // 页面其他内容
  );
}

方案二:页面层WillPopScope拦截

如果只有少量页面需要拦截,也可以在页面层通过WillPopScope实现,耦合度更低:

@override
Widget build(BuildContext context) {
  return WillPopScope(
    // 拦截系统返回键
    onWillPop: () async {
      final bool? confirm = await showDialog<bool>(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('确认退出?'),
          content: const Text('退出后已输入的内容将会丢失'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context, false),
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context, true),
              child: const Text('确认退出'),
            ),
          ],
        ),
      );
      return confirm ?? false;
    },
    child: Scaffold(
      appBar: AppBar(
        title: const Text("添加产品"),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () async {
            // 调用和系统返回键相同的拦截逻辑
            await Navigator.of(context).maybePop();
            // 确认返回后修改状态
            if (context.mounted) {
              context.read<AppNotifier>().setNewItem(false);
            }
          },
        ),
      ),
      // 页面其他内容
    ),
  );
}

这种方案不需要修改AppRouterDelegate的原有逻辑,适合页面拦截逻辑差异较大的场景。


内容的提问来源于stack exchange,提问作者Sa231asd3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:45:05