Как запрашивать и проверять разрешения во Flutter

Я использую различные плагины для получения пользовательских данных, контактов, фотографий и камеры, когда пользователь нажимает кнопку «Не разрешать». Приложение переходит в беззвучный режим. Я хочу показать пользователю диалоговое окно запроса разрешения, когда пользователь проверяет Никогда больше не спрашивать и Не разрешать и снова входит в приложение.

В настоящее время приложение никогда не запрашивает снова, когда пользователь проверяет Не разрешать

Я знаю, что пользователи должны предоставить приложению разрешение на доступ к личной информации, включая текущее местоположение, камеру, календарь, контакты , mediaLibrary, микрофон, датчики, речи и фотографии. Хотя люди ценят удобство использования приложения, которое имеет доступ к этой информации, они также ожидают контроля над своими личными данными. Например, людям нравится иметь возможность автоматически отмечать фотографии с их физическим местоположением или находить ближайших друзей, но им также нужна возможность отключить такие функции.

Как снова запросить разрешение у пользователя во флаттере?


person Paresh Mangukiya    schedule 02.11.2020    source источник


Ответы (3)


Я был очень обеспокоен этой проблемой. После применения нескольких решений я нашел это решение. Я хочу поделиться этим со всеми, поэтому я задал вопрос и ответил

В большинстве операционных систем разрешения предоставляются приложениям не только во время установки. Скорее, разработчики должны запрашивать у пользователя разрешения во время работы приложения.

Наилучший способ обработки разрешений - использование подключаемого модуля 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

    • Add permission to your Info.plist file. Here's an example Info.plist with a complete list of all possible permissions.

    ВАЖНО: вам нужно будет включить все параметры разрешений, когда вы хотите отправить свое приложение. Это связано с тем, что плагин 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',
    

Android

  1. Добавьте в свой gradle.properties файл следующее:

    android.useAndroidX=true
    android.enableJetifier=true
    
  2. Убедитесь, что вы установили compileSdkVersion в вашем android/app/build.gradle файле на 28:

    android {
      compileSdkVersion 28
      ...
    }
    
3. Make sure you replace all the android. dependencies to their AndroidX counterparts (a full list can be found here: https://developer.android.com/jetpack/androidx/migrate)

  Add permissions to your `AndroidManifest.xml` file. There's a `debug`, `main` and `profile` version which are chosen depending on how you start your app. In general, it's sufficient to add permission only to the `main` version. Here's an example `AndroidManifest.xml` with a complete list of all possible permissions.
  1. Есть несколько разрешений. Вы можете получить Permission status, то есть granted, denied, restricted или permanentlyDenied.

    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.
    }
    

    Позвоните request() по Permission, чтобы запросить его. Если он уже был предоставлен ранее, ничего не происходит. 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]);
    

    На Android вы можете показать обоснование использования разрешения:

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

введите описание изображения здесь

person Paresh Mangukiya    schedule 02.11.2020
comment
спасибо, я действительно понятия не имел, как получить разрешение для ios и android одновременно - person ahmed; 03.11.2020
comment
этот пакет довольно глючный. Вы порекомендуете еще один? - person ykonda; 06.01.2021
comment
@ykonda Я думаю, что это не ошибка, но немного сложно реализовать и понять, как это работает, но как только вы хорошо его реализуете, он работает отлично. Если вы что-то пропустите во время реализации, полностью не сработает. - person Paresh Mangukiya; 06.01.2021
comment
@PareshMangukiya есть довольно серьезная проблема с камерой Android github.com/Baseflow/flutter -permission-handler / issues / 336 - person ykonda; 10.01.2021
comment
Согласно проблеме github, опубликованной сразу же выше, @ykonda, проблема продолжает возникать с последней версией (5.0.1 + 1) на сегодняшний день (28 января 2021 г.) - person Mark Gavagan; 28.01.2021
comment
Привет, @PareshMangukiya! В настоящее время этот плагин не поддерживается веб-приложением. Знаете ли вы какой-нибудь другой плагин, который можно использовать для веб-приложения? - person Deepak Patankar; 13.02.2021
comment
@PareshMangukiya, как это возможно работает ??? Когда я следую инструкциям и использую Permission.camera.status, он выделяет мою константу камеры и говорит, что getter 'camera' не определен для типа 'dynamic Function ()'. Кроме того, когда я добавляю пакет permission_handler, он выдает ошибку для любой переменной List. Не могли бы вы поделиться полным примером кода? Их пример кода НЕ РАБОТАЕТ. - person zuboje; 14.03.2021
comment
Вы делаете одно и то же в статусах isGranted и isDenied? - person BoshRa; 08.06.2021
comment
Как установить действие при нажатии на уведомление после предоставления? - person Abhishek Thapliyal; 06.07.2021

Для простоты я использовал разрешение на местоположение. Чтобы запросить другое разрешение, просто замените местоположение этим разрешением. Вот список всех разрешений.

1. Настройка Android:

  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=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

Основная работа:

  • Проверьте разрешение:

    Чтобы проверить, включено ли местоположение или 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'),
        ),
      ),
    );
  }
}
person CopsOnRoad    schedule 18.06.2021
comment
Это не работает для Android 10, вам также может потребоваться обновить MainActivity.kt или MainActivity.java. - person Anthony O; 06.07.2021
comment
@AnthonyO Работает на всех версиях Android. Сам тестировал на Android 10. Не нужно ничего добавлять в MainActivity ни одного файла. - person CopsOnRoad; 06.07.2021

Я рекомендую использовать 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 OnPermatentlyDenied onPermatentlyDenied,
    required final OnGranted onGranted,
  }) async {
    PermissionStatus status = await permission.status;
    print("GrantPermissionStrategy status: $status");
    if (status.isPermanentlyDenied) {
      onPermatentlyDenied.call();
      return;
    }
    if (!status.isLimited && !status.isGranted) {
      final PermissionStatus result = await permission.request();
      if (!result.isGranted) {
        return;
      }
    }
    onGranted.call();
  }
}

typedef OnPermatentlyDenied = 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.
    });
  }
person Michał Dobi Dobrzański    schedule 18.04.2021