如何在 Flutter 中请求和检查权限

Posted

技术标签:

【中文标题】如何在 Flutter 中请求和检查权限【英文标题】:How to request and check permissions in Flutter 【发布时间】:2021-02-15 03:24:01 【问题描述】:

当用户单击不允许时,我正在使用各种插件来获取用户数据、联系人、照片和相机,应用程序会静默。 当用户选中“不再询问”和“不允许”并再次进入应用程序时,我想向用户显示“询问权限”对话框。

目前,当用户选中“不允许”时,该应用不会再次询问

我知道用户必须授予应用访问个人信息的权限,包括当前位置相机日历联系人媒体库麦克风传感器语音和照片。尽管人们喜欢使用可以访问这些信息的应用程序的便利性,但他们也希望能够控制他们的私人数据。例如,人们喜欢能够自动用他们的实际位置标记照片或找到附近的朋友,但他们也希望能够选择禁用这些功能。

flutter中如何再次请求用户权限?

【问题讨论】:

【参考方案1】:

我对这个问题非常困扰在应用了几个解决方案后,我找到了这个解决方案。所以我想和大家分享一下,所以我问了这个问题,我回答了

在大多数操作系统上,权限不仅仅在安装时授予应用程序。相反,开发人员必须在应用运行时向用户请求权限。

处理权限的最佳方式是使用permission_handler 插入。此插件提供跨平台(iosandroid)API 来请求权限并检查其状态。

我们还可以打开设备的应用设置,以便用户授予权限。 在 Android 上,我们可以展示请求权限的理由。

    将此添加到您的包的pubspec.yaml 文件中:

    dependencies:
      permission_handler: ^5.0.1+1
    

    现在在你的 Dart 代码中,你可以使用:

    import 'package:permission_handler/permission_handler.dart';
    

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

    iOS

    为您的 Info.plist 文件添加权限。 Here 是一个示例 Info.plist,其中包含所有可能权限的完整列表。

    重要提示:当您想提交您的应用程序时,您必须包含所有权限选项。这是因为permission_handler 插件涉及所有不同的 SDK,并且静态代码分析器(由 Apple 在应用程序提交时运行)检测到这一点,如果在 Info.plist 中找不到匹配的权限选项,它将断言。更多相关信息请见here。

    将以下内容添加到您的 Podfile 文件中:

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        flutter_additional_ios_build_settings(target)
        target.build_configurations.each do |config|
                config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
                   '$(inherited)',
    
                   ## dart: PermissionGroup.calendar
                   'PERMISSION_EVENTS=0',
    
                   ## dart: PermissionGroup.reminders
                   'PERMISSION_REMINDERS=0',
    
                   ## dart: PermissionGroup.contacts
                   # 'PERMISSION_CONTACTS=0',
    
                   ## dart: PermissionGroup.camera
                   # 'PERMISSION_CAMERA=0',
    
                   ## dart: PermissionGroup.microphone
                   # 'PERMISSION_MICROPHONE=0',
    
                   ## dart: PermissionGroup.speech
                   'PERMISSION_SPEECH_RECOGNIZER=0',
    
                   ## dart: PermissionGroup.photos
                   # 'PERMISSION_PHOTOS=0',
    
                   ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
                   'PERMISSION_LOCATION=0',
    
                   ## dart: PermissionGroup.notification
                   # 'PERMISSION_NOTIFICATIONS=0',
    
                   ## dart: PermissionGroup.mediaLibrary
                   'PERMISSION_MEDIA_LIBRARY=0',
    
                   ## dart: PermissionGroup.sensors
                   'PERMISSION_SENSORS=0'
                 ]
        end
      end
    end
    

    删除您确实要使用的权限前面的# 字符。例如,如果您确实需要访问日历,请确保代码如下所示:

    ## dart: PermissionGroup.calendar
    'PERMISSION_EVENTS=0',
    

