Tutorials
Snippets
Subscribe on Youtube

Request Permissions in Flutter as a Service

Cover image
Slack
View Code

Requesting permissions usually become a messy task when there's a lot of paths you want to take your user on based on the permissions. To start the clean up process of all the permissions code we'll wrap it in a service with dedicated permission request functions on it.

Setup

In this tutorial we'll be using the permissions_handler package to request our permissions so lets add that to the pubspec.

# Permission checking
permission_handler: ^3.1.0

We'll create functions to request two types of permission, we'll do Location as well as Contacts. First we need to tell the OS that our app will be using these permissions.

Android

In the AndroidManifest add the uses-permission tags for the two features.

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

iOS

In the info.plist file add the keys along with your message

  <key>NSContactsUsageDescription</key>
	<string>This app requires contacts access to function.</string>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string>This app requires access to your location when in use to show relevan information.</string>
	<key>NSLocationAlwaysUsageDescription</key>
	<string>This app requires always on access to to your location to notifiy you when are near a store.</string>

Implementation

The PermissionsService will have a dedicated function on it for the permissions that you require. This way it can be called when the user wants to use the functionality that requires it. It will also be easy to write custom logic per function(group) through providing callbacks. Create a new file called permissions_service.dart and in it we'll make a class that has an instance of the PermissionHandler from the package.

import 'package:permission_handler/permission_handler.dart';

class PermissionsService {
  final PermissionHandler _permissionHandler = PermissionHandler();
}

Then we can add a generic function that takes in a PermissionGroup to request a permission of what we want.

...
 Future<bool> _requestPermission(PermissionGroup permission) async {
    var result = await _permissionHandler.requestPermissions([permission]);
    if (result[permission] == PermissionStatus.granted) {
      return true;
    }

    return false;
  }
...

And using this function we can create specific functions for each permission.

...
  /// Requests the users permission to read their contacts.
  Future<bool> requestContactsPermission() async {
    return _requestPermission(PermissionGroup.contacts);
  }

  /// Requests the users permission to read their location when the app is in use
  Future<bool> requestLocationPermission() async {
    return _requestPermission(PermissionGroup.locationWhenInUse);
  }
...

Custom Permission Logic

Many of the times when it comes to permissions we want to do something custom or want to prompt the user again to allow us the permissions. Because we have dedicated functions we can now handle each permission request in a custom way. Lets say the app needs to have access to the contacts to work, something like whatsapp. We can supply a function that will be called when the permission is denied so that we can show a dialog on the outside. Checkout this tutorial for quick dialogs

We'll pass in a onPermissionDenied function that will get called when the user declines a permission.

 /// Requests the users permission to read their contacts.
  Future<bool> requestContactsPermission({Function onPermissionDenied}) async {
    var granted = await _requestPermission(PermissionGroup.contacts);
    if (!granted) {
      onPermissionDenied();
    }
    return granted;
  }

Now on the outside you can pass in your function, show your dialog when it's denied and then request it again if the user chooses to do so :)

Has permissions

Another thing is to check if the app has a permission already. We'll create the same setup, a generic function that takes in a PermissionGroup and then use dedicated functions for specific permissions. You don't have to do this I just find it easier to maintain and it makes the outside code less dependent on the PermissionHandler package.

...
  Future<bool> hasContactsPermission() async {
    return hasPermission(PermissionGroup.contacts);
  }

  Future<bool> hasPermission(PermissionGroup permission) async {
    var permissionStatus =
        await _permissionHandler.checkPermissionStatus(permission);
    return permissionStatus == PermissionStatus.granted;
  }
...

Usage

In the code we can now use the service to request permissions like this.

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: Center(
          child: MaterialButton(
            color: Colors.yellow[300],
            child: Text('Request contacts permission'),
            onPressed: () {
              PermissionsService().requestContactsPermission(
                  onPermissionDenied: () {
                print('Permission has been denied');
              });
            },
          ),
        ),
      ));
  }
}

Services should be injected or located as shown here using provider only or in an architecture like this with Provider and get_it. Having your functionality wrapped in a service removes any relation between your code and third party implementation details so it's always a preferred solution for me.

Check out more of the snippets on the site. New snippets every weekday.

Slack
View Code