46

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

目前,当用户选中不允许时,应用程序不再要求

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

如何在颤动中再次请求用户许可?

4

3 回答 3

91

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

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

处理权限的最佳方法是使用permission_handler 插件。该插件提供了一个跨平台(iOS、Android)API 来请求权限并检查其状态。

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

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

    dependencies:
      permission_handler: ^5.0.1+1
    
  2. 现在在您的 Dart 代码中,您可以使用:

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

    iOS

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

    重要提示:当您要提交您的应用程序时,您必须包括所有权限选项。这是因为permission_handler插件涉及所有不同的 SDK,并且静态代码分析器(由 Apple 在应用程序提交时运行)检测到这一点,如果在Info.plist. 可以在此处找到有关此的更多信息。

  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)',
    
                   ## 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
    
  2. 删除#您确实要使用的权限前面的字符。例如,如果您确实需要访问日历,请确保代码如下所示:

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

安卓

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

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

     android {
       compileSdkVersion 30
       ...
     }
    
  3. 确保你更换了所有的android。对其 AndroidX 对应物的依赖项(完整列表可在此处找到:https://developer.android.com/jetpack/androidx/migrate)

    AndroidManifest.xml 为您的文件添加权限 。 根据您启动应用程序的方式选择, debug和 main 版本 。profile通常,仅向 main 版本添加权限就足够了。AndroidManifest.xml 这是一个包含所有可能权限的完整列表的示例 。

最后你可以像这样使用

有许多 权限。您可以得到一个 Permission's  , status即 granted,  或 。deniedrestrictedpermanentlyDenied

    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(),
                          ),
                        ],
                      ));
            }
          }),
    ],
  ),
)

在此处输入图像描述

于 2020-11-02T13:00:11.177 回答
15

为简单起见,我使用了位置权限。要请求另一个权限,只需将 location 替换为该权限。这是所有权限的列表

1.安卓设置:

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

    android.useAndroidX=true
    android.enableJetifier=true
    
  2. android/app/build.gradle文件中:

    android {
      compileSdkVersion 30 // Set this to at least 30
      ...
    }
    
  3. 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设置:

  1. 将此添加到info.plist文件中:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App needs location permission to work</string>
    
  2. 添加PERMISSION_LOCATION=1Podfile

    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

主要工作:

  • 检查权限:

    检查位置(即 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'),
        ),
      ),
    );
  }
}
于 2021-06-18T23:17:31.273 回答
5

我建议使用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.
    });
  }
于 2021-04-18T17:30:50.590 回答