Flutter WebView中PDF文件无法下载的问题求助
解决Flutter WebView中PDF无法下载的问题
嘿,我懂你的困扰——WebView里点PDF链接完全没反应对吧?这是因为默认的WebView并不会自动处理下载请求,咱们得手动拦截这些请求,再用专门的工具来完成下载操作。下面是具体的解决步骤和修改后的代码:
步骤1:添加必要依赖
先在你的pubspec.yaml里加上这几个依赖,用来处理下载、文件路径和权限请求:
dependencies: flutter: sdk: flutter webview_flutter: ^4.4.0 # 建议用最新稳定版 flutter_downloader: ^1.10.4 path_provider: ^2.1.1 permission_handler: ^11.0.1
然后运行flutter pub get安装这些依赖。
步骤2:配置平台权限
- Android端:在
android/app/src/main/AndroidManifest.xml里添加权限和下载服务:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <!-- 在application标签内添加 --> <service android:name="vn.hunghd.flutterdownloader.DownloadService" android:permission="android.permission.FOREGROUND_SERVICE" android:exported="false"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/> </intent-filter> </service>
- iOS端:在
ios/Runner/Info.plist里添加权限描述:
<key>NSDownloadsUsageDescription</key> <string>需要访问下载目录保存PDF文件</string> <key>NSFileProviderDomainUsageDescription</key> <string>需要访问文件系统存储下载的PDF</string>
步骤3:修改WebView代码(整合下载逻辑)
下面是完整的修改后代码,关键部分我都标出来了:
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'LoadingIndicator.dart'; class Row_Element_2 extends StatefulWidget { final String url; const Row_Element_2({ Key? key, required this.url, }) : super(key: key); @override State<Row_Element_2> createState() => _Row_Element_2State(); } class _Row_Element_2State extends State<Row_Element_2> { bool isLoading = true; final _key = UniqueKey(); @override void initState() { super.initState(); if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); // 初始化下载器并请求权限 _setupDownloadEnv(); } // 初始化下载环境+请求权限 Future<void> _setupDownloadEnv() async { await FlutterDownloader.initialize( debug: true, // 开发阶段可开启调试日志 ignoreSsl: false, // 生产环境建议关闭,除非链接证书有问题 ); // 安卓请求存储权限 if (Platform.isAndroid) { final status = await Permission.storage.request(); if (status.isDenied) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('需要存储权限才能下载PDF哦')), ); } } } // 处理PDF下载 Future<void> _startPdfDownload(String pdfUrl) async { // 获取设备的下载目录 final directory = Platform.isAndroid ? await getExternalStorageDirectory() : await getApplicationDocumentsDirectory(); // 发起下载请求 await FlutterDownloader.enqueue( url: pdfUrl, savedDir: directory!.path, showNotification: true, // 显示下载进度通知 openFileFromNotification: true, // 下载完成后可从通知打开文件 ); } @override Widget build(BuildContext context) { return SafeArea( child: Stack( children: [ WebView( key: _key, initialUrl: widget.url, javascriptMode: JavascriptMode.unrestricted, onPageFinished: (finish) { setState(() { isLoading = false; }); }, // 核心:拦截导航请求,识别PDF链接 navigationDelegate: (NavigationRequest request) { // 判断是否为PDF链接(可根据实际链接格式调整判断逻辑) if (request.url.endsWith('.pdf')) { _startPdfDownload(request.url); return NavigationDecision.prevent; // 阻止WebView默认跳转 } // 其他请求正常放行 return NavigationDecision.navigate; }, ), isLoading ? Container(child: LoadingIndicator()) : const SizedBox.shrink(), ], ), ); } }
关键逻辑说明
- navigationDelegate:这个回调会拦截WebView的所有导航请求,我们在这里判断如果是
.pdf结尾的链接,就触发下载逻辑,同时阻止WebView的默认跳转行为。 - FlutterDownloader:专门的下载插件,支持后台下载、进度通知、下载完成后打开文件等功能,比自己实现下载逻辑更稳定可靠。
- 权限处理:安卓需要存储权限才能保存文件,iOS需要访问下载目录的权限,所以初始化阶段要主动请求权限,避免下载失败。
这样修改后,用户在WebView里点击PDF链接时,就会自动触发下载,还能在通知栏看到进度,下载完成后直接从通知打开文件就行。
内容的提问来源于stack exchange,提问作者Dhruv Mavani




