Flutter权限管理插件custom_permission_handler的使用

Flutter权限管理插件custom_permission_handler的使用

在大多数操作系统上,权限并非在安装应用时自动授予。相反,开发者需要在应用运行时请求用户的权限。此插件提供了跨平台(iOS、Android)的API来请求权限并检查其状态。你还可以打开设备的应用设置,以便用户可以授予权限。在Android上,你可以为请求权限显示理由。

设置

虽然权限是在运行时请求的,但你仍然需要告诉操作系统你的应用可能使用哪些权限。这需要在Android和iOS特定的文件中添加权限配置。

Android

升级前1.12版本的Android项目

自4.4.0版本起,此插件使用了Flutter 1.12的Android插件API。不幸的是,这意味着应用程序开发人员也需要将他们的应用程序迁移到支持新的Android基础设施。你可以通过遵循升级前1.12 Android项目迁移指南来进行升级。未能这样做可能会导致意外行为。最常见的已知错误是调用request()方法后,权限处理程序未返回。

AndroidX

自3.1.0版本起,permission_handler插件切换到了AndroidX版本的Android支持库。这意味着你需要确保你的Android项目也升级以支持AndroidX。详细的说明可以在这里找到。

简要版本如下:

  1. 在你的gradle.properties文件中添加以下内容:
    android.useAndroidX=true
    android.enableJetifier=true
    
  2. 确保你的android/app/build.gradle文件中的compileSdkVersion设置为33:
    android {
      compileSdkVersion 33
      ...
    }
    
  3. 确保你将所有android.依赖项替换为其AndroidX对应的依赖项(完整的列表可以在这里找到)。

在你的AndroidManifest.xml文件中添加权限。有debugmainprofile版本,具体取决于你如何启动应用。通常,只需在main版本中添加权限即可。 这里是一个包含所有可能权限的示例AndroidManifest.xml

iOS

在你的Info.plist文件中添加权限。 这里是一个包含所有可能权限的示例Info.plist

重要提示:提交应用时,你必须包括所有权限选项。这是因为permission_handler插件会触及所有不同的SDK,静态代码分析器(由Apple在应用提交时运行)会检测到这一点,并在找不到匹配的权限选项时断言。更多信息可以在这里找到。

permission_handler插件使用宏来控制权限是否启用。

