Skip to main content

simple_permissions_native 1.6.0 copy "simple_permissions_native: ^1.6.0" to clipboard
simple_permissions_native: ^1.6.0 copied to clipboard

unlisted

Unified permission handling for Flutter apps. Manages runtime permissions, app roles (SMS, Dialer), and battery optimization.

simple_permissions_native #

CI

A federated Flutter permission plugin with a typed, sealed-class API. Every permission is a Dart type — no stringly-typed lookups, no magic enums. Pattern-match on results, batch requests in a single call, and express features as Intentions instead of raw permission lists.

Backed by Pigeon-generated native bridges for Android (Kotlin), iOS (Swift), and macOS (Swift).

Installation #

dependencies:
  simple_permissions_native: ^1.2.0

Quick start #

import 'package:simple_permissions_native/simple_permissions_native.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SimplePermissionsNative.initialize();
  runApp(const MyApp());
}

Check and request a single permission #

final perms = SimplePermissionsNative.instance;

final grant = await perms.check(const CameraAccess());
if (grant != PermissionGrant.granted) {
  final result = await perms.request(const CameraAccess());
  if (result == PermissionGrant.permanentlyDenied) {
    // User selected "Don't allow" — direct them to Settings
    await perms.openAppSettings();
  }
}

Batch-request multiple permissions #

final result = await perms.requestAll([
  const CameraAccess(),
  const RecordAudio(),
  const FineLocation(),
]);

if (result.isFullyGranted) {
  startVideoCall();
} else if (result.requiresSettings) {
  showSettingsPrompt(result.permanentlyDenied);
}

Use Intentions for feature-level requests #

Instead of listing individual permissions, express what the app intends to do:

// Request everything needed for SMS messaging
final ok = await perms.requestIntention(Intention.texting);

// Or get detailed results
final result = await perms.requestIntentionDetailed(Intention.contacts);
if (result.hasDenial) {
  handleDenied(result.denied);
}

Compose custom Intentions:

final videoCall = Intention.combine('video_call', [
  Intention.camera,
  Intention.microphone,
  Intention.location,
]);

Version-aware permissions #

Android splits storage permissions at API 33 and Bluetooth at API 31. VersionedPermission resolves this automatically:

// Resolves to ReadMediaImages on API 33+, ReadExternalStorage on older devices
final result = await perms.request(VersionedPermission.images());

API reference #

Method Returns Description
initialize() Future<void> Required before any other call
check(Permission) Future<PermissionGrant> Current grant state (no prompt)
request(Permission) Future<PermissionGrant> Request with system dialog
checkAll(List<Permission>) Future<PermissionResult> Batch check
requestAll(List<Permission>) Future<PermissionResult> Batch request (optimized on Android)
checkIntention(Intention) Future<bool> All permissions in intention granted?
requestIntention(Intention) Future<bool> Request all, return success/failure
checkIntentionDetailed(Intention) Future<PermissionResult> Intention with per-permission results
requestIntentionDetailed(Intention) Future<PermissionResult> Request intention with details
isSupported(Permission) Future<bool> Does this permission exist on this platform/OS version?
openAppSettings() Future<bool> Open system settings for this app
checkLocationAccuracy() Future<LocationAccuracyStatus> GPS precision level

PermissionGrant states #

State Meaning What to do
granted User approved Proceed
denied User denied (can ask again) Show rationale, then re-request
permanentlyDenied "Don't ask again" selected Call openAppSettings()
restricted OS-level restriction (parental controls, MDM) Inform user; cannot be changed
limited Partial access (e.g., iOS limited photo library) Usable — isFullyGranted treats this as satisfied
provisional iOS provisional notifications (delivers quietly) Usable — isFullyGranted treats this as satisfied
notApplicable Permission doesn't exist on this platform Hide the feature or skip
notAvailable Permission exists but not on this OS version Hide or use fallback

Intentions #

Built-in presets group common runtime permissions. Role takeovers are explicit:

Intention Permissions
Intention.texting SendSms, ReadSms, ReceiveSms, ReceiveMms, ReceiveWapPush
Intention.calling ReadPhoneState, ReadPhoneNumbers, MakeCalls, AnswerCalls
Intention.contacts ReadContacts, WriteContacts
Intention.camera CameraAccess
Intention.microphone RecordAudio
Intention.location FineLocation, CoarseLocation
Intention.notifications PostNotifications
Intention.mediaImages VersionedPermission.images()
Intention.mediaVideo VersionedPermission.video()
Intention.mediaAudio VersionedPermission.audio()
Intention.mediaVisual images + video combined
Intention.defaultSmsRole DefaultSmsApp (Android role)
Intention.defaultDialerRole DefaultDialerApp (Android role)

Intention.textingWithDefaultSmsRole and Intention.callingWithDefaultDialerRole combine runtime permissions with role requests.

Platform support #

Platform Support
Android Runtime permissions, app roles, system-setting intents
iOS Framework permission APIs (22 permissions)
macOS Framework permission APIs (13 permissions)
Web Camera, microphone, geolocation, notifications via browser Permissions API
Linux / Windows Returns notApplicable (graceful no-op)

Host app setup #

iOS — Info.plist #

Add only the usage-description keys your app needs. The App Store will reject builds that include unused keys.

