π§ smart_permission
π An opinionated wrapper around permission_handler that makes runtime permissions effortless.
Request permissions with one API β smart dialogs, adaptive styles, and complete UX flow handling built-in.
β¨ Why smart_permission?
Most apps spend unnecessary time handling permission logic manually.
smart_permission
takes care of the entire flow β from first ask to permanently denied β automatically, with adaptive dialogs and analytics tracking.
π‘ What makes it different?
- β One-line permission requests (single or batch)
- β Handles denied and permanently denied flows automatically
- β Adaptive Material / Cupertino / Adaptive dialogs
- β Built-in titles & descriptions (with custom overrides)
- β Global theming, analytics hooks, and custom dialogs
- β
Re-exports
Permission
β no need for multiple imports
π§© Feature Highlights
Feature | Description |
---|---|
π Easy API | SmartPermission.request() for one or many permissions |
π¨ Adaptive Dialogs | Material / Cupertino / Platform adaptive support |
π§ Auto Flows | Handles denied/permanently denied states automatically |
π§± Central Configuration | Set global themes, titles, descriptions, analytics |
π§© Custom Dialog Builders | Build your own dialog UI if you need full control |
π§ In-Memory Analytics Tracker | Tracks denied and permanently denied permissions |
βοΈ Installation
Add to your pubspec.yaml
:
dependencies:
smart_permission: ^0.0.1
Then import:
import 'package:smart_permission/smart_permission.dart';
β‘ Quick Start Example
final ok = await SmartPermission.request(
context,
permission: Permission.camera,
style: PermissionDialogStyle.adaptive,
description: 'We need camera to scan QR codes.',
);
Or request multiple:
final result = await SmartPermission.requestMultiple(
context,
permissions: [
Permission.camera,
Permission.microphone,
],
);
Automatic Dialog Flow
State | Behavior |
---|---|
First time | Shows native system sheet |
Denied | Shows rationale dialog |
Permanently denied | Shows βOpen Settingsβ dialog |
π¨ Configuration & Customization
You can configure SmartPermission
globally at app startup or dynamically at runtime.
Global Setup
SmartPermission.config
..brightness = Brightness.light
..primaryColor = Colors.indigo
..analytics = InMemoryPermissionAnalyticsTracker();
Toggle Theme & Primary Color (as in example app)
SmartPermission.config.brightness =
isDark ? Brightness.dark : Brightness.light;
SmartPermission.config.primaryColor = selectedPrimaryColor;
π§ Custom Texts Per Permission
Provide your own titles and messages for each permission:
SmartPermission.config
..titleProvider = (p) {
if (p == Permission.camera) return 'Camera Access Needed';
if (p == Permission.microphone) return 'Microphone Access Needed';
if (p == Permission.locationWhenInUse) return 'Location Access Needed';
return null;
}
..descriptionProvider = (p) {
if (p == Permission.camera) return 'We need the camera to scan QR codes.';
if (p == Permission.microphone) return 'We need the microphone for voice features.';
if (p == Permission.locationWhenInUse) return 'We use your location to show nearby stores.';
return null;
};
π§© Custom Dialog Builder
Want your own UI (like a bottom sheet)? You can override the default dialog completely.
SmartPermission.config.customDialogBuilder = (
context, {
required style,
required title,
required message,
required primaryText,
required secondaryText,
}) async {
return showModalBottomSheet<bool>(
context: context,
isScrollControlled: true,
backgroundColor: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (ctx) {
final cs = Theme.of(ctx).colorScheme;
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(children: [
Icon(Icons.privacy_tip_outlined, color: cs.primary),
const SizedBox(width: 12),
Expanded(child: Text(title, style: Theme.of(ctx).textTheme.titleLarge)),
]),
const SizedBox(height: 12),
Text(message),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(ctx, false),
child: Text(secondaryText),
),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton(
onPressed: () => Navigator.pop(ctx, true),
child: Text(primaryText),
),
),
],
),
],
),
);
},
);
};
π Analytics Tracking
Track user behavior for better insights or debugging:
class MyAnalytics implements PermissionAnalyticsTracker {
@override
void onDenied(Permission permission) {
print('Permission denied: $permission');
}
@override
void onPermanentlyDenied(Permission permission) {
print('Permission permanently denied: $permission');
}
}
SmartPermission.config.analytics = MyAnalytics();
The example app uses InMemoryPermissionAnalyticsTracker()
to log permission denials in real-time within the UI.
π§© Example App Features
The included /example
project demonstrates:
- π Theme toggle (Light/Dark)
- π¨ Primary color cycling (Indigo / Teal / Orange)
- π± Dialog style selection (Material, Cupertino, Adaptive)
- π§ Custom title/description per permission
- πͺ Custom bottom-sheet dialog builder
- π Real-time analytics tracking for denied states
- π¦ Batch permission requests
π§ Tips & Notes
- Some Android permissions (e.g.,
manageExternalStorage
,systemAlertWindow
) open system settings instead of a dialog. - On Android 13+, use
Permission.photos
,Permission.videos
, orPermission.audio
instead of deprecated storage permissions. locationAlways
often requires first grantinglocationWhenInUse
.- Use
SmartPermission.requestMultiple()
for grouped requests.
π€ Author
Created with β€οΈ by Jaimin Kavathia πΌ LinkedIn β’ π GitHub
π License
Licensed under the MIT License. Free for personal and commercial use.
β If you like this package, give it a star on GitHub & pub.flutter-io.cn!
smart_permission β because handling permissions shouldnβt be a hassle.