外观
第十章:权限管理(Android / iOS 差异)(别等到上线才踩坑)
10.1 本章目标(验收标准)
完成后你需要能:
- 在 Flutter 中正确申请运行时权限
- 清楚 Android 与 iOS 权限配置文件分别在哪里
- 处理“用户拒绝/永久拒绝/仅一次允许”等状态
- 给相机/相册功能准备好权限基础(为下一章做铺垫)
10.2 核心概念:权限有两层
- 声明权限(配置文件里写)
- 运行时请求(用户弹窗授权)
Web 对比:
- Web 里权限常见是浏览器 API(比如 getUserMedia),配置较少
- 移动端必须同时处理平台配置 + 运行时状态
10.3 安装 permission_handler
powershell
flutter pub add permission_handler10.4 Android 权限配置(关键)
文件位置:
android/app/src/main/AndroidManifest.xml
常见权限(相机/相册/存储):
- 相机:
android.permission.CAMERA - 相册:Android 13+ 推荐
READ_MEDIA_IMAGES,旧版本用READ_EXTERNAL_STORAGE
示例(按需添加,不要全抄):
xml
<manifest ...>
<uses-permission android:name="android.permission.CAMERA" />
<!-- Android 13+ 读取图片权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 12 及以下(某些机型/插件可能需要) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<application ...>
...
</application>
</manifest>重要提醒:
- 不要“一次性把所有权限都写上”,按功能需要最小化
- Android 13 开始媒体权限拆分:图片/视频/音频
10.5 iOS 权限配置(关键)
文件位置:
ios/Runner/Info.plist
相机/相册必须提供用途说明,否则审核/运行都可能失败。
示例:
xml
<key>NSCameraUsageDescription</key>
<string>用于拍摄笔记配图</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>用于从相册选择笔记配图</string>10.6 实战:封装一个“权限请求工具”(可复用)
目标:把权限逻辑从 UI 里抽出去。
新建:lib/permissions/permission_service.dart
dart
import 'package:permission_handler/permission_handler.dart';
class PermissionService {
const PermissionService();
Future<bool> ensureCameraPermission() async {
final status = await Permission.camera.status;
if (status.isGranted) return true;
final result = await Permission.camera.request();
return result.isGranted;
}
Future<bool> ensurePhotosPermission() async {
// iOS/Android 的“相册/照片库”在 permission_handler 里抽象为 photos
final status = await Permission.photos.status;
if (status.isGranted || status.isLimited) return true;
final result = await Permission.photos.request();
return result.isGranted || result.isLimited;
}
Future<void> openSettingsIfPermanentlyDenied() async {
await openAppSettings();
}
}Web 对比:
- 你在 Web 里常做“统一封装 fetch/权限”
- Flutter 里也一样:把平台差异收敛到 service 层
10.7 UI 里如何正确处理“拒绝/永久拒绝”
示例(伪 UI 片段):
dart
final ok = await permissionService.ensureCameraPermission();
if (!ok) {
// 给用户明确提示,并提供去设置页
// 永久拒绝(Android 勾选不再询问 / iOS 拒绝后)一般只能引导去设置
}建议交互:
- 第一次拒绝:解释用途 + 提供“再试一次”
- 永久拒绝:提供“去设置开启”按钮
10.8 实战小练习(必须做)
练习 A:权限状态页
- 新建一个页面
PermissionsDebugPage - 展示 camera/photos 权限状态(granted/denied/restricted 等)
- 提供两个按钮:请求相机权限、请求照片权限
练习 B:把权限请求接到设置页
- 设置页加一个入口:
权限管理 - 进入调试页
10.9 常见坑
- iOS 没写
Info.plist的用途说明:运行直接崩/审核被拒 - Android 13 权限变化:旧的存储权限可能无效或行为不同
- 一进入 App 就弹权限:体验差;按功能触发时再申请
- 不处理“永久拒绝”:用户一直点按钮却永远没反应
