permission_master 0.1.0 copy "permission_master: ^0.1.0" to clipboard
permission_master: ^0.1.0 copied to clipboard

**Permission Master** is a **Flutter** plugin for managing and requesting permissions on **All Platform**.

example/lib/main.dart

// ignore_for_file: unreachable_switch_default, use_build_context_synchronously

import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:permission_master/permission_master.dart';
import 'package:permission_master_example/ios_example_screen.dart';
import 'package:permission_master_example/linux_example_screen.dart';
import 'package:permission_master_example/test_screen.dart';
import 'package:permission_master_example/web_example_screen.dart';

import 'windows_example_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Permission Master Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.dark,
        ),
      ),
      home: kIsWeb
          ? const WebExampleScreen()
          : Platform.isWindows
          ? const WindowsExampleScreen()
          : Platform.isMacOS
          ? const IosExampleScreen() // Using iOS example for macOS
          : Platform.isLinux
          ? const LinuxExampleScreen()
          : Platform.isIOS
          ? const IosExampleScreen() // iOS specific example
          : const PermissionDemoPage(), // Android example
    );
  }
}

/// Enhanced example screen specifically designed for macOS and iOS
/// Showcases all available permissions with modern UI and comprehensive testing
class MacOSIOSExampleScreen extends StatefulWidget {
  const MacOSIOSExampleScreen({super.key});

  @override
  State<MacOSIOSExampleScreen> createState() => _MacOSIOSExampleScreenState();
}