安卓

    将以下内容添加到您的“gradle.properties”文件中:

    android.useAndroidX=true
    android.enableJetifier=true
    

    确保将“android/app/build.gradle”文件中的compileSdkVersion 设置为28:

     android 
       compileSdkVersion 30
       ...
     
    

    确保替换所有的 android。对其 AndroidX 对应项的依赖项(完整列表可在此处找到:https://developer.android.com/jetpack/androidx/migrate)

    为您的AndroidManifest.xml 文件添加权限。有debugmainprofile 版本可供选择,具体取决于您启动应用的方式。通常,仅向main 版本添加权限就足够了。这是一个示例 AndroidManifest.xml ,其中包含所有可能权限的完整列表。

终于可以这样使用了

有许多权限。您可以获得Permissionstatus,即granteddeniedrestrictedpermanentlyDenied

    var status = await Permission.photos.status;

    if (status.isDenied) 
      // We didn't ask for permission yet.
    

    // You can can also directly ask the permission about its status.
    if (await Permission.location.isRestricted) 
       // The OS restricts access, for example because of parental controls.
    

Call `request()` on a `Permission` to request it. If it has already been granted before, nothing happens.

request() 返回Permission 的新状态。

    if (await Permission.contacts.request().isGranted) 
      // Either the permission was already granted before or the user just granted it.
    

    // You can request multiple permissions at once.
    Map<Permission, PermissionStatus> statuses = await [
      Permission.location,
      Permission.storage,
    ].request();
    print(statuses[Permission.location]);

On Android, you can show a rationale for using permission:

    bool isShown = await Permission.contacts.shouldShowRequestRationale;

完整示例

Container(
  child: Wrap(
    children: <Widget>[
      ListTile(
          leading: Icon(Icons.camera_enhance),
          title: Text(getTranslated(context, "Camera")),
          onTap: () async 
            var status = await Permission.photos.status;
            if (status.isGranted) 
              final pickedFile =
                  await _picker.getImage(source: ImageSource.camera);
              final File file = File(pickedFile.path);
              imageSelected(file);
             else if (status.isDenied) 
              final pickedFile =
                  await _picker.getImage(source: ImageSource.camera);
              final File file = File(pickedFile.path);
              imageSelected(file);
             else 
              showDialog(
                  context: context,
                  builder: (BuildContext context) => CupertinoAlertDialog(
                        title: Text('Camera Permission'),
                        content: Text(
                            'This app needs camera access to take pictures for upload user profile photo'),
                        actions: <Widget>[
                          CupertinoDialogAction(
                            child: Text('Deny'),
                            onPressed: () => Navigator.of(context).pop(),
                          ),
                          CupertinoDialogAction(
                            child: Text('Settings'),
                            onPressed: () => openAppSettings(),
                          ),
                        ],
                      ));
            
          ),
    ],
  ),
)

【讨论】:

谢谢你,我真的不知道如何同时获得 ios 和 android 的权限 这个包有很多bug。你还有其他推荐吗? @ykonda 我认为这不是错误,但实施和理解它的工作原理有点困难,但是一旦你很好地实施它,它就会完美地工作。如果您在实施时遗漏任何内容,它将无法完全发挥作用 @PareshMangukiya android 相机存在这个非常关键的问题github.com/Baseflow/flutter-permission-handler/issues/336 根据@ykonda 在上面发布的 github 问题,截至今天(2021 年 1 月 28 日),最新版本 (5.0.1+1) 继续出现该问题【参考方案2】:

我建议使用permission_handler 库并编写抽象代码(策略模式)以相同的方式处理所有权限。通常,文档很模糊,并且没有说明如何处理不可恢复/禁用场景。

代码:

/// handles .isLimited for iOS 14+ where we can restrict access.
abstract class GrantPermissionStrategy 
  final Permission permission;

  GrantPermissionStrategy(this.permission);

  Future<void> request(
    required final OnPermanentlyDenied onPermanentlyDenied,
    required final OnGranted onGranted,
  ) async 
    PermissionStatus status = await permission.status;
    print("GrantPermissionStrategy status: $status");

    if (!status.isLimited && !status.isGranted) 
      final PermissionStatus result = await permission.request();
      if (result.isPermanentlyDenied) 
        onPermanentlyDenied.call();
        return;
      
      if (!result.isGranted) 
        return;
      
    
    onGranted.call();
  


typedef OnPermanentlyDenied = void Function();

typedef OnGranted = void Function();


而且,你可以做出具体的实现,比如:

class GrantPermissionCameraStrategy extends GrantPermissionStrategy 
  GrantPermissionCameraStrategy() : super(Permission.camera);


class GrantPermissionPhotosStrategy extends GrantPermissionStrategy 
  GrantPermissionPhotosStrategy() : super(Platform.isAndroid ? Permission.storage : Permission.photos);


最后,调用它!:

    await GrantPermissionPhotosStrategy().request(onPermatentlyDenied: () 
      // launch dialog, make user go to app settings
    , onGranted: () async 
      // we have passed! Launch the feature.
    );
  

【讨论】:

检查 status.isPermanentlyDenied 不可靠来自 permission_handler: &gt;6.0.0。正如文档所述:WARNING: This can only be determined AFTER requesting this permission. Therefore make a request call first. 在此处阅读更多信息:github.com/Baseflow/flutter-permission-handler/issues/… @DarkMikey 感谢您发现这一点。更新了代码。【参考方案3】:

为简单起见,我使用了位置权限。要请求另一个权限,只需将 location 替换为该权限。这是list of all permissions。

1。安卓设置:

    将这些添加到android/grade.properties 文件中:

    android.useAndroidX=true
    android.enableJetifier=true
    

    android/app/build.gradle 文件中:

    android 
      compileSdkVersion 30 // Set this to at least 30
      ...
    
    

    android/app/src/main/AndroidManifest.xml文件添加权限

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
      ...
     </manifest>
    

2。 iOS 设置:

    将此添加到info.plist 文件:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App needs location permission to work</string>
    

    PERMISSION_LOCATION=1 添加到Podfile

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        flutter_additional_ios_build_settings(target)
    
        target.build_configurations.each do |config|
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
            '$(inherited)',
    
            ## Add the following line.
             'PERMISSION_LOCATION=1'
          ]
    
        end
      end
    end
    

