Flutter 存储权限:适配主流系统
简述
你提供的这套 Flutter 存储权限申请代码和配置,核心逻辑是对的,但缺少关键细节、异常处理、全场景适配(比如 Android 11/12 的特殊逻辑、iOS 细分权限、权限组申请等),我会补充完整的细节,让代码更健壮、适配所有主流系统版本(Android 10-14、iOS 11-17)。
一、补充 pubspec.yaml 依赖(完整)
除了 permission_handler,device_info_plus 是代码中依赖的(判断 Android 版本),需要一起添加,同时指定稳定版本:
1 | dependencies: |
添加后执行:
flutter pub get
Android 配置
- AndroidManifest.xml 完整配置(分场景)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<!-- 基础配置:适配所有Android版本 -->
<manifest ...>
<!-- 1. 兼容 Android 10 分区存储(可选,关闭Scoped Storage) -->
<!-- 如果需要"访问所有文件",建议保留;仅访问媒体文件可删除 -->
<application
...
android:requestLegacyExternalStorage="true"> <!-- Android 10 适配 -->
</application>
<!-- 2. 媒体文件权限(基础,所有版本都加) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/> <!-- Android 10+ 自动失效 -->
<!-- 3. Android 13+ 媒体细分权限(必须) -->
<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"/>
<!-- 4. Android 11+ 访问所有文件(仅当需要读写非媒体文件时添加) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
</manifest>
关键补充说明:
android:requestLegacyExternalStorage=”true”:仅对 Android 10(SDK 29)生效,关闭分区存储,兼容旧版权限逻辑;Android 11+ 需用 MANAGE_EXTERNAL_STORAGE。
WRITE_EXTERNAL_STORAGE 加 maxSdkVersion=”28”:避免 Android 10+ 重复申请(系统自动忽略)。
MANAGE_EXTERNAL_STORAGE 是特殊权限:无法通过 permission_handler 直接申请,必须引导用户到系统设置页开启(下面代码会补充)。
- Android 14 额外适配(targetSdk 34)
如果你的 build.gradle 中 targetSdkVersion = 34,需添加:
1 | <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"/> |
Ios 配置
- Info.plist 完整配置(覆盖全场景)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<dict>
<!-- 相册读取(基础) -->
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册以选择图片/视频</string>
<!-- 相册写入(iOS 11+) -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要将图片/视频保存到相册</string>
<!-- iOS 14+ 选择性相册权限(用户可只授权部分照片) -->
<key>NSPhotoLibraryLimitedUsageDescription</key>
<string>需要访问您选择的照片/视频</string>
<!-- 本地文件访问(如Documents目录,iOS 11+) -->
<key>NSDocumentsFolderUsageDescription</key>
<string>需要访问本地文件以管理您的资料</string>
<!-- 可选:如果需要访问iCloud文件 -->
<key>NSUbiquitousContainersUsageDescription</key>
<string>需要访问iCloud文件以同步您的资料</string>
</dict>
关键补充:
NSPhotoLibraryLimitedUsageDescription:iOS 14+ 新增,用户点击“选择照片”时显示,必须添加,否则权限申请会失败。
描述文案不能为空/太简单:App Store 审核会拒绝(比如不能只写“访问相册”,要说明用途)。
权限管理
1 | import 'dart:io'; |
代码关键补充点:
异常捕获:添加 try-catch 捕获 PlatformException(插件调用异常),避免崩溃。
Android 11+ 所有文件权限:MANAGE_EXTERNAL_STORAGE 是特殊权限,必须引导用户到设置页开启,无法直接申请。
iOS 14+ 有限权限:处理 isLimited 状态(用户只授权部分照片),这是 iOS 14+ 新增的权限类型。
权限组申请:Android 13+ 拆分了媒体权限(照片/视频/音频),按需申请更合规。
工具方法:新增 checkStoragePermission,方便提前检查权限状态。
五、使用示例(完整流程)
1 | // 点击按钮申请权限 |
避坑指南
- Android 11+ MANAGE_EXTERNAL_STORAGE 审核:
Google Play 审核严格,仅允许文件管理器、备份类 App 使用该权限,普通 App 申请会被拒;
优先使用分区存储(Scoped Storage),仅在必要时申请该权限。
- iOS 权限描述文案:
必须真实描述用途,不能夸大(比如“访问相册用于展示头像”,而不是“访问相册”);
缺少文案会导致 App 崩溃。
- 权限申请时机:
- 不要一启动 App 就申请,要在用户触发操作时(比如点击“选择照片”)再申请,提升授权率。
- targetSdkVersion 适配:
- 建议设置为 34(Android 14),并测试所有权限逻辑,避免版本兼容问题。
总结
配置层面:Android 需区分 SDK 版本添加权限,iOS 必须补充完整的权限描述文案;
代码层面:核心是处理 Android 13+ 媒体细分权限、Android 11+ 所有文件权限、iOS 14+ 有限相册权限;
体验层面:权限申请要在用户触发操作时进行,拒绝后引导到设置页,同时做好异常捕获。
