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

Flutter Web中用Go Router实现浏览器回退/刷新确认弹窗

Flutter Web + GoRouter:实现初始路由返回/刷新的退出确认弹窗

针对你遇到的GoRouter下WillPopScope无效、onBeforeUnload在Safari适配性差的问题,可以通过GoRouter路由监听+浏览器原生事件结合的方案解决,同时覆盖返回按钮、刷新、页面关闭等场景,适配Chrome和Safari(包括iOS Safari)。

方案实现步骤

1. 自定义路由观察者监听初始路由状态

创建NavigatorObserver子类,实时判断当前是否处于初始路由,动态开启/关闭浏览器事件监听:

import 'dart:js' as js;
import 'dart:html' as html;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

class PopConfirmObserver extends NavigatorObserver {
  bool _isOnInitialRoute = false;
  // 替换成你的初始路由路径
  final String initialRoutePath = '/home';

  @override
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute);
    _updateInitialRouteState();
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    _updateInitialRouteState();
  }

  @override
  void didReplace({Route? newRoute, Route? oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    _updateInitialRouteState();
  }

  void _updateInitialRouteState() {
    final currentPath = GoRouter.of(navigator!.context).location;
    _isOnInitialRoute = currentPath == initialRoutePath;
    _setupBrowserEventListeners();
  }

  void _setupBrowserEventListeners() {
    if (!kIsWeb) return;

    // 先移除旧监听,避免重复绑定
    js.context['onpopstate'] = null;
    html.window.removeEventListener('beforeunload', _beforeUnloadHandler);
    html.window.removeEventListener('visibilitychange', _visibilityChangeHandler);

    if (_isOnInitialRoute) {
      // 监听浏览器返回按钮(popstate事件)
      js.context['onpopstate'] = (js.JsEvent event) {
        if (!html.window.confirm('确定要退出吗?')) {
          // 用户取消返回,恢复历史状态阻止页面跳转
          js.context.callMethod('history', ['pushState'], [null, '', html.window.location.href]);
        } else {
          // 用户确认返回,清理冗余历史记录
          js.context.callMethod('history', ['replaceState'], [null, '', html.window.location.href]);
        }
      };

      // 监听页面刷新/关闭(beforeunload事件)
      html.window.addEventListener('beforeunload', _beforeUnloadHandler);

      // 补充适配iOS Safari的visibilitychange事件(部分版本beforeunload不触发)
      html.window.addEventListener('visibilitychange', _visibilityChangeHandler);
    }
  }

  void _beforeUnloadHandler(html.Event event) {
    event.preventDefault();
    // 部分浏览器需要设置returnValue才会弹出确认框
    (event as html.BeforeUnloadEvent).returnValue = '确定要退出吗?';
  }

  void _visibilityChangeHandler(html.Event event) {
    if (html.document.visibilityState == 'hidden') {
      // iOS Safari切换标签/返回时触发,弹窗提示
      html.window.confirm('确定要退出吗?');
    }
  }
}

2. 给GoRouter添加观察者

在GoRouter初始化时,将自定义观察者加入observers列表:

final GoRouter router = GoRouter(
  observers: [PopConfirmObserver()],
  routes: [
    GoRoute(
      path: '/home',
      builder: (context, state) => const HomeRoute(),
    ),
    // 其他路由配置...
  ],
);

关键细节说明

  • 适配Safari返回按钮:通过popstate事件监听浏览器返回动作,手动弹出确认框,用户取消时用history.pushState恢复历史状态,阻止页面跳转。
  • 处理刷新/关闭:保留beforeunload事件处理,同时设置returnValue确保在多数浏览器中触发弹窗。
  • iOS Safari补充:部分iOS Safari版本不触发beforeunload,通过visibilitychange事件监听页面隐藏状态,补充弹窗提示(虽然无法完全阻止页面关闭,但能给用户明确提示)。
  • 路由切换自动启停:通过NavigatorObserver监听路由变化,仅在初始路由时开启监听,避免其他路由出现不必要的弹窗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:45:53