class _MacOSIOSExampleScreenState extends State<MacOSIOSExampleScreen>
    with TickerProviderStateMixin {
  final PermissionMaster _permissionMaster = PermissionMaster();
  final Map<String, PermissionStatus> _permissionStatus = {};
  final Map<String, int> _permissionAttempts = {};
  final GetStorageBridge _storage = GetStorageBridge();

  late TabController _tabController;
  bool _isLoading = false;
  String _platformVersion = 'Unknown';

  // Permission categories for better organization
  final List<PermissionGroup> _permissionGroups = [
    PermissionGroup(
      title: 'Media & Camera',
      icon: Icons.camera_alt,
      permissions: [
        PermissionItem(
          type: PermissionType.camera,
          title: 'Camera',
          description: 'Access camera for photos and video calls',
          icon: Icons.camera_alt,
          color: Colors.blue,
        ),
        PermissionItem(
          type: PermissionType.microphone,
          title: 'Microphone',
          description: 'Record audio for voice messages and calls',
          icon: Icons.mic,
          color: Colors.red,
        ),
        PermissionItem(
          type: PermissionType.readStorage,
          title: 'Photo Library',
          description: 'Access photos and media files',
          icon: Icons.photo_library,
          color: Colors.purple,
        ),
      ],
    ),
    PermissionGroup(
      title: 'Location & Motion',
      icon: Icons.location_on,
      permissions: [
        PermissionItem(
          type: PermissionType.fineLocation,
          title: 'Location',
          description: 'Access precise location for maps and navigation',
          icon: Icons.location_on,
          color: Colors.green,
        ),
        PermissionItem(
          type: PermissionType.activityRecognition,
          title: 'Motion & Fitness',
          description: 'Track physical activity and motion data',
          icon: Icons.directions_run,
          color: Colors.orange,
        ),
      ],
    ),
    PermissionGroup(
      title: 'Communication',
      icon: Icons.contacts,
      permissions: [
        PermissionItem(
          type: PermissionType.contacts,
          title: 'Contacts',
          description: 'Access your contacts for easy sharing',
          icon: Icons.contacts,
          color: Colors.teal,
        ),
        PermissionItem(
          type: PermissionType.notifications,
          title: 'Notifications',
          description: 'Send you important notifications',
          icon: Icons.notifications,
          color: Colors.amber,
        ),
        PermissionItem(
          type: PermissionType.calendar,
          title: 'Calendar',
          description: 'Access calendar events and create reminders',
          icon: Icons.calendar_today,
          color: Colors.indigo,
        ),
      ],
    ),
    PermissionGroup(
      title: 'Connectivity',
      icon: Icons.bluetooth,
      permissions: [
        PermissionItem(
          type: PermissionType.bluetooth,
          title: 'Bluetooth',
          description: 'Connect to Bluetooth devices',
          icon: Icons.bluetooth,
          color: Colors.cyan,
        ),
      ],
    ),
  ];

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      PermissionMaster.setContext(context);
      _initializeApp();
    });
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  Future<void> _initializeApp() async {
    setState(() => _isLoading = true);

    try {
      // Get platform version
      final version = await _permissionMaster.getPlatformVersion();
      setState(() => _platformVersion = version!);

      // Load saved attempts
      await _loadPermissionAttempts();

      // Check initial permissions
      await _checkAllPermissions();
    } catch (e) {
      _showErrorSnackBar('Failed to initialize: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _loadPermissionAttempts() async {
    try {
      final attempts = await _storage.read<Map<String, int>>(
        'permission_attempts',
        {},
      );
      setState(() {
        _permissionAttempts.addAll(attempts);
      });
    } catch (e) {
      debugPrint('Failed to load permission attempts: $e');
    }
  }

  Future<void> _savePermissionAttempts() async {
    try {
      await _storage.write('permission_attempts', _permissionAttempts);
    } catch (e) {
      debugPrint('Failed to save permission attempts: $e');
    }
  }

  Future<void> _checkAllPermissions() async {
    final allPermissions = _permissionGroups
        .expand((group) => group.permissions)
        .map((item) => item.type)
        .toList();

    try {
      final status = await _permissionMaster.checkMultiplePermissions(
        allPermissions,
      );
      if (mounted) {
        setState(() {
          _permissionStatus.addAll(status);
        });
      }
    } catch (e) {
      _showErrorSnackBar('Failed to check permissions: $e');
    }
  }

  Future<void> _requestAllPermissions() async {
    setState(() => _isLoading = true);

    try {
      for (final group in _permissionGroups) {
        for (final permission in group.permissions) {
          await _requestSinglePermission(permission);
          // Small delay between requests for better UX
          await Future.delayed(const Duration(milliseconds: 500));
        }
      }
    } catch (e) {
      _showErrorSnackBar('Failed to request all permissions: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _requestSinglePermission(PermissionItem permission) async {
    try {
      final attempts = _permissionAttempts[permission.type.value] ?? 0;

      // Update attempt count
      setState(() {
        _permissionAttempts[permission.type.value] = attempts + 1;
      });
      await _savePermissionAttempts();

      PermissionStatus result;

      // Request specific permission based on type
      switch (permission.type) {
        case PermissionType.camera:
          result = await _permissionMaster.requestCameraPermission();
          break;
        case PermissionType.microphone:
          result = await _permissionMaster.requestMicrophonePermission();
          break;
        case PermissionType.fineLocation:
          result = await _permissionMaster.requestLocationPermission();
          break;
        case PermissionType.readStorage:
          result = await _permissionMaster.requestStoragePermission();
          break;
        case PermissionType.contacts:
          result = await _permissionMaster.requestContactsPermission();
          break;
        case PermissionType.notifications:
          result = await _permissionMaster.requestNotificationPermission();
          break;
        case PermissionType.bluetooth:
          result = await _permissionMaster.requestBluetoothPermission();
          break;
        case PermissionType.calendar:
          result = await _permissionMaster.requestCalendarPermission();
          break;
        case PermissionType.activityRecognition:
          result = await _permissionMaster
              .requestActivityRecognitionPermission();
          break;
        default:
          result = PermissionStatus.error;
      }

      if (mounted) {
        setState(() {
          _permissionStatus[permission.type.value] = result;
        });

        _showPermissionResult(permission.title, result);
      }
    } catch (e) {
      _showErrorSnackBar('Failed to request ${permission.title}: $e');
    }
  }

  void _showPermissionResult(String title, PermissionStatus result) {
    String message;
    Color backgroundColor;
    IconData icon;

    switch (result) {
      case PermissionStatus.granted:
        message = '$title permission granted!';
        backgroundColor = Colors.green;
        icon = Icons.check_circle;
        break;
      case PermissionStatus.denied:
        message = '$title permission denied';
        backgroundColor = Colors.red;
        icon = Icons.cancel;
        break;
      case PermissionStatus.openSettings:
        message = '$title requires settings change';
        backgroundColor = Colors.orange;
        icon = Icons.settings;
        break;
      case PermissionStatus.error:
        message = 'Error with $title permission';
        backgroundColor = Colors.red;
        icon = Icons.error;
        break;
      default:
        message = 'Unknown status for $title';
        backgroundColor = Colors.grey;
        icon = Icons.help;
    }

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Row(
          children: [
            Icon(icon, color: Colors.white),
            const SizedBox(width: 8),
            Expanded(child: Text(message)),
          ],
        ),
        backgroundColor: backgroundColor,
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
    );
  }

  void _showErrorSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Row(
          children: [
            const Icon(Icons.error, color: Colors.white),
            const SizedBox(width: 8),
            Expanded(child: Text(message)),
          ],
        ),
        backgroundColor: Colors.red,
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final platform = Platform.isMacOS ? 'macOS' : 'iOS';

    return Scaffold(
      appBar: AppBar(
        title: Text('Permission Master - $platform'),
        centerTitle: true,
        elevation: 0,
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _checkAllPermissions,
            tooltip: 'Refresh permissions',
          ),
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () => _permissionMaster.openAppSettings(),
            tooltip: 'Open app settings',
          ),
        ],
        bottom: TabBar(
          controller: _tabController,
          tabs: const [
            Tab(icon: Icon(Icons.security), text: 'Permissions'),
            Tab(icon: Icon(Icons.analytics), text: 'Status'),
            Tab(icon: Icon(Icons.info), text: 'Info'),
          ],
        ),
      ),
      body: _isLoading
          ? const Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  CircularProgressIndicator(),
                  SizedBox(height: 16),
                  Text('Loading permissions...'),
                ],
              ),
            )
          : TabBarView(
              controller: _tabController,
              children: [
                _buildPermissionsTab(),
                _buildStatusTab(),
                _buildInfoTab(),
              ],
            ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _isLoading ? null : _requestAllPermissions,
        icon: const Icon(Icons.security),
        label: const Text('Request All'),
        tooltip: 'Request all permissions',
      ),
    );
  }

  Widget _buildPermissionsTab() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      itemCount: _permissionGroups.length,
      itemBuilder: (context, index) {
        final group = _permissionGroups[index];
        return _buildPermissionGroup(group);
      },
    );
  }

  Widget _buildPermissionGroup(PermissionGroup group) {
    return Card(
      margin: const EdgeInsets.only(bottom: 16),
      child: ExpansionTile(
        leading: Icon(group.icon, color: Theme.of(context).primaryColor),
        title: Text(
          group.title,
          style: Theme.of(
            context,
          ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
        ),
        children: group.permissions
            .map((permission) => _buildPermissionTile(permission))
            .toList(),
      ),
    );
  }

  Widget _buildPermissionTile(PermissionItem permission) {
    final status = _permissionStatus[permission.type.value];
    final attempts = _permissionAttempts[permission.type.value] ?? 0;
    final isGranted = status == PermissionStatus.granted;

    return ListTile(
      leading: CircleAvatar(
        backgroundColor: permission.color.withValues(alpha: 0.1),
        child: Icon(permission.icon, color: permission.color),
      ),
      title: Text(permission.title),
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(permission.description),
          if (attempts > 0)
            Text(
              'Requested $attempts ${attempts == 1 ? 'time' : 'times'}',
              style: Theme.of(
                context,
              ).textTheme.bodySmall?.copyWith(color: Colors.grey[600]),
            ),
        ],
      ),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (status != null)
            Icon(
              isGranted ? Icons.check_circle : Icons.cancel,
              color: isGranted ? Colors.green : Colors.red,
            ),
          const SizedBox(width: 8),
          ElevatedButton(
            onPressed: () => _requestSinglePermission(permission),
            style: ElevatedButton.styleFrom(
              backgroundColor: isGranted ? Colors.green : null,
              foregroundColor: isGranted ? Colors.white : null,
            ),
            child: Text(isGranted ? 'Granted' : 'Request'),
          ),
        ],
      ),
    );
  }

  Widget _buildStatusTab() {
    final allPermissions = _permissionGroups
        .expand((group) => group.permissions)
        .toList();

    final grantedCount = allPermissions
        .where(
          (p) => _permissionStatus[p.type.value] == PermissionStatus.granted,
        )
        .length;

    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Permission Overview',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 16),
                  LinearProgressIndicator(
                    value: allPermissions.isEmpty
                        ? 0
                        : grantedCount / allPermissions.length,
                    backgroundColor: Colors.grey[300],
                    valueColor: AlwaysStoppedAnimation<Color>(
                      Theme.of(context).primaryColor,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    '$grantedCount of ${allPermissions.length} permissions granted',
                    style: Theme.of(context).textTheme.bodyMedium,
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          Expanded(
            child: ListView.builder(
              itemCount: allPermissions.length,
              itemBuilder: (context, index) {
                final permission = allPermissions[index];
                final status = _permissionStatus[permission.type.value];
                return _buildStatusTile(permission, status);
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildStatusTile(PermissionItem permission, PermissionStatus? status) {
    Color statusColor;
    String statusText;
    IconData statusIcon;

    switch (status) {
      case PermissionStatus.granted:
        statusColor = Colors.green;
        statusText = 'Granted';
        statusIcon = Icons.check_circle;
        break;
      case PermissionStatus.denied:
        statusColor = Colors.red;
        statusText = 'Denied';
        statusIcon = Icons.cancel;
        break;
      case PermissionStatus.openSettings:
        statusColor = Colors.orange;
        statusText = 'Settings Required';
        statusIcon = Icons.settings;
        break;
      case PermissionStatus.error:
        statusColor = Colors.red;
        statusText = 'Error';
        statusIcon = Icons.error;
        break;
      default:
        statusColor = Colors.grey;
        statusText = 'Unknown';
        statusIcon = Icons.help;
    }

    return ListTile(
      leading: Icon(permission.icon, color: permission.color),
      title: Text(permission.title),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(statusIcon, color: statusColor, size: 20),
          const SizedBox(width: 4),
          Text(
            statusText,
            style: TextStyle(color: statusColor, fontWeight: FontWeight.bold),
          ),
        ],
      ),
    );
  }

  Widget _buildInfoTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Platform Information',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 16),
                  _buildInfoRow('Platform', Platform.operatingSystem),
                  _buildInfoRow('Version', _platformVersion),
                  _buildInfoRow('Plugin', 'Permission Master'),
                  _buildInfoRow('Updated', 'January 2026'),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Features',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 16),
                  _buildFeatureTile(
                    'iOS 26 & macOS 26 Support',
                    'Full compatibility with latest OS versions',
                    Icons.system_update,
                  ),
                  _buildFeatureTile(
                    'New Calendar API',
                    'Updated to use latest EventKit APIs',
                    Icons.calendar_today,
                  ),
                  _buildFeatureTile(
                    'Enhanced Privacy',
                    'Respects user privacy preferences',
                    Icons.privacy_tip,
                  ),
                  _buildFeatureTile(
                    'Smart Requests',
                    'Intelligent permission request handling',
                    Icons.psychology,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildInfoRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            label,
            style: Theme.of(
              context,
            ).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold),
          ),
          Text(value, style: Theme.of(context).textTheme.bodyMedium),
        ],
      ),
    );
  }

  Widget _buildFeatureTile(String title, String description, IconData icon) {
    return ListTile(
      leading: Icon(icon, color: Theme.of(context).primaryColor),
      title: Text(title),
      subtitle: Text(description),
      contentPadding: EdgeInsets.zero,
    );
  }
}

