app_permissions 1.0.0 copy "app_permissions: ^1.0.0" to clipboard
app_permissions: ^1.0.0 copied to clipboard

Centralized permission management system with automatic requests, status tracking, rationale dialogs, and graceful degradation.

example/lib/main.dart

import 'package:app_permissions/app_permissions.dart';
import 'package:app_storage/app_storage.dart';
import 'package:flutter/material.dart';

import 'snackbar.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize services
  await StorageService.instance.init();
  await PermissionService.instance.initialize();

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'AppPermissions Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      home: const SplashScreen(),
    );
  }
}

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

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    _checkFirstLaunch();
  }

  Future<void> _checkFirstLaunch() async {
    await Future.delayed(const Duration(seconds: 1));

    if (!mounted) return;

    final launched = await StorageService.instance.get<bool>('launched');

    if (!mounted) return;

    if (launched == null || !launched) {
      // First launch - show onboarding
      await StorageService.instance.save('launched', true);

      if (!mounted) return;

      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (_) => const SafeOnboardingScreen()),
      );
    } else {
      // Not first launch - go to home
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (_) => const HomeScreen()),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(body: Center(child: CircularProgressIndicator()));
  }
}

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

  @override
  State<SafeOnboardingScreen> createState() => _SafeOnboardingScreenState();
}

class _SafeOnboardingScreenState extends State<SafeOnboardingScreen> {
  void _handleComplete() {
    // Method call ensures we have access to current context
    if (mounted) {
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (_) => const HomeScreen()),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return PermissionOnboardingScreen(
      groups: PermissionGroups.required,
      onComplete: _handleComplete,
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(title: const Text("Permission Examples")),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          // Example 1: Simple Permission Request
          Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(8.0),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
              side: BorderSide.none,
            ),
            child: ListTile(
              leading: Icon(Icons.camera_alt, color: theme.colorScheme.primary),
              title: const Text('Camera Permission'),
              subtitle: const Text('Simple permission request'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const CameraExampleScreen()),
              ),
            ),
          ),
          const SizedBox(height: 8.0),

          // Example 2: Permission Guard
          Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(8.0),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
              side: BorderSide.none,
            ),
            child: ListTile(
              leading: Icon(Icons.photo, color: theme.colorScheme.primary),
              title: const Text('Permission Guard'),
              subtitle: const Text('Auto-request with fallback'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const PhotoExampleScreen()),
              ),
            ),
          ),
          const SizedBox(height: 8.0),

          // Example 3: Permission Groups
          Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(8.0),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
              side: BorderSide.none,
            ),
            child: ListTile(
              leading: Icon(Icons.group, color: theme.colorScheme.primary),
              title: const Text('Permission Groups'),
              subtitle: const Text('Request multiple permissions'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const GroupExampleScreen()),
              ),
            ),
          ),
          const SizedBox(height: 8.0),

          // Example 4: Permission Status
          Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(8.0),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
              side: BorderSide.none,
            ),
            child: ListTile(
              leading: Icon(Icons.info, color: theme.colorScheme.primary),
              title: const Text('Permission Status'),
              subtitle: const Text('View all permission statuses'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const StatusExampleScreen()),
              ),
            ),
          ),
          const SizedBox(height: 8.0),

          // Example 5: Settings
          Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(8.0),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
              side: BorderSide.none,
            ),
            child: ListTile(
              leading: Icon(Icons.settings, color: theme.colorScheme.primary),
              title: const Text('Permission Settings'),
              subtitle: const Text('Manage all permissions'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => const SettingsExampleScreen(),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// Example 1: Simple Camera Permission Request
class CameraExampleScreen extends StatelessWidget {
  const CameraExampleScreen({super.key});

  Future<void> _requestCamera(BuildContext context) async {
    final result = await PermissionService.instance.requestWithRationale(
      context,
      const PermissionRequest(
        permission: AppPermission.camera,
        title: 'Camera Access Required',
        message: 'We need camera access to take photos for your profile',
        showRationale: true,
      ),
    );

    if (!context.mounted) return;

    if (result.isGranted) {
      AppSnackbar.show(
        context,
        message: 'Camera permission granted!',
        type: AppSnackbarType.success,
      );
    } else if (result.isPermanentlyDenied) {
      await PermissionService.instance.handlePermanentlyDenied(
        context,
        AppPermission.camera,
      );
    } else {
      AppSnackbar.show(
        context,
        message: 'Camera permission denied',
        type: AppSnackbarType.warning,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Camera Permission')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(32.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Icon(Icons.camera_alt, size: 100),
              const SizedBox(height: 32.0),
              const Text(
                'Camera Permission Example',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 16.0),
              const Text(
                'This example shows how to request a single permission with a custom rationale dialog.',
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 32.0),
              SizedBox(
                width: double.infinity,
                child: ElevatedButton(
                  onPressed: () => _requestCamera(context),
                  child: const Text('Request Camera Permission'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// Example 2: Permission Guard
class PhotoExampleScreen extends StatelessWidget {
  const PhotoExampleScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Permission Guard')),
      body: PermissionGuard(
        permission: AppPermission.photos,
        autoRequest: true,
        rationale: 'Photo access is needed to select images for upload',
        child: const Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.check_circle, size: 100, color: Colors.green),
              SizedBox(height: 16.0),
              Text(
                'Photo Permission Granted!',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 8.0),
              Text('You can now select and upload photos.'),
            ],
          ),
        ),
      ),
    );
  }
}

// Example 3: Permission Groups
class GroupExampleScreen extends StatelessWidget {
  const GroupExampleScreen({super.key});

  Future<void> _requestMediaGroup(BuildContext context) async {
    final results = await PermissionService.instance.requestGroup(
      PermissionGroups.media,
    );

    if (!context.mounted) return;

    final granted = results.entries
        .where((e) => e.value.isGranted)
        .map((e) => e.key);
    final denied = results.entries
        .where((e) => !e.value.isGranted)
        .map((e) => e.key);

    String message;
    AppSnackbarType type;

    if (denied.isEmpty) {
      message = 'All media permissions granted!';
      type = AppSnackbarType.success;
    } else {
      message =
          'Granted: ${granted.length}, Denied: ${denied.length} permissions';
      type = AppSnackbarType.warning;
    }

    AppSnackbar.show(context, message: message, type: type);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Permission Groups')),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          const Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              'Permission groups allow you to request multiple related permissions at once.',
              style: TextStyle(fontSize: 16),
            ),
          ),
          ...PermissionGroups.all.map(
            (group) => Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: PermissionGroupCard(
                group: group,
                onToggle: () {
                  AppSnackbar.show(
                    context,
                    message: '${group.title} permissions updated',
                    type: AppSnackbarType.info,
                  );
                },
              ),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _requestMediaGroup(context),
        child: const Icon(Icons.group_add),
      ),
    );
  }
}

