Flutter WebView遇URL重定向时返回循环的问题解决
问题分析与解决方案
当WebView加载带重定向的URL时,默认会将**重定向源页面(firstpage.html)和目标页面(secondpage.html)**都加入浏览器历史栈。而常规浏览器会自动优化这种场景,只保留最终的目标页面在历史栈中,因此返回时直接退出,不会回到源页面。你的WebView出现循环问题,核心原因就是历史栈中残留了重定向源页面,返回时触发二次重定向。
同时你的代码存在一个明显问题:重复调用setNavigationDelegate,第二次调用会完全覆盖第一次的配置,导致之前的导航规则失效。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() => runApp(const MaterialApp(home: WebViewExample())); class WebViewExample extends StatefulWidget { const WebViewExample({super.key}); @override State<WebViewExample> createState() => _WebViewExampleState(); } class _WebViewExampleState extends State<WebViewExample> { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.green, body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const WebViewExample1()), ); }, child: const Text("open web view"), ) ], ), ); } } class WebViewExample1 extends StatefulWidget { const WebViewExample1({super.key}); @override State<WebViewExample1> createState() => _WebViewExample1State(); } class _WebViewExample1State extends State<WebViewExample1> { late final WebViewController _controller; // 记录初始加载的重定向源页面URL final String _initialRedirectUrl = 'http://sample.com/firstpage.html'; // 标记是否已处理过重定向历史清理 bool _hasCleanedHistory = false; @override void initState() { super.initState(); _loadPageFn2(); } void _loadPageFn2() { final WebViewController controller = WebViewController(); controller ..setJavaScriptMode(JavaScriptMode.unrestricted) // 合并NavigationDelegate,避免重复设置导致配置覆盖 ..setNavigationDelegate( NavigationDelegate( onProgress: (int progress) { debugPrint('WebView is loading (progress : $progress%)'); }, onPageStarted: (String url) { debugPrint('Page started loading: $url'); }, onPageFinished: (String url) async { debugPrint('Page finished loading: $url'); // 当页面加载完成,且当前页面是重定向目标,且未清理过历史时执行操作 if (url != _initialRedirectUrl && !_hasCleanedHistory) { // 移除重定向源页面的历史条目 await controller.removeHistoryEntry(); setState(() { _hasCleanedHistory = true; }); } }, onWebResourceError: (WebResourceError error) { debugPrint(''' Page resource error: code: ${error.errorCode} description: ${error.description} errorType: ${error.errorType} isForMainFrame: ${error.error.isForMainFrame} '''); }, onNavigationRequest: (NavigationRequest request) { if (request.url.startsWith('https://www.youtube.com/')) { debugPrint('blocking navigation to ${request.url}'); return NavigationDecision.prevent; } debugPrint('allowing navigation to ${request.url}'); return NavigationDecision.navigate; }, onUrlChange: (UrlChange change) { debugPrint('url change to ${change.url}'); }, ), ) ..loadRequest(Uri.parse(_initialRedirectUrl)); _controller = controller; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.green, body: WillPopScope( onWillPop: () async { if (await _controller.canGoBack()) { _controller.goBack(); return false; } else { return true; } }, child: WebViewWidget(controller: _controller), ), ); } }
关键修改说明
- 合并NavigationDelegate配置:删除重复的
setNavigationDelegate调用,确保所有导航规则生效。 - 自动清理重定向历史:
- 用
_initialRedirectUrl记录重定向源页面地址 - 在
onPageFinished回调中判断当前页面是否为最终重定向目标,若为是则调用removeHistoryEntry()移除源页面的历史记录 - 用
_hasCleanedHistory标记,避免重复执行清理操作
- 用
- 保持返回逻辑一致性:清理历史栈后,WebView的历史栈仅保留最终页面,此时点击返回会直接退出WebView,和常规浏览器行为完全一致。
备选方案(兼容旧版本)
如果你的webview_flutter版本不支持removeHistoryEntry,可以修改返回逻辑手动跳过重定向源页面:
onWillPop: () async { final List<String> historyUrls = await _controller.getHistoryUrlList(); if (historyUrls.length > 1) { // 检测上一页是否是重定向源页面,若是则直接退出WebView if (historyUrls[historyUrls.length - 2] == _initialRedirectUrl) { return true; } else { _controller.goBack(); return false; } } else { return true; } },
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

