Skip to main content

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

PlatformAndroid

One Android API for special-access permissions (usage access, overlay, accessibility, notification listener, exact alarms, roles) with status checks and Settings deep links.

example/lib/main.dart

import 'package:android_special_permissions/android_special_permissions.dart';
import 'package:flutter/material.dart';

/// Class names of the do-nothing services declared in the example manifest.
const _accessibilityService = '.ExampleAccessibilityService';
const _notificationListener = '.ExampleNotificationListener';

void main() => runApp(const ExampleApp());

/// Lists every special permission with its status and buttons to open it.
class ExampleApp extends StatelessWidget {
  /// Creates the example app.
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Special permissions',
      theme: ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true),
      home: const PermissionsPage(),
    );
  }
}

/// The single page of the example.
class PermissionsPage extends StatefulWidget {
  /// Creates the page.
  const PermissionsPage({super.key});

  @override
  State<PermissionsPage> createState() => _PermissionsPageState();
}

class _PermissionsPageState extends State<PermissionsPage>
    with WidgetsBindingObserver {
  final _permissions = AndroidSpecialPermissions();
  final _statuses = <SpecialPermission, SpecialPermissionStatus>{};
  final _roles = <AndroidRole, SpecialPermissionStatus>{};

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _refresh();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // After "Open", the user may change a toggle and come back.
    if (state == AppLifecycleState.resumed) _refresh();
  }

  String? _serviceFor(SpecialPermission permission) => switch (permission) {
        SpecialPermission.accessibilityService => _accessibilityService,
        SpecialPermission.notificationListener => _notificationListener,
        _ => null,
      };

  Future<void> _refresh() async {
    for (final permission in SpecialPermission.values) {
      _statuses[permission] = await _permissions.status(permission,
          serviceClassName: _serviceFor(permission));
    }
    for (final role in AndroidRole.values) {
      _roles[role] = await _permissions.roleStatus(role);
    }
    if (mounted) setState(() {});
  }

  void _say(String message) {
    if (!mounted) return;
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text(message)));
  }

  Future<void> _run(Future<void> Function() action) async {
    try {
      await action();
    } on SpecialPermissionException catch (e) {
      _say(e.message ?? e.code.name);
    }
    await _refresh();
  }

  Future<void> _open(SpecialPermission permission) => _run(() async {
        final opened = await _permissions.openSettings(permission,
            serviceClassName: _serviceFor(permission));
        if (!opened) _say('No Settings screen found');
      });

  Future<void> _request(SpecialPermission permission) => _run(() async {
        final status = await _permissions.request(permission,
            serviceClassName: _serviceFor(permission));
        _say('${permission.name}: ${status.name}');
      });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Special permissions'),
        actions: [
          IconButton(
            tooltip: 'App info',
            icon: const Icon(Icons.info_outline),
            onPressed: () => _run(_permissions.openAppSettings),
          ),
          IconButton(
            tooltip: 'Refresh',
            icon: const Icon(Icons.refresh),
            onPressed: _refresh,
          ),
        ],
      ),
      body: ListView(
        children: [
          for (final permission in SpecialPermission.values)
            _PermissionTile(
              title: permission.name,
              status: _statuses[permission],
              // Background location is status-only: request it with
              // permission_handler.
              onOpen: permission == SpecialPermission.backgroundLocation
                  ? null
                  : () => _open(permission),
              onRequest: permission == SpecialPermission.backgroundLocation
                  ? null
                  : () => _request(permission),
            ),
          const Divider(),
          const ListTile(title: Text('Default-app roles')),
          for (final role in AndroidRole.values)
            _PermissionTile(
              title: role.name,
              status: _roles[role],
              onRequest: () => _run(() => _permissions.requestRole(role)),
            ),
        ],
      ),
    );
  }
}

class _PermissionTile extends StatelessWidget {
  const _PermissionTile({
    required this.title,
    required this.status,
    this.onOpen,
    this.onRequest,
  });

  final String title;
  final SpecialPermissionStatus? status;
  final VoidCallback? onOpen;
  final VoidCallback? onRequest;

  @override
  Widget build(BuildContext context) {
    final applicable = status?.isApplicable ?? false;
    final (icon, color) = switch (status) {
      SpecialPermissionStatus.granted => (Icons.check_circle, Colors.green),
      SpecialPermissionStatus.denied => (Icons.cancel, Colors.red),
      SpecialPermissionStatus.notApplicable => (Icons.block, Colors.grey),
      null => (Icons.hourglass_empty, Colors.grey),
    };
    return ListTile(
      leading: Icon(icon, color: color),
      title: Text(title),
      subtitle: Text(status?.name ?? '...'),
      trailing: Wrap(
        spacing: 4,
        children: [
          if (onOpen != null)
            TextButton(
              onPressed: applicable ? onOpen : null,
              child: const Text('Open'),
            ),
          if (onRequest != null)
            FilledButton.tonal(
              onPressed: applicable ? onRequest : null,
              child: const Text('Request'),
            ),
        ],
      ),
    );
  }
}
0
likes
160
points
394
downloads

Documentation

API reference

Publisher

verified publisherabdeldjalilchougui.biz

Weekly Downloads

One Android API for special-access permissions (usage access, overlay, accessibility, notification listener, exact alarms, roles) with status checks and Settings deep links.

Homepage
Repository (GitHub)
View/report issues

Topics

#permissions #android #settings #accessibility #plugin

License

MIT (license)

Dependencies

flutter

More

Packages that depend on android_special_permissions

Packages that implement android_special_permissions