🧠 smart_permission

Pub.dev Badge Build Badge MIT License Flutter Badge

πŸš€ 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.

Smart Permission


✨ 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, or Permission.audio instead of deprecated storage permissions.
  • locationAlways often requires first granting locationWhenInUse.
  • 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.

Libraries

smart_permission