Flutter如何拦截WebView跳转并唤起原生浏览器打开链接
Flutter WebView 外链跳转系统浏览器实现方案
实现思路
- 拦截WebView所有页面跳转请求,过滤出外部http/https链接
- 拦截到外链时阻止WebView内部加载,弹出跳转确认提示
- 用户确认后调用系统能力唤起原生浏览器打开对应链接
具体实现步骤
1. 添加必要依赖
在项目pubspec.yaml文件中添加跳转系统浏览器所需的官方插件,如果你项目已经引入过对应版本可跳过:
dependencies: flutter: sdk: flutter # 你项目已有的webview依赖无需重复添加 webview_flutter: ^4.4.2 # 用于唤起系统浏览器 url_launcher: ^6.2.2
Android端需要在android/app/src/main/AndroidManifest.xml的<manifest>节点下添加包可见性配置,适配Android 11及以上版本:
<queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="https" /> </intent> </queries>
2. 完整修改后的代码
直接替换原有隐私政策页代码即可,核心逻辑是给WebView添加导航委托拦截跳转:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; class PrivacyPolicyPage extends StatefulWidget { const PrivacyPolicyPage({super.key}); static const routeName = '/privacy_policy'; @override State<PrivacyPolicyPage> createState() => _PrivacyPolicyPageState(); } class _PrivacyPolicyPageState extends State<PrivacyPolicyPage> { final _filePath = Assets.assetsPrivacyPolicy; late WebViewController _webViewController; @override void initState() { super.initState(); _webViewController = WebViewController() // 开启JS支持 ..setJavaScriptMode(JavaScriptMode.unrestricted) // 设置导航委托拦截跳转 ..setNavigationDelegate( NavigationDelegate( onNavigationRequest: (request) async { final url = request.url; // 本地加载的隐私政策是data URI格式,直接放行 if (url.startsWith('data:')) { return NavigationDecision.navigate; } // 其余外链全部拦截,先弹提示 final isConfirm = await showDialog<bool>( context: context, barrierDismissible: false, builder: (context) { return AlertDialog( title: const Text('跳转提示'), content: const Text('即将离开应用,跳转至系统浏览器打开第三方页面,是否继续?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('确认'), ), ], ); }, ); // 用户确认跳转则唤起系统浏览器 if (isConfirm == true) { final uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl( uri, // 强制使用外部系统浏览器打开 mode: LaunchMode.externalApplication, ); } } // 阻止WebView内部加载外链 return NavigationDecision.prevent; }, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Privacy Policy'), centerTitle: true, ), body: WebViewWidget( controller: _webViewController, ), ); } @override void didChangeDependencies() { super.didChangeDependencies(); _loadHtmlFromAssets(); } Future<void> _loadHtmlFromAssets() async { final fileHtmlContents = await rootBundle.loadString(_filePath); await _webViewController.loadRequest( Uri.dataFromString( fileHtmlContents, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'), ), ); } }
注意事项
- 代码中用
WebViewWidget替代了旧版WebView组件,适配webview_flutter4.x版本的官方写法,如果你用的是3.x旧版本,可以把导航委托逻辑放到旧版WebView的navigationDelegate参数里,逻辑完全一致 - 拦截判断逻辑以
data:开头作为本地内容放行规则,如果你后续有其他应用内页面需要在WebView里加载,可以自行扩展白名单判断 launchUrl方法必须传入LaunchMode.externalApplication参数,否则可能会在应用内打开自定义标签页,达不到唤起系统浏览器的效果
内容的提问来源于stack exchange,提问作者userName
相关产品推荐
相关产品推荐