3。颤振设置:

将此添加到pubspec.yaml 文件中:

permission_handler: ^8.0.0+2

主要工作:

检查权限:

检查位置 aka GPS 是否开启。

final serviceStatus = await Permission.locationWhenInUse.serviceStatus;
bool isGpsOn = serviceStatus == ServiceStatus.enabled;

请求许可:

final status = await Permission.locationWhenInUse.request();
if (status == PermissionStatus.granted) 
  print('Permission granted');
 else if (status == PermissionStatus.denied) 
  print('Denied. Show a dialog with a reason and again ask for the permission.');
 else if (status == PermissionStatus.permanentlyDenied) 
  print('Take the user to the settings page.');


完整代码:

class HomePage extends StatelessWidget 
  Future<void> _checkPermission() async 
    final serviceStatus = await Permission.locationWhenInUse.serviceStatus;
    final isGpsOn = serviceStatus == ServiceStatus.enabled;
    if (!isGpsOn) 
      print('Turn on location services before requesting permission.');
      return;
    

    final status = await Permission.locationWhenInUse.request();
    if (status == PermissionStatus.granted) 
      print('Permission granted');
     else if (status == PermissionStatus.denied) 
      print('Permission denied. Show a dialog and again ask for the permission');
     else if (status == PermissionStatus.permanentlyDenied) 
      print('Take the user to the settings page.');
      await openAppSettings();
    
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: _checkPermission,
          child: Text('Check Permission'),
        ),
      ),
    );
  

【讨论】:

这不适用于 Android 10,您可能还需要更新 MainActivity.kt 或 MainActivity.java @AnthonyO 它适用于所有 Android 版本。我自己在 Android 10 上对其进行了测试。您无需向任何文件的MainActivity 添加任何内容。 @CopsOnRoad 你是个聪明人,你的一些回答对我帮助很大。谢谢你帮助别人。你的答案是正确的答案。当我们使用 [var status = await Permission.photos.status ] 出现错误时,我们必须使用 [var status = await Permission.photos.request();]

以上是关于如何在 Flutter 中请求和检查权限的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android M 中检查单个请求的多个权限?

如何使用 HTTP 请求(Flutter/Dart)检查 Internet 连接?

如何在 iOS5 中重新请求照片访问权限?

如何在 Flutter 中与蓝牙连接和通信

Flutter:如何检查互联网连接? [复制]

如何在 Flutter 中更改权限对话框 UI? [关闭]