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
相关产品推荐
相关产品推荐

