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

Flutter Web中弹窗显示时如何处理浏览器返回按钮?

解决方案:拦截GoRouter路由pop以关闭弹窗

问题根源:浏览器返回按钮触发的是GoRouter管理的路由栈pop,而弹窗属于Flutter本地Navigator的弹窗,不在GoRouter的路由栈中。你之前将PopScope包裹在弹窗内部,只能监听弹窗自身的本地pop操作(如点击关闭按钮),无法拦截GoRouter的路由级pop事件。

修改方案

在Page2页面外层使用PopScope,监听路由级别的pop请求,判断弹窗是否处于打开状态:

  • 若弹窗打开:优先关闭弹窗,阻止路由返回
  • 若弹窗关闭:正常执行路由返回

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final GoRouter _router = GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const Page1(),
      ),
      GoRoute(
        path: '/page2',
        builder: (context, state) => const Page2(),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Page 1')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            context.go('/page2');
          },
          child: const Text('goto Page 2'),
        ),
      ),
    );
  }
}

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

  @override
  State<Page2> createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  // 标记弹窗是否打开
  bool _isDialogOpen = false;

  @override
  Widget build(BuildContext context) {
    // 在页面外层包裹PopScope,监听路由级pop事件
    return PopScope(
      // 弹窗打开时,禁止路由pop
      canPop: !_isDialogOpen,
      onPopInvokedWithResult: (didPop, result) {
        if (_isDialogOpen) {
          // 关闭弹窗
          Navigator.of(context).pop();
          setState(() {
            _isDialogOpen = false;
          });
        } else {
          // 正常返回上路由
          context.pop();
        }
      },
      child: Scaffold(
        appBar: AppBar(title: const Text('Page 2')),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              setState(() {
                _isDialogOpen = true;
              });
              // 等待弹窗关闭后更新状态
              await showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    title: const Text('Dialog'),
                    content: const Text('Dialog content'),
                    actions: [
                      TextButton(
                        onPressed: () {
                          Navigator.of(context).pop();
                          setState(() {
                            _isDialogOpen = false;
                          });
                          print('Close button clicked');
                        },
                        child: const Text('Close'),
                      ),
                    ],
                  );
                },
              );
              // 弹窗关闭后确保状态更新(处理点击弹窗外部关闭的情况)
              setState(() {
                _isDialogOpen = false;
              });
            },
            child: const Text('Open dialog'),
          ),
        ),
      ),
    );
  }
}

关键修改点

  1. 将Page2改为StatefulWidget,添加_isDialogOpen状态标记弹窗状态
  2. 在Page2外层包裹PopScope,监听路由级pop事件
  3. 弹窗打开时设置canPop: false,阻止路由返回;关闭时恢复为true
  4. 在onPopInvokedWithResult中判断弹窗状态,优先关闭弹窗
  5. 处理弹窗关闭后的状态更新(包括点击关闭按钮和点击弹窗外部关闭的情况)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 21:54:56