// Example 4: Permission Status
class StatusExampleScreen extends StatelessWidget {
  const StatusExampleScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Permission Status')),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          const Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              'View the current status of all app permissions:',
              style: TextStyle(fontSize: 16),
            ),
          ),
          ...AppPermission.values
              .take(10)
              .map(
                (permission) => Padding(
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: PermissionStatusWidget(
                    permission: permission,
                    description: permission.defaultRationale,
                    showActionButton: true,
                  ),
                ),
              ),
        ],
      ),
    );
  }
}

// Example 5: Settings
class SettingsExampleScreen extends StatelessWidget {
  const SettingsExampleScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(title: const Text('Permission Settings')),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(Icons.info, color: theme.colorScheme.primary),
                      const SizedBox(width: 8.0),
                      const Text(
                        'Permission Management',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 16.0),
                  const Text(
                    'You can manage all app permissions here. Some permissions '
                    'are required for the app to function properly.',
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16.0),
          Card(
            child: ListTile(
              leading: const Icon(Icons.settings_applications),
              title: const Text('Open System Settings'),
              subtitle: const Text('Manage permissions in device settings'),
              trailing: const Icon(Icons.open_in_new),
              onTap: () async {
                await PermissionService.instance.openAppSettings();
              },
            ),
          ),
          const SizedBox(height: 16.0),
          Card(
            child: ListTile(
              leading: const Icon(Icons.refresh),
              title: const Text('Reset Permission States'),
              subtitle: const Text('Clear cached permission data'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () async {
                await PermissionService.instance.resetPermissionStates();
                if (context.mounted) {
                  AppSnackbar.show(
                    context,
                    message: 'Permission states reset',
                    type: AppSnackbarType.success,
                  );
                }
              },
            ),
          ),
          const SizedBox(height: 16.0),
          Card(
            child: ListTile(
              leading: const Icon(Icons.info_outline),
              title: const Text('View Request Counts'),
              subtitle: const Text('See how many times each was requested'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () async {
                final counts = await PermissionService.instance
                    .getRequestCounts();
                if (context.mounted) {
                  showDialog(
                    context: context,
                    builder: (_) => AlertDialog(
                      title: const Text('Request Counts'),
                      content: SingleChildScrollView(
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: counts.entries.map((e) {
                            return ListTile(
                              title: Text(e.key.displayName),
                              trailing: Text('${e.value}x'),
                            );
                          }).toList(),
                        ),
                      ),
                      actions: [
                        TextButton(
                          onPressed: () => Navigator.pop(context),
                          child: const Text('Close'),
                        ),
                      ],
                    ),
                  );
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
160
points
103
downloads

Publisher

verified publisherkyawzayartun.com

Weekly Downloads

Centralized permission management system with automatic requests, status tracking, rationale dialogs, and graceful degradation.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

app_settings, app_storage, flutter, permission_handler, shared_preferences

More

Packages that depend on app_permissions