你必须在应用中列出要使用的权限:

  1. 在你的Podfile文件中添加以下内容:

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        ... # 这里是一些由flutter自动生成的配置
    
        # permission_handler配置开始
        target.build_configurations.each do |config|
    
          # 你可以在这里启用所需的权限。例如,要启用相机权限,
          # 只需移除前面的`#`字符,使其看起来像这样:
          #
          # ## dart: PermissionGroup.camera
          # 'PERMISSION_CAMERA=1'
          #
          # 预处理器定义可以在[这里](https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h)找到
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
            '$(inherited)',
    
            ## dart: PermissionGroup.calendar
            # 'PERMISSION_EVENTS=1',
    
            ## dart: PermissionGroup.reminders
            # 'PERMISSION_REMINDERS=1',
    
            ## dart: PermissionGroup.contacts
            # 'PERMISSION_CONTACTS=1',
    
            ## dart: PermissionGroup.camera
            # 'PERMISSION_CAMERA=1',
    
            ## dart: PermissionGroup.microphone
            # 'PERMISSION_MICROPHONE=1',
    
            ## dart: PermissionGroup.speech
            # 'PERMISSION_SPEECH_RECOGNIZER=1',
    
            ## dart: PermissionGroup.photos
            # 'PERMISSION_PHOTOS=1',
    
            ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
            # 'PERMISSION_LOCATION=1',
    
            ## dart: PermissionGroup.notification
            # 'PERMISSION_NOTIFICATIONS=1',
    
            ## dart: PermissionGroup.mediaLibrary
            # 'PERMISSION_MEDIA_LIBRARY=1',
    
            ## dart: PermissionGroup.sensors
            # 'PERMISSION_SENSORS=1',
    
            ## dart: PermissionGroup.bluetooth
            # 'PERMISSION_BLUETOOTH=1',
    
            ## dart: PermissionGroup.appTrackingTransparency
            # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
    
            ## dart: PermissionGroup.criticalAlerts
            # 'PERMISSION_CRITICAL_ALERTS=1'
          ]
    
        end 
        # permission_handler配置结束
      end
    end
    
  2. 移除你想要使用的权限前的#字符。例如,如果你需要访问日历,请确保代码看起来像这样:

    ## dart: PermissionGroup.calendar
    'PERMISSION_EVENTS=1',
    
  3. 删除Info.plist中相应的权限描述。例如,当你不需要相机权限时,只需删除NSCameraUsageDescription。 下面列出了PermissionInfo.plist键之间的关系:

    Permission Info.plist Macro
    PermissionGroup.calendar NSCalendarsUsageDescription PERMISSION_EVENTS
    PermissionGroup.reminders NSRemindersUsageDescription PERMISSION_REMINDERS
    PermissionGroup.contacts NSContactsUsageDescription PERMISSION_CONTACTS
    PermissionGroup.camera NSCameraUsageDescription PERMISSION_CAMERA
    PermissionGroup.microphone NSMicrophoneUsageDescription PERMISSION_MICROPHONE
    PermissionGroup.speech NSSpeechRecognitionUsageDescription PERMISSION_SPEECH_RECOGNIZER
    PermissionGroup.photos NSPhotoLibraryUsageDescription PERMISSION_PHOTOS
    PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse NSLocationUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription, NSLocationWhenInUseUsageDescription PERMISSION_LOCATION
    PermissionGroup.notification PermissionGroupNotification PERMISSION_NOTIFICATIONS
    PermissionGroup.mediaLibrary NSAppleMusicUsageDescription, kTCCServiceMediaLibrary PERMISSION_MEDIA_LIBRARY
    PermissionGroup.sensors NSMotionUsageDescription PERMISSION_SENSORS
    PermissionGroup.bluetooth NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription PERMISSION_BLUETOOTH
    PermissionGroup.appTrackingTransparency NSUserTrackingUsageDescription PERMISSION_APP_TRACKING_TRANSPARENCY
    PermissionGroup.criticalAlerts PermissionGroupCriticalAlerts PERMISSION_CRITICAL_ALERTS
  4. 清理并重建。

如何使用

有许多Permission。 你可以获取Permissionstatus,它可以是granteddeniedrestrictedpermanentlyDeniedlimitedprovisional

var status = await Permission.camera.status;
if (status.isDenied) {
  // 我们还没有请求权限或者权限被拒绝过,但不是永久拒绝。
}

// 你也可以直接询问权限的状态。
if (await Permission.location.isRestricted) {
  // 操作系统限制了访问,例如因为家长控制。
}

调用request()方法请求权限。如果之前已经授予过权限,则不会发生任何事情。request()返回Permission的新状态。

if (await Permission.contacts.request().isGranted) {
  // 权限要么已经被授予,要么用户刚刚授予了它。
}

// 你可以一次请求多个权限。
Map<Permission, PermissionStatus> statuses = await [
  Permission.location,
  Permission.storage,
].request();
print(statuses[Permission.location]);

一些权限,例如位置或加速度传感器权限,有一个关联的服务,可以是enableddisabled

if (await Permission.locationWhenInUse.serviceStatus.isEnabled) {
  // 使用位置。
}

你还可以打开应用设置:

if (await Permission.speech.isPermanentlyDenied) {
  // 用户选择不再看到该应用的权限请求对话框。
  // 唯一的方法是让用户手动在系统设置中启用它。
  openAppSettings();
}

在Android上,你可以为使用权限显示理由:

bool isShown = await Permission.contacts.shouldShowRequestRationale;

一些权限不会显示对话框询问用户是否允许或拒绝请求的权限。 这是因为正在检索应用的OS设置以获取相应的权限。 设置的状态将决定权限是granted还是denied

