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

Flutter中如何检测并阻止用户离开当前页面?

Flutter 页面离开行为检测与拦截实现

Flutter中页面离开的触发场景分为两类:一类是物理返回键、AppBar返回按钮、iOS侧滑返回触发的路由弹出(pop)操作;另一类是代码主动调用路由跳转方法触发的离开,两类场景需要分别适配。


1. 拦截路由弹出类离开(官方推荐方案,适用于Flutter 3.12+)

使用官方提供的PopScope组件实现,这是替代旧版WillPopScope的标准方案,自动兼容iOS侧滑返回手势,无需额外适配。
核心参数说明:

  • canPop:布尔值,设置为false时会拦截所有系统触发的pop操作
  • onPopInvoked:pop操作触发时的回调,参数didPop标识系统是否已经完成页面弹出,值为true时无需额外处理,值为false代表拦截成功,可在回调中执行业务判断逻辑

完整代码示例:

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

  @override
  State<EditPage> createState() => _EditPageState();
}

class _EditPageState extends State<EditPage> {
  // 业务状态标识:例如当前存在未保存的编辑内容
  bool _hasUnSavedChange = true;

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: !_hasUnSavedChange,
      onPopInvoked: (bool didPop) async {
        if (didPop) return;
        // 拦截后执行业务判断,例如弹出离开确认弹窗
        final bool isConfirmLeave = await showDialog<bool>(
          context: context,
          builder: (dialogContext) => AlertDialog(
            title: const Text('离开提示'),
            content: const Text('当前有未保存的修改,确定要离开当前页面吗?'),
            actions: [
              TextButton(
                onPressed: () => Navigator.pop(dialogContext, false),
                child: const Text('继续编辑'),
              ),
              TextButton(
                onPressed: () => Navigator.pop(dialogContext, true),
                child: const Text('确认离开'),
              ),
            ],
          ),
        ) ?? false;

        // 用户确认离开时,手动执行pop操作
        if (isConfirmLeave && mounted) {
          Navigator.pop(context);
        }
      },
      child: Scaffold(
        appBar: AppBar(title: const Text('内容编辑页')),
        body: const Center(child: Text('编辑区域')),
      ),
    );
  }
}

使用注意:

  • 业务状态变更时(比如用户点击保存按钮),调用setState更新_hasUnSavedChange为false,canPop会自动变为true,后续pop操作将直接放行
  • 不要在onPopInvoked中未判断didPop就直接执行pop,避免重复弹出路由报错

2. 低版本Flutter兼容方案(3.12版本以下)

Flutter 3.12以下版本可以使用WillPopScope组件实现拦截,核心是通过onWillPop回调的返回值控制是否放行,返回true允许离开,返回false拦截操作。
代码示例:

@override
Widget build(BuildContext context) {
  return WillPopScope(
    onWillPop: () async {
      // 执行业务判断,例如弹出确认框
      final bool isConfirmLeave = await showDialog<bool>(
        context: context,
        builder: (context) => /* 同上的确认弹窗逻辑 */,
      ) ?? false;
      return isConfirmLeave;
    },
    child: Scaffold(/* 页面内容 */),
  );
}

注意:该方案默认会禁用iOS的侧滑返回手势,如需保留需要额外做手势适配,优先建议升级Flutter版本使用PopScope。


3. 拦截主动代码触发的路由跳转

上述两种方案仅能拦截pop类的离开行为,对于代码主动调用Navigator.push、pushReplacement、pushNamed等方法触发的跳转,需要通过统一路由入口拦截:

  • 封装项目全局的路由跳转方法,所有页面跳转必须走封装方法,在方法内提前判断当前页面的拦截状态,不符合放行条件时终止跳转
  • 如果使用go_router、auto_route等三方路由框架,可直接使用框架提供的路由拦截回调,在跳转前执行校验逻辑

简单的封装示例:

/// 统一路由跳转封装
Future<T?>? appPush<T>(BuildContext context, Route<T> targetRoute) async {
  // 读取当前页面的拦截状态,例如获取编辑页的未保存标识
  final editPageState = context.findAncestorStateOfType<_EditPageState>();
  if (editPageState?._hasUnSavedChange == true) {
    final confirm = await showDialog<bool>(
      context: context,
      builder: (context) => /* 离开确认弹窗 */,
    ) ?? false;
    if (!confirm) return null;
  }
  // 校验通过后执行跳转
  return Navigator.push(context, targetRoute);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 00:21:14