В чем разница в работе с разрешениями (permissions) на iOS и Android в Flutter?

«В чем разница в работе с разрешениями (permissions) на iOS и Android в Flutter?» — вопрос из категории Платформенное взаимодействие, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Работа с разрешениями — одна из ключевых платформенно-зависимых задач. Во Flutter мы используем пакет permission_handler, но важно понимать различия в поведении ОС.

Android:

  1. Объявление в манифесте: Все разрешения должны быть объявлены в android/app/src/main/AndroidManifest.xml.
  2. Runtime-запрос: "Опасные" разрешения (камера, контакты, местоположение, хранилище) требуют запроса во время работы приложения. Пользователь может ответить "Разрешить" или "Запретить".
  3. Scoped Storage (Android 10+): Доступ к общему хранилищу сильно ограничен. Для выбора файлов используется системный пикер (file_picker), а для папок может потребоваться специальное разрешение MANAGE_EXTERNAL_STORAGE.

iOS:

  1. Объявление в Info.plist: Необходимо добавить соответствующие ключи (например, NSLocationWhenInUseUsageDescription) в ios/Runner/Info.plist с описанием причины запроса.
  2. Runtime-запрос: Запрос происходит один раз при первом обращении к функционалу. Пользователь может выбрать "Разрешить один раз", "Разрешить при использовании приложения" или "Запретить".
  3. Повторный запрос: Если пользователь запретил доступ, приложение может только перенаправить его в настройки системы (openAppSettings()).

Пример кода с permission_handler:

import 'package:permission_handler/permission_handler.dart';

Future<bool> requestCameraPermission() async {
  // Проверяем статус
  var status = await Permission.camera.status;
  if (status.isDenied) {
    // Запрашиваем разрешение (на iOS может показать диалог, на Android — системное окно)
    status = await Permission.camera.request();
  }

  if (status.isPermanentlyDenied) {
    // Пользователь нажал "Не спрашивать снова" (Android) или запретил (iOS)
    // Открываем настройки приложения
    await openAppSettings();
  }

  return status.isGranted;
}

Ключевое отличие: На Android логика "разрешить один раз" отсутствует, а управление разрешениями более детализировано (например, можно запросить доступ только к фотографиям, а не ко всему хранилищу). На iOS критически важно заполнить Info.plist, иначе разрешение не будет запрошено, а функционал просто не сработает.