В чем разница в работе с разрешениями (permissions) на iOS и Android в 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, иначе разрешение не будет запрошено, а функционал просто не сработает.

Ответ 18+ 🔞

А, ну это ж классика, ебать мои старые костыли! Смотри, с разрешениями в мобилке — это вообще отдельная песня, где каждый куплет тебе в душу мать. Берёшь пакет permission_handler, думаешь: «Ну сейчас всё по-быстрому сделаю». А нихуя! Тут два зверя, Android и iOS, и у них в голове, прости господи, хуй с винтом.

Android, этот хитрая жопа:

  1. Манифест — святое. Если не прописал разрешение в AndroidManifest.xml, то можешь хоть обоссысь, запросить его в рантайме не получится. Это как прийти в клуб без штанов — тебя просто нахуй пошлют.
  2. Запрос на лету. Опасные штуки вроде камеры или локации надо спрашивать у юзера, когда приложение уже работает. Он может кивнуть или послать тебя. Стандартно.
  3. Scoped Storage (Android 10+). Вот тут начинается пиздец, Карл! Раньше ты мог шариться по всей памяти телефона как у себя дома. А теперь тебе сказали: «Хуй с горы, дружок». Для выбора файла юзаешь системный пикер (file_picker), а чтобы получить доступ ко всему хранилищу, нужно выпрашивать особое разрешение MANAGE_EXTERNAL_STORAGE, и тебя ещё будут смотреть как на полупидора. Шансов — ноль ебать.

iOS, эта мартышлюшка:

  1. Info.plist — твой пропуск. Не добавил ключик с внятным описанием (типа NSLocationWhenInUseUsageDescription) в Info.plist? Поздравляю, твоя фича просто не сработает, и ты будешь месяц искать баг, пока не охуеешь. Описание — обязательно, иначе система даже не чихнет в твою сторону.
  2. Запрос один раз. Спросил — и живи с ответом. Пользователь может выбрать «Разрешить один раз», «Только когда приложение открыто» или «Иди нахуй». И это навсегда. Ну или почти.
  3. Повторный запрос? Не, не слышал. Если юзер запретил, то твой код может только вежливо предложить ему пойти в настройки системы и там всё поправить. Через openAppSettings(). А сам — ни хуя не может.

Вот, смотри, как это в коде выглядит, типа:

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; // Вернёт true, только если доступ дали.
}

А главная разница, ёпта, в чём? На Android нету варианта «разрешить один раз», зато там можно ковыряться в разрешениях точечнее. А на iOS — если забыл про Info.plist, то вся твоя работа насмарку, доверия к системе — ноль ебать. Так что держи ухо востро, чувак, тут овердохуища подводных камней.