permission_plus
A unified, cross-platform Flutter plugin for requesting and checking permissions on Android, iOS, macOS, Web, Windows, and Linux.
Features
- Check permission status (
granted,denied,restricted,permanentlyDenied,limited,provisional, andnotDetermined). - Request a single permission or multiple permissions at once.
- Open the platform's app settings page.
- Determine if a rationale should be shown before requesting a permission (Android only).
- Fetch location accuracy (precise vs reduced).
- Type-safe implementation leveraging Pigeon-generated host APIs.
Supported Platforms
- Android: API 21+
- iOS: iOS 12.0+
- macOS: macOS 11.0+
- Web: Chrome, Firefox, Safari (uses the Web Permissions API)
- Windows: Windows 10+ (Win32 / WinRT)
- Linux: Any modern Linux distribution (returns
grantedfor desktop apps)
Setup
Android
Add the required permissions to your android/app/src/main/AndroidManifest.xml file. Only add the permissions your app actually uses:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Microphone / Audio -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Location -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- Contacts -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- Calendar -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<!-- Storage / Photos -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Sensors / Motion -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- Phone -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- SMS -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
</manifest>
iOS
Add the corresponding usage descriptions to your ios/Runner/Info.plist. Only add the keys for permissions your app actually requests:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record audio.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select photos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs photo library addition access to save photos.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to find nearby places.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs location always access to track background location.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access for map features.</string>
<key>NSContactsUsageDescription</key>
<string>This app needs contacts access to find friends.</string>
<key>NSCalendarsUsageDescription</key>
<string>This app needs calendar access to schedule events.</string>
<key>NSRemindersUsageDescription</key>
<string>This app needs reminders access to create tasks.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs bluetooth access to connect to devices.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs bluetooth access to connect to devices in background.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app needs speech recognition access for voice commands.</string>
<key>NSAppleMusicUsageDescription</key>
<string>This app needs media library access to play music.</string>
<key>NSMotionUsageDescription</key>
<string>This app needs motion access to track steps.</string>
<key>NSUserTrackingUsageDescription</key>
<string>This app needs tracking access for personalized ads.</string>
macOS
Add the corresponding usage descriptions to your macos/Runner/Info.plist. Only add the keys for permissions your app actually requests:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record audio.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select photos.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to find nearby places.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs location always access to track background location.</string>
<key>NSContactsUsageDescription</key>
<string>This app needs contacts access to find friends.</string>
<key>NSCalendarsUsageDescription</key>
<string>This app needs calendar access to schedule events.</string>
<key>NSRemindersUsageDescription</key>
<string>This app needs reminders access to create tasks.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs bluetooth access to connect to devices.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs bluetooth access to connect to devices in background.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app needs speech recognition access for voice commands.</string>
<key>NSAppleMusicUsageDescription</key>
<string>This app needs media library access to play music.</string>
<key>NSUserNotificationsUsageDescription</key>
<string>This app needs notification access to send alerts.</string>
Additionally, if your macOS app is sandboxed (which is the default), you must add entitlements to macos/Runner/DebugProfile.entitlements and Release.entitlements. Only add entitlements for resources you use:
<!-- Hardware Access -->
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
<!-- Network Access (Required for Speech Recognition) -->
<key>com.apple.security.network.client</key>
<true/>
<!-- Personal Information -->
<key>com.apple.security.personal-information.location</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
<true/>
<key>com.apple.security.personal-information.calendars</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
Important Note for macOS Notifications: macOS
UNUserNotificationCenterenforces strict security for notifications. If you are developing locally and your app is "Ad-hoc" signed (flutter runwithout an Apple Developer account configured in Xcode), macOS will instantly reject notification requests and returnpermanentlyDeniedwithout showing a prompt. To test macOS notification prompts natively, you must openmacos/Runner.xcworkspacein Xcode and sign the app with a valid Apple Developer Team, OR you can test notification logic using the iOS Simulator (which bypasses this signing restriction).
Web, Windows, and Linux
No special setup is required.
- On the Web, the browser handles permission prompts automatically using the standard Web Permissions API.
- On Windows, Win32 apps have unrestricted access to most resources. Permissions like Camera and Location will check Windows Privacy Settings.
- On Linux, standard desktop applications have unrestricted access to the user's hardware and files, so all permissions will return
granted.
Usage
1. Check a permission status
import 'package:permission_plus/permission_plus.dart';
Future<void> checkCamera() async {
final status = await PermissionPlus.checkPermission(PermissionType.camera);
switch (status) {
case PermissionStatus.granted:
print('Camera access is granted.');
break;
case PermissionStatus.denied:
print('Camera access is denied.');
break;
case PermissionStatus.permanentlyDenied:
print('Camera access is permanently denied.');
break;
case PermissionStatus.restricted:
print('Camera access is restricted (e.g. parental controls).');
break;
case PermissionStatus.limited:
print('Camera access is limited.');
break;
case PermissionStatus.provisional:
print('Camera access is provisional.');
break;
case PermissionStatus.notDetermined:
print('Camera access has not been determined yet.');
break;
}
}
2. Request a permission
import 'package:permission_plus/permission_plus.dart';
Future<void> requestCamera() async {
final status = await PermissionPlus.requestPermission(PermissionType.camera);
if (status == PermissionStatus.granted) {
print('Camera permission granted');
} else if (status == PermissionStatus.permanentlyDenied) {
print('Permission permanently denied. Please enable it in settings.');
await PermissionPlus.openSettings();
}
}
3. Request multiple permissions
import 'package:permission_plus/permission_plus.dart';
Future<void> requestMultiple() async {
final statuses = await PermissionPlus.requestPermissions([
PermissionType.camera,
PermissionType.location,
]);
if (statuses[PermissionType.camera] == PermissionStatus.granted) {
print('Camera granted');
}
}
4. Open App Settings
If a permission is permanentlyDenied, you can guide the user to the platform's settings page so they can enable it manually.
import 'package:permission_plus/permission_plus.dart';
Future<void> openSettings() async {
final opened = await PermissionPlus.openSettings();
if (opened) {
print('Successfully opened settings.');
} else {
print('Could not open settings.');
}
}
Contributing
Contributions are welcome! Please open an issue or pull request on the GitHub repository.