Key Required for
NSContactsUsageDescription ReadContacts, WriteContacts
NSCameraUsageDescription CameraAccess
NSMicrophoneUsageDescription RecordAudio
NSLocationWhenInUseUsageDescription FineLocation, CoarseLocation
NSLocationAlwaysAndWhenInUseUsageDescription BackgroundLocation
NSPhotoLibraryUsageDescription ReadMediaImages, ReadMediaVideo
NSCalendarsUsageDescription ReadCalendar, WriteCalendar
NSRemindersUsageDescription ReadReminders, WriteReminders
NSSpeechRecognitionUsageDescription SpeechRecognition
NSBluetoothAlwaysUsageDescription BluetoothConnect, BluetoothScan, BluetoothAdvertise
NSMotionUsageDescription BodySensors, ActivityRecognition
NSUserTrackingUsageDescription AppTrackingTransparency
NSHealthShareUsageDescription HealthAccess
NSHealthUpdateUsageDescription HealthAccess

macOS — Entitlements #

macOS sandboxed apps require matching entitlements in addition to Info.plist keys:

<!-- Example: enable camera + contacts in *.entitlements -->
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
<true/>

Android — AndroidManifest.xml #

Declare permissions in android/app/src/main/AndroidManifest.xml. Only include what your app uses.

<!-- Common permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Storage: use both for backward compatibility -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<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" />

<!-- Notifications (API 33+) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

See the full list of Android permission strings in the Android developer docs.

Native Kotlin helpers for sibling plugins #

Plugin authors whose Android code needs to check (read-only) whether a runtime permission is granted or whether the app holds a default-app role can import PermissionGuards from simple_permissions_android instead of reaching for ContextCompat.checkSelfPermission(...) / RoleManager.isRoleHeld(...) directly.

import io.simplezen.simple_permissions_android.PermissionGuards
import android.app.role.RoleManager
import android.Manifest

// Single permission
if (!PermissionGuards.isPermissionGranted(
        context, Manifest.permission.READ_SMS)) {
  return emptyList()  // silent-fail path: caller must request via Dart API
}

// Batch precondition
if (!PermissionGuards.areAllPermissionsGranted(
        context,
        listOf(Manifest.permission.READ_SMS, Manifest.permission.SEND_SMS))) {
  return
}

// Default-app role
if (!PermissionGuards.isRoleHeld(context, RoleManager.ROLE_SMS)) {
  // Read-only; to request the role, call
  // SimplePermissionsNative.instance.request(DefaultSmsApp()) from Dart.
}

Gradle wiring #

Flutter's plugin-loader is what makes cross-repo consumption work, and it does exactly the right thing once you give it the right inputs:

Consuming plugin's root pubspec.yaml declares the Dart dep:

dependencies:
  simple_permissions_native: ^1.4.0

Consuming plugin's android/build.gradle[.kts] wires the project ref:

dependencies {
  implementation(project(":simple_permissions_android"))
}

That's it. In any app that consumes the plugin, Flutter's plugin-loader walks the pubspec graph — including transitive plugin deps — and synthesizes Gradle project entries for each federated plugin's Android module. The :simple_permissions_android project then exists in the final app's Gradle build alongside your consuming plugin, and the implementation(project(...)) ref resolves.

No composite builds, no Maven publication, no settings.gradle gymnastics.

One gotcha for local path-dep workflows

If your consuming plugin's example app uses dependency_overrides to point at a sibling simple-permissions checkout, override each federated platform package explicitly — not just the facade:

dependency_overrides:
  simple_permissions_native:
    path: ../../simple-permissions
  simple_permissions_platform_interface:
    path: ../../simple-permissions/packages/simple_permissions_platform_interface
  simple_permissions_android:
    path: ../../simple-permissions/packages/simple_permissions_android
  # ...ios, macos, web if you need them

Why: the plugin-loader registers Gradle project paths per federated platform package. If only the facade is overridden, :simple_permissions_android silently resolves to whatever's on pub.dev, which will be older than your local working copy — and Kotlin compile fails with "unresolved reference" on any new native API. Overriding the platform packages too keeps the Gradle classpath and the Dart workspace in lockstep.

When none of the above apply

If you can't add simple_permissions_native to the consuming plugin (e.g. you want to avoid the runtime dep for policy reasons), keep using Android primitives inline — ContextCompat.checkSelfPermission(...) is a legitimate read-only OS query, and Rule 2 ("access state goes through simple-permissions") is still upheld at the Dart API boundary (request flows, observation, permission types). PermissionGuards is a cleanup path, not a requirement.

No request-side helpers #

PermissionGuards deliberately does not expose a "request permission" equivalent. Request flows surface UI and route through activity bindings — they belong behind the Dart API (SimplePermissionsNative.instance.request(...)) so the prompt is scoped by the user's consent flow, not a random native call site.

Architecture #

simple_permissions_native/
├── lib/simple_permissions_native.dart          <- App-facing facade
├── packages/simple_permissions_platform_interface/
│   └── Permission sealed classes, PermissionGrant, PermissionResult, Intention
├── packages/simple_permissions_android/        <- Pigeon + Kotlin handlers
├── packages/simple_permissions_ios/            <- Pigeon + Swift handlers
├── packages/simple_permissions_macos/          <- Pigeon + Swift handlers
└── packages/simple_permissions_web/            <- Browser Permissions API

License #

MIT © 2025 SimpleZen

0
likes
140
points
92
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Unified permission handling for Flutter apps. Manages runtime permissions, app roles (SMS, Dialer), and battery optimization.

Repository (GitHub)
View/report issues

Topics

#permissions #android #ios #privacy

License

MIT (license)

Dependencies

flutter, simple_permissions_android, simple_permissions_ios, simple_permissions_macos, simple_permissions_platform_interface, simple_permissions_web

More

Packages that depend on simple_permissions_native

Packages that implement simple_permissions_native