Android 11+下Flutter用dio下载PDF至Downloads文件夹并操作的方法
Dio下载PDF到Downloads文件夹的权限与操作解决方案
解决重复文档树弹窗问题
你用shared_storage时PersistedUris失效,大概率是没走对持久化流程:
- 第一次调用
openDocumentTree时,必须让用户选中整个Downloads目录(别选子文件夹),这是获取持久化权限的前提 - 选中后立刻调用
persistUriPermission把Uri存下来,之后每次启动App用getPersistedUriPermissions取已授权的Uri,就不用再弹文档树了 - 代码示例:
// 首次授权流程 final Uri? downloadsUri = await openDocumentTree(initialUri: downloadsDirectoryUri); if (downloadsUri != null) { await persistUriPermission(downloadsUri); } // 后续直接用持久化的Uri final persistedUris = await getPersistedUriPermissions(); if (persistedUris.isNotEmpty) { final targetUri = persistedUris.first; // 用这个Uri操作Downloads下的文件/文件夹 }
无需用户手动创建子文件夹:直接写入Downloads
Android允许应用直接往Downloads目录写文件,不用用户手动创建以应用命名的子文件夹,结合Dio的实现步骤:
- 先用Dio把PDF下载到App的临时目录
- 通过MediaStore把文件复制到Downloads目录,同时拿到可用于后续操作的ContentUri
- 后续查看、删除都用这个ContentUri即可
- 代码示例:
import 'dart:io'; import 'package:dio/dio.dart'; import 'package:shared_storage/shared_storage.dart'; import 'package:path_provider/path_provider.dart'; Future<void> downloadAndSaveToDownloads(String pdfUrl, String fileName) async { // 1. 下载到临时目录 final tempDir = await getTemporaryDirectory(); final tempFile = File('${tempDir.path}/$fileName'); await Dio().download(pdfUrl, tempFile.path); // 2. 写入Downloads目录 final contentUri = await insertMedia( DownloadsCollection.uri, { DownloadsCollection.displayName: fileName, DownloadsCollection.mimeType: 'application/pdf', DownloadsCollection.isPending: false, }, sourceFile: tempFile, ); // 3. 后续操作示例:删除文件 await deleteMedia(contentUri); // 查看可通过contentUri调用系统文件查看器 }
替代插件与方案
如果shared_storage不好用,可以换这些思路:
- 试试
media_store插件:专门针对MediaStore操作,不用SAF就能往Downloads写文件,权限处理更简洁 - 自定义原生工具类:直接调用Android的SAF API处理持久化权限,避开插件封装的坑
关键注意事项
- Android 13+中,应用访问自己写入Downloads的文件无需额外权限;针对Android 12及以下,需在
AndroidManifest.xml中申请READ_EXTERNAL_STORAGE权限 - 若必须创建应用名子文件夹,首次拿到Downloads的持久化Uri后,用
createDirectory方法在其下创建子文件夹,后续直接操作该子文件夹的Uri即可
内容的提问来源于stack exchange,提问作者LordShrunkenant
相关产品推荐
相关产品推荐