以下权限不会显示对话框:

  • 通知
  • 蓝牙

以下权限不会显示对话框,但会打开相应的设置意图,以便用户更改权限状态:

  • manageExternalStorage
  • systemAlertWindow
  • requestInstallPackages
  • accessNotificationPolicy

locationAlways权限不能直接请求,用户必须首先请求locationWhenInUse权限。 点击“仅在使用应用时允许”将给用户提供请求locationAlways权限的可能性。 这将弹出另一个权限弹窗,询问你是选择“仅在使用时保持”还是“始终允许”。

常见问题解答

请求存储权限总是返回“拒绝”在Android 13+上,我能做什么?

在Android上,Permission.storage权限与Android的READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限相关联。从Android 10(API 29)开始,READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限已被标记为弃用,并且自Android 13(API 33)起完全被禁用/禁用。

如果你的应用需要访问媒体文件,Google建议使用READ_MEDIA_IMAGESREAD_MEDIA_VIDEOSREAD_MEDIA_AUDIO权限。这些可以通过Permission.photosPermission.videosPermission.audio分别请求。要请求这些权限,请确保android/app/build.gradle文件中的compileSdkVersion设置为33

如果你的应用需要访问Android的文件系统,可以请求MANAGE_EXTERNAL_STORAGE权限(使用Permission.manageExternalStorage)。自Android 11(API 30)起,MANAGE_EXTERNAL_STORAGE权限被视为高风险或敏感权限。因此,如果你打算通过Google Play商店发布应用,需要声明使用这些权限

请求Permission.locationAlways在Android 10+(API 29+)上总是返回“拒绝”,我能做什么?

自Android 10起,应用在请求读取设备位置的权限之前,必须先获得读取设备位置的权限。当直接请求locationAlways权限或同时请求两个权限时,系统将忽略请求。因此,不要只调用Permission.location.request(),而是首先调用Permission.location.request()Permission.locationWhenInUse.request(),并获得读取GPS的权限。一旦获得此权限,你可以调用Permission.locationAlways.request()。这将为用户提供选项,以便在后台始终读取位置。有关更多信息,请访问Android文档

问题

请在我们的GitHub页面上报告任何问题、错误或功能请求。如果你需要帮助将插件集成到你的应用或服务中,商业支持是可用的。你可以通过hello@baseflow.com联系我们。

想要贡献

如果你希望为插件做出贡献(例如改进文档、解决问题或添加新功能),请仔细阅读我们的贡献指南并发送您的拉取请求

作者

此Flutter权限处理插件由Baseflow开发。你可以通过hello@baseflow.com联系他们。


完整示例Demo

以下是一个完整的示例代码,展示了如何使用custom_permission_handler插件来请求和管理权限。

示例代码

import 'dart:io';

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: PermissionExamplePage(),
    );
  }
}

class PermissionExamplePage extends StatefulWidget {
  [@override](/user/override)
  _PermissionExamplePageState createState() => _PermissionExamplePageState();
}

class _PermissionExamplePageState extends State<PermissionExamplePage> {
  PermissionStatus _cameraPermissionStatus = PermissionStatus.denied;

  [@override](/user/override)
  void initState() {
    super.initState();
    _checkCameraPermission();
  }

  Future<void> _checkCameraPermission() async {
    final status = await Permission.camera.status;
    setState(() {
      _cameraPermissionStatus = status;
    });
  }

  Future<void> _requestCameraPermission() async {
    final status = await Permission.camera.request();
    setState(() {
      _cameraPermissionStatus = status;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('权限管理示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '摄像头权限状态:',
              style: TextStyle(fontSize: 18),
            ),
            SizedBox(height: 10),
            Text(
              _cameraPermissionStatus.toString(),
              style: TextStyle(fontSize: 16, color: _getColorByPermissionStatus()),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _cameraPermissionStatus == PermissionStatus.granted
                  ? null
                  : _requestCameraPermission,
              child: Text('请求摄像头权限'),
            ),
          ],
        ),
      ),
    );
  }

  Color _getColorByPermissionStatus() {
    switch (_cameraPermissionStatus) {
      case PermissionStatus.granted:
        return Colors.green;
      case PermissionStatus.denied:
        return Colors.red;
      case PermissionStatus.restricted:
        return Colors.orange;
      case PermissionStatus.permanentlyDenied:
        return Colors.grey;
      default:
        return Colors.black;
    }
  }
}

