Flutter 3.13.2下Android/iOS下载并打开PDF等文件的方案求助
Flutter跨平台下载并打开PDF文件的可行方案
针对你遇到的flutter_downloader不支持API33、background_downloader不稳定、不想冗余引入Dio的问题,以下是几个稳定可控的解决方案:
方案一:基于官方HTTP包+路径管理+文件打开工具(无冗余依赖)
如果你的项目已经使用Flutter官方http包做网络请求,这个方案完全没有额外冗余,逻辑透明可控,适合前台下载场景。
实现步骤:
- 依赖准备:添加
path_provider(获取设备存储路径)和open_filex(跨平台打开本地文件)到pubspec.yaml - 核心代码:
import 'dart:io'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:open_filex/open_filex.dart'; Future<void> downloadAndOpenPdf(String pdfUrl) async { try { // 1. 获取PDF字节流 final response = await http.get(Uri.parse(pdfUrl)); if (response.statusCode != 200) { throw Exception('下载失败,状态码:${response.statusCode}'); } // 2. 确定存储路径(适配Android/iOS) Directory? targetDir; if (Platform.isAndroid) { // 存到公共Downloads目录,让系统文件应用可见 targetDir = await getExternalStoragePublicDirectory(StorageDirectory.downloads); } else if (Platform.isIOS) { // 存到应用文档目录,需配置Info.plist让文件应用可访问 targetDir = await getApplicationDocumentsDirectory(); } if (targetDir == null) throw Exception('无法获取有效存储目录'); // 3. 写入文件 final fileName = pdfUrl.split('/').last; final saveFile = File('${targetDir.path}/$fileName'); await saveFile.writeAsBytes(response.bodyBytes); // 4. 打开文件 await OpenFilex.open(saveFile.path); } catch (e) { print('操作失败:$e'); // 这里可以添加用户提示逻辑 } }
平台适配配置:
- Android:
在AndroidManifest.xml中添加权限(API33+适配):<!-- 访问公共媒体文件 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 若需写入公共Downloads目录,API30+需申请此权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" tools:ignore="ScopedStorage" /> - iOS:
在Info.plist中添加配置,让文件应用可访问应用目录:<key>UIFileSharingEnabled</key> <true/> <key>LSSupportsOpeningDocumentsInPlace</key> <true/> <key>NSDocumentsFolderUsageDescription</key> <string>需要访问文档目录保存下载的PDF文件</string>
优缺点:
- ✅ 无冗余依赖(若已用官方http)、稳定可控、代码逻辑清晰
- ❌ 需要自行处理下载进度、错误重试、后台下载等进阶逻辑
方案二:前台下载+本地通知(适配后台需求)
如果需要后台下载能力和进度通知,可以结合flutter_local_notifications包,用Isolate处理下载任务避免阻塞UI。
核心逻辑:
- 初始化通知通道
- 启动Isolate执行下载任务,实时发送进度到主Isolate
- 更新通知显示进度,下载完成后触发文件打开
关键代码片段:
import 'dart:isolate'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); // 初始化通知 Future<void> _initNotifications() async { const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initSettings = InitializationSettings(android: androidInit); await _notificationsPlugin.initialize(initSettings); } // Isolate内执行下载任务 Future<void> _downloadTask(List<dynamic> args) async { final String pdfUrl = args[0]; final SendPort sendPort = args[1]; final response = await http.get(Uri.parse(pdfUrl), onReceiveProgress: (count, total) { if (total != -1) { sendPort.send((count / total * 100).toInt()); } }); // 写入文件逻辑同方案一 // ... sendPort.send('completed'); } // 发起带通知的下载 Future<void> downloadWithNotification(String pdfUrl) async { await _initNotifications(); const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'download_channel', '下载通知', channelDescription: 'PDF文件下载进度', importance: Importance.max, priority: Priority.high, showProgress: true, maxProgress: 100); const NotificationDetails notificationDetails = NotificationDetails(android: androidDetails); // 启动Isolate执行下载 final ReceivePort receivePort = ReceivePort(); await Isolate.spawn(_downloadTask, [pdfUrl, receivePort.sendPort]); // 监听进度更新 receivePort.listen((message) { if (message is int) { _notificationsPlugin.show( 0, '正在下载PDF', '进度:$message%', notificationDetails); } else if (message == 'completed') { _notificationsPlugin.show( 0, '下载完成', 'PDF已保存到设备', notificationDetails); // 打开文件逻辑 // ... } }); }
优缺点:
- ✅ 支持后台下载、进度通知,依赖包轻量成熟
- ❌ 需要处理通知权限、Isolate通信等额外逻辑
方案三:原生通道实现(零第三方依赖)
如果对第三方依赖极度敏感,且具备原生开发能力,可以直接调用Android/iOS的原生下载API,完全可控且符合平台规范。
实现思路:
- Android:调用系统
DownloadManager,配置下载请求到公共Downloads目录,通过广播监听下载完成状态,再通知Flutter打开文件 - iOS:使用
NSURLSession创建下载任务,保存到应用文档目录,通过回调通知Flutter下载完成
优缺点:
- ✅ 零第三方依赖、完全遵循平台规范、稳定可靠
- ❌ 需要编写原生代码,维护成本较高
内容的提问来源于stack exchange,提问作者Praveen Bhusal
相关产品推荐
相关产品推荐

