Skip to content

权限管理

移动应用需要获取用户的授权才能使用某些设备功能(如相机、定位、存储等)。不同平台的权限机制不同,需要分别配置。

为什么需要权限

移动操作系统为了保护用户隐私,要求 App 在使用敏感功能前必须声明权限并获得用户授权。如果不在配置文件中声明权限,相关功能将无法使用甚至导致崩溃。

三大平台安全模型

平台权限声明位置运行时请求说明
AndroidAndroidManifest.xml部分(危险权限需运行时请求)权限分普通和危险两类
iOSInfo.plist是(必须)每种隐私访问都需描述用途
鸿蒙module.json5类似 Android 但有独立权限体系

Android 权限配置

1. 在 AndroidManifest.xml 中声明权限

文件位置:android/app/src/main/AndroidManifest.xml

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 普通权限(安装时自动授予) -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <!-- 危险权限(需运行时请求) -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

2. 运行时请求危险权限

使用 permission_handler 插件:

bash
flutter pub add permission_handler
dart
import 'package:permission_handler/permission_handler.dart';

// 请求相机权限
Future<bool> requestCameraPermission() async {
  // ─── ★ 运行时请求权限 ──────────────
  final status = await Permission.camera.request();
  // ─── ☆ 运行时请求权限 ──────────────

  // ─── ★ 权限已授予 ──────────────
  if (status.isGranted) {
    return true;
  } else if (status.isDenied) {
    // 用户拒绝,可以再次请求
    return false;
  } else if (status.isPermanentlyDenied) {
    // 用户永久拒绝,需要引导去设置页
    await openAppSettings();
    return false;
  }
  // ─── ☆ 权限已授予 ──────────────
  return false;
}

// 检查权限状态
Future<bool> hasCameraPermission() async {
  final status = await Permission.camera.status;
  return status.isGranted;
}

Android 危险权限分组

权限组权限
位置ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
相机CAMERA
存储READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE
麦克风RECORD_AUDIO
联系人READ_CONTACTS, WRITE_CONTACTS
电话READ_PHONE_STATE, CALL_PHONE
短信SEND_SMS, READ_SMS
通知POST_NOTIFICATIONS (Android 13+)

iOS 权限配置

在 Info.plist 中添加隐私描述

文件位置:ios/Runner/Info.plist

xml
<dict>
    <!-- 相机 -->
    <key>NSCameraUsageDescription</key>
    <string>需要访问相机以拍摄照片</string>

    <!-- 相册 -->
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要访问相册以选择照片</string>

    <!-- 定位(使用时) -->
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>需要获取位置信息以提供附近服务</string>

    <!-- 定位(始终) -->
    <key>NSLocationAlwaysUsageDescription</key>
    <string>需要始终获取位置信息以提供导航服务</string>

    <!-- 麦克风 -->
    <key>NSMicrophoneUsageDescription</key>
    <string>需要访问麦克风以录音</string>

    <!-- 通讯录 -->
    <key>NSContactsUsageDescription</key>
    <string>需要访问通讯录以选择联系人</string>

    <!-- 通知 -->
    <key>UIBackgroundModes</key>
    <array>
        <string>remote-notification</string>
    </array>
</dict>

重要

iOS 审核要求每个隐私权限必须有清晰的用途描述。如果描述模糊或与功能无关,会被拒审。


鸿蒙权限配置

在 module.json5 中声明权限

文件位置:ohos/entry/src/main/module.json5

json
{
  "requestPermissions": [
    { "name": "ohos.permission.INTERNET" },
    { "name": "ohos.permission.GET_WIFI_INFO" },
    { "name": "ohos.permission.APPROXIMATELY_LOCATION" },
    { "name": "ohos.permission.LOCATION" },
    { "name": "ohos.permission.CAMERA" }
  ]
}

鸿蒙权限分类

类型说明示例
normal安装时自动授予INTERNET, GET_WIFI_INFO
system_basic需用户授权LOCATION, CAMERA
system_core仅系统应用极少使用

常见权限场景速查

功能Android 权限iOS Key鸿蒙权限
网络请求INTERNET无需ohos.permission.INTERNET
相机CAMERANSCameraUsageDescriptionohos.permission.CAMERA
相册读取READ_EXTERNAL_STORAGENSPhotoLibraryUsageDescription-
定位ACCESS_FINE_LOCATIONNSLocationWhenInUseUsageDescriptionohos.permission.LOCATION
麦克风RECORD_AUDIONSMicrophoneUsageDescription-
通知POST_NOTIFICATIONSBackground Modes-
文件读写READ/WRITE_EXTERNAL_STORAGE无需(沙盒)-
生物识别USE_BIOMETRICNSFaceIDUsageDescription-

权限被拒绝后的处理策略

dart
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

class PermissionExamplePage extends StatelessWidget {
  const PermissionExamplePage({super.key});

  Future<void> requestWithFallback(BuildContext context, Permission permission) async {
    // ─── ★ 通用权限请求封装 ──────────────
    final status = await permission.request();
    // ─── ☆ 通用权限请求封装 ──────────────

    if (status.isGranted) {
      // 权限已授予,执行功能
      // ─── ★ 异步操作后检查 mounted ──────────────
      if (context.mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('权限已授予')),
        );
      }
      // ─── ☆ 异步操作后检查 mounted ──────────────
    } else if (status.isDenied) {
      // 用户拒绝了,但可以再次请求
      // 展示为什么需要此权限的说明,然后再次请求
    } else if (status.isPermanentlyDenied) {
      // 用户选择了"不再询问",需要引导去设置
      if (context.mounted) {
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            title: const Text('需要权限'),
            content: const Text('请在设置中开启权限以使用此功能'),
            actions: [
              TextButton(
                onPressed: () => Navigator.pop(context),
                child: const Text('取消'),
              ),
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  openAppSettings();
                },
                child: const Text('去设置'),
              ),
            ],
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('权限管理')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => requestWithFallback(context, Permission.camera),
          child: const Text('请求相机权限'),
        ),
      ),
    );
  }
}

最佳实践

  1. 在使用功能前请求权限,不要一次性请求所有权限
  2. 被拒绝后解释为什么需要此权限
  3. 永久拒绝后引导用户去系统设置
  4. 权限被拒绝时提供降级功能(如无相机时选择相册)

下一步

基于 Flutter 官方文档整理