// Data models for better organization
class PermissionGroup {
  final String title;
  final IconData icon;
  final List<PermissionItem> permissions;

  PermissionGroup({
    required this.title,
    required this.icon,
    required this.permissions,
  });
}

class PermissionItem {
  final PermissionType type;
  final String title;
  final String description;
  final IconData icon;
  final Color color;

  PermissionItem({
    required this.type,
    required this.title,
    required this.description,
    required this.icon,
    required this.color,
  });
}

class PermissionDemoPage extends StatefulWidget {
  const PermissionDemoPage({super.key});

  @override
  State<PermissionDemoPage> createState() => _PermissionDemoPageState();
}

class _PermissionDemoPageState extends State<PermissionDemoPage> {
  final PermissionMaster _permissionMaster = PermissionMaster();
  final Map<String, PermissionStatus> _permissionStatus = {};
  final Map<String, int> _permissionAttempts = {};
  final GetStorageBridge _storage = GetStorageBridge();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      PermissionMaster.setContext(context);
      _checkInitialPermissions();
    });

    _loadPermissionAttempts();
  }

  @override
  void dispose() {
    super.dispose();
  }

  Future<void> _loadPermissionAttempts() async {
    final attempts = await _storage.read<Map<String, int>>(
      'permission_attempts',
      {},
    );
    setState(() {
      _permissionAttempts.addAll(attempts);
    });
  }

  Future<void> _savePermissionAttempts() async {
    await _storage.write('permission_attempts', _permissionAttempts);
  }

  Future<void> _checkInitialPermissions() async {
    final permissions = [
      PermissionType.camera,
      PermissionType.fineLocation,
      PermissionType.backgroundLocation,
      PermissionType.readStorage,
      PermissionType.writeStorage,
      PermissionType.microphone,
      PermissionType.contacts,
      PermissionType.bluetooth,
      PermissionType.bluetoothAdmin,
      PermissionType.bluetoothScan,
      PermissionType.bluetoothAdvertise,
      PermissionType.bluetoothConnect,
      PermissionType.bodySensors,
      PermissionType.accessWifi,
      PermissionType.changeWifi,
      PermissionType.sms,
      PermissionType.notifications,
      PermissionType.alarm,
      PermissionType.calendar,
      PermissionType.phone,
      PermissionType.activityRecognition,
      PermissionType.nearbyDevices,
    ];

    final status = await _permissionMaster.checkMultiplePermissions(
      permissions,
    );
    if (mounted) {
      setState(() {
        _permissionStatus.addAll(status);
      });
    }
  }

  Future<void> _checkSelectedPermissions() async {
    final permissions = [
      PermissionType.camera,
      PermissionType.fineLocation,
      PermissionType.microphone,
      PermissionType.contacts,
      PermissionType.notifications,
    ];
    final status = await _permissionMaster.checkMultiplePermissions(
      permissions,
    );
    if (!mounted) return;
    setState(() {
      _permissionStatus.addAll(status);
    });
  }

  Future<void> _requestSelectedPermissionsSequentially() async {
    try {
      final cam = await _permissionMaster.requestCameraPermission();
      if (!mounted) return;
      setState(() {
        _permissionStatus[PermissionType.camera.value] = cam;
        _permissionAttempts[PermissionType.camera.value] =
            (_permissionAttempts[PermissionType.camera.value] ?? 0) + 1;
      });

      final loc = await _permissionMaster.requestLocationPermission();
      if (!mounted) return;
      setState(() {
        _permissionStatus[PermissionType.fineLocation.value] = loc;
        _permissionAttempts[PermissionType.fineLocation.value] =
            (_permissionAttempts[PermissionType.fineLocation.value] ?? 0) + 1;
      });

      final mic = await _permissionMaster.requestMicrophonePermission();
      if (!mounted) return;
      setState(() {
        _permissionStatus[PermissionType.microphone.value] = mic;
        _permissionAttempts[PermissionType.microphone.value] =
            (_permissionAttempts[PermissionType.microphone.value] ?? 0) + 1;
      });

      final con = await _permissionMaster.requestContactsPermission();
      if (!mounted) return;
      setState(() {
        _permissionStatus[PermissionType.contacts.value] = con;
        _permissionAttempts[PermissionType.contacts.value] =
            (_permissionAttempts[PermissionType.contacts.value] ?? 0) + 1;
      });

      final noti = await _permissionMaster.requestNotificationPermission();
      if (!mounted) return;
      setState(() {
        _permissionStatus[PermissionType.notifications.value] = noti;
        _permissionAttempts[PermissionType.notifications.value] =
            (_permissionAttempts[PermissionType.notifications.value] ?? 0) + 1;
      });

      await _savePermissionAttempts();
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Error requesting multiple permissions: $e'),
          backgroundColor: Colors.red,
        ),
      );
    }
  }

  Future<void> _requestPermission(
    String title,
    String permission,
    Future<PermissionStatus> Function() requestFunc,
  ) async {
    try {
      final attempts = _permissionAttempts[permission] ?? 0;

      if (attempts >= 2 &&
          _permissionStatus[permission] != PermissionStatus.granted) {
        final status = await _permissionMaster.checkPermissionStatus(
          permission,
        );

        if (status != PermissionStatus.granted) {
          final bool? openSettings = await showDialog<bool>(
            context: context,
            builder: (BuildContext context) => AlertDialog(
              title: Text('$title Permission Required'),
              content: const Text(
                'You have denied this permission multiple times. '
                'Please enable it from settings to use this feature.',
              ),
              actions: [
                TextButton(
                  child: const Text('Cancel'),
                  onPressed: () => Navigator.pop(context, false),
                ),
                TextButton(
                  child: const Text('Open Settings'),
                  onPressed: () => Navigator.pop(context, true),
                ),
              ],
            ),
          );

          if (openSettings == true) {
            try {
              await _permissionMaster.openAppSettings();

              // کمی صبر کنید تا کاربر برگردد
              await Future.delayed(const Duration(seconds: 1));

              final newStatus = await _permissionMaster.checkPermissionStatus(
                permission,
              );
              _updatePermissionStatus(title, permission, newStatus);
            } catch (e) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Failed to open settings: $e'),
                  backgroundColor: Colors.red,
                ),
              );
            }
          }
          return;
        }
      }

      // کد اصلی برای درخواست مجوز (بدون تغییر)
      if (attempts > 0 &&
          _permissionStatus[permission] != PermissionStatus.granted) {
        final bool? shouldRequest = await showDialog<bool>(
          context: context,
          builder: (BuildContext context) => AlertDialog(
            title: Text('$title Permission Required'),
            content: Text(
              'This permission is required for $title functionality. '
              'Would you like to grant it now?',
            ),
            actions: [
              TextButton(
                child: const Text('No, thanks'),
                onPressed: () => Navigator.pop(context, false),
              ),
              TextButton(
                child: const Text('Grant Permission'),
                onPressed: () => Navigator.pop(context, true),
              ),
            ],
          ),
        );

        if (shouldRequest != true) {
          return;
        }
      }

      setState(() {
        _permissionAttempts[permission] = attempts + 1;
      });
      await _savePermissionAttempts();

      final result = await requestFunc();
      _updatePermissionStatus(title, permission, result);
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Error requesting $title permission: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

  void _updatePermissionStatus(
    String title,
    String permission,
    PermissionStatus result,
  ) {
    if (mounted) {
      setState(() {
        _permissionStatus[permission] = result;
      });

      String message;
      Color backgroundColor;
      switch (result) {
        case PermissionStatus.granted:
          message = '$title permission granted!';
          backgroundColor = Colors.green;
          break;
        case PermissionStatus.denied:
          message = '$title permission denied!';
          backgroundColor = Colors.red;
          break;
        case PermissionStatus.openSettings:
          message = '$title permission requires settings change.';
          backgroundColor = Colors.orange;
          break;
        case PermissionStatus.error:
          message = 'Error with $title permission!';
          backgroundColor = Colors.red;
          break;
        default:
          message = 'Unknown status for $title permission!';
          backgroundColor = Colors.red;
      }

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(message), backgroundColor: backgroundColor),
      );
    }
  }

  Widget _buildPermissionCard({
    required String title,
    required String description,
    required IconData icon,
    required String permission,
    required Future<PermissionStatus> Function() onRequest,
  }) {
    final status = _permissionStatus[permission];
    final attempts = _permissionAttempts[permission] ?? 0;

    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: ListTile(
        leading: Icon(icon, color: Theme.of(context).primaryColor),
        title: Text(title),
        subtitle: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(description),
            if (attempts > 0)
              Text(
                'Requested $attempts ${attempts == 1 ? 'time' : 'times'}',
                style: Theme.of(context).textTheme.bodySmall,
              ),
          ],
        ),
        trailing: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            if (status != null)
              Icon(
                status == PermissionStatus.granted
                    ? Icons.check_circle
                    : Icons.cancel,
                color: status == PermissionStatus.granted
                    ? Colors.green
                    : Colors.red,
              ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: attempts >= 2 && status != PermissionStatus.granted
                  ? () => _requestPermission(title, permission, onRequest)
                  : () => _requestPermission(title, permission, onRequest),
              child: Text(
                attempts >= 2 && status != PermissionStatus.granted
                    ? 'Open Settings'
                    : 'Request',
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Permission Master Demo'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _checkInitialPermissions,
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.only(bottom: 100),
        child: ListView(
          children: [
            Card(
              margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              child: ListTile(
                leading: const Icon(Icons.layers),
                title: const Text('Multiple permissions (example)'),
                subtitle: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Demo of checkMultiplePermissions and sequential requests',
                    ),
                    const SizedBox(height: 8),
                    Wrap(
                      spacing: 8,
                      runSpacing: 8,
                      children: [
                        ElevatedButton(
                          onPressed: _checkSelectedPermissions,
                          child: const Text('Check Multiple'),
                        ),
                        ElevatedButton(
                          onPressed: _requestSelectedPermissionsSequentially,
                          child: const Text('Request Multiple'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            _buildPermissionCard(
              title: 'Camera',
              description: 'Required for taking photos and video calls',
              icon: Icons.camera_alt,
              permission: PermissionType.camera.value,
              onRequest: _permissionMaster.requestCameraPermission,
            ),
            _buildPermissionCard(
              title: 'Location',
              description: 'Required for location-based features',
              icon: Icons.location_on,
              permission: PermissionType.fineLocation.value,
              onRequest: _permissionMaster.requestLocationPermission,
            ),
            _buildPermissionCard(
              title: 'Storage',
              description: 'Required for saving files and media',
              icon: Icons.storage,
              permission: PermissionType.readStorage.value,
              onRequest: _permissionMaster.requestStoragePermission,
            ),
            _buildPermissionCard(
              title: 'Microphone',
              description: 'Required for voice messages and calls',
              icon: Icons.mic,
              permission: PermissionType.microphone.value,
              onRequest: _permissionMaster.requestMicrophonePermission,
            ),
            _buildPermissionCard(
              title: 'Contacts',
              description: 'Required for finding your friends',
              icon: Icons.contacts,
              permission: PermissionType.contacts.value,
              onRequest: _permissionMaster.requestContactsPermission,
            ),
            _buildPermissionCard(
              title: 'Bluetooth',
              description: 'Required for connecting to Bluetooth devices',
              icon: Icons.bluetooth,
              permission: PermissionType.bluetooth.value,
              onRequest: _permissionMaster.requestBluetoothPermission,
            ),
            _buildPermissionCard(
              title: 'Sensors',
              description: 'Required for fitness tracking features',
              icon: Icons.sensors,
              permission: PermissionType.bodySensors.value,
              onRequest: _permissionMaster.requestSensorsPermission,
            ),
            _buildPermissionCard(
              title: 'WiFi',
              description: 'Required for WiFi connectivity features',
              icon: Icons.wifi,
              permission: PermissionType.accessWifi.value,
              onRequest: _permissionMaster.requestWifiPermission,
            ),
            _buildPermissionCard(
              title: 'SMS',
              description: 'Required for sending and receiving SMS',
              icon: Icons.sms,
              permission: PermissionType.sms.value,
              onRequest: _permissionMaster.requestSmsPermission,
            ),
            _buildPermissionCard(
              title: 'Notifications',
              description: 'Required for sending notifications',
              icon: Icons.notifications,
              permission: PermissionType.notifications.value,
              onRequest: _permissionMaster.requestNotificationPermission,
            ),
            _buildPermissionCard(
              title: 'Alarm',
              description: 'Required for scheduling alarms',
              icon: Icons.alarm,
              permission: PermissionType.alarm.value,
              onRequest: _permissionMaster.requestAlarmPermission,
            ),
            _buildPermissionCard(
              title: 'Calendar',
              description: 'Required for calendar access',
              icon: Icons.calendar_today,
              permission: PermissionType.calendar.value,
              onRequest: _permissionMaster.requestCalendarPermission,
            ),
            _buildPermissionCard(
              title: 'Phone',
              description: 'Required for call-related features',
              icon: Icons.phone,
              permission: PermissionType.phone.value,
              onRequest: _permissionMaster.requestPhonePermission,
            ),
            _buildPermissionCard(
              title: 'Activity Recognition',
              description: 'Required for fitness tracking',
              icon: Icons.directions_run,
              permission: PermissionType.activityRecognition.value,
              onRequest: _permissionMaster.requestActivityRecognitionPermission,
            ),
            _buildPermissionCard(
              title: 'Nearby Devices',
              description: 'Required for discovering nearby devices',
              icon: Icons.devices_other,
              permission: PermissionType.nearbyDevices.value,
              onRequest: _permissionMaster.requestNearbyDevicesPermission,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => const TestScreen()),
          );
        },
        tooltip: 'Advanced Tests',
        child: const Icon(Icons.science),
      ),
    );
  }
}
31
likes
150
points
246
downloads

Publisher

verified publisherswanflutterdev.com

Weekly Downloads

**Permission Master** is a **Flutter** plugin for managing and requesting permissions on **All Platform**.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on permission_master

Packages that implement permission_master