更多关于Flutter权限管理插件custom_permission_handler的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter权限管理插件custom_permission_handler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


custom_permission_handler 是一个用于在 Flutter 应用中处理权限请求的插件。它允许开发者请求和管理 Android 和 iOS 设备上的各种权限。虽然 custom_permission_handler 并不是 Flutter 官方推荐的权限管理插件(官方推荐的是 permission_handler),但它可能是一个自定义的或社区维护的插件,用于满足特定的需求。

以下是如何在 Flutter 项目中使用 custom_permission_handler 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 custom_permission_handler 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  custom_permission_handler: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 custom_permission_handler 插件。

import 'package:custom_permission_handler/custom_permission_handler.dart';

3. 请求权限

使用 CustomPermissionHandler 来请求特定的权限。以下是一个请求相机权限的示例:

void requestCameraPermission() async {
  var status = await CustomPermissionHandler.requestPermission(Permission.camera);
  
  if (status == PermissionStatus.granted) {
    print("Camera permission granted");
  } else if (status == PermissionStatus.denied) {
    print("Camera permission denied");
  } else if (status == PermissionStatus.permanentlyDenied) {
    print("Camera permission permanently denied");
    // 你可以在这里引导用户到应用设置页面手动开启权限
  }
}

4. 检查权限状态

你还可以检查某个权限的当前状态:

void checkCameraPermission() async {
  var status = await CustomPermissionHandler.checkPermissionStatus(Permission.camera);
  
  if (status == PermissionStatus.granted) {
    print("Camera permission is granted");
  } else {
    print("Camera permission is not granted");
  }
}

5. 打开应用设置

如果用户永久拒绝了某个权限,你可以引导用户到应用设置页面手动开启权限:

void openAppSettings() async {
  await CustomPermissionHandler.openAppSettings();
}

6. 处理多个权限

你还可以一次性请求多个权限:

void requestMultiplePermissions() async {
  var statuses = await CustomPermissionHandler.requestPermissions([
    Permission.camera,
    Permission.location,
    Permission.storage,
  ]);
  
  statuses.forEach((permission, status) {
    print("$permission: $status");
  });
}

7. 处理权限请求结果

根据权限请求的结果,你可以执行不同的操作。例如,如果用户拒绝了某个权限,你可以显示一个提示信息,或者引导用户到设置页面。

8. 处理平台特定配置

在 Android 和 iOS 上,某些权限可能需要额外的配置。例如,在 Android 上,你需要在 AndroidManifest.xml 文件中声明所需的权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

在 iOS 上,你需要在 Info.plist 文件中添加相应的权限描述:

<key>NSCameraUsageDescription</key>
<string>We need access to your camera to take photos.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need access to your location to provide location-based services.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to save photos.</string>

9. 处理权限请求的回调

在某些情况下,你可能需要在权限请求完成后执行一些操作。你可以使用 then 方法来处理回调:

void requestPermissionWithCallback() async {
  await CustomPermissionHandler.requestPermission(Permission.camera).then((status) {
    if (status == PermissionStatus.granted) {
      print("Camera permission granted");
    } else {
      print("Camera permission denied");
    }
  });
}

10. 处理权限请求的异常

在请求权限时,可能会遇到异常情况。你可以使用 try-catch 块来捕获并处理这些异常:

void requestPermissionWithExceptionHandling() async {
  try {
    var status = await CustomPermissionHandler.requestPermission(Permission.camera);
    if (status == PermissionStatus.granted) {
      print("Camera permission granted");
    } else {
      print("Camera permission denied");
    }
  } catch (e) {
    print("Error requesting permission: $e");
  }
}
回到顶部