'How to request and check permissions in Flutter

I am using a various plugin to get user data, contact, photos and camera when the user clicks Don't allow, The application goes silent. I want to show the user the ask permission dialog when the user checks the Never ask again and Don't allow and enters the application again.

Currently the app never ask to again when the user checks Don't allow

I Know Users must grant permission for an app to access personal information, including the current location, camera, calendar, contacts, mediaLibrary, microphone, sensors, speech and photos. Although people appreciate the convenience of using an app that has access to this information, they also expect to have control over their private data. For example, people like being able to automatically tag photos with their physical location or find nearby friends, but they also want the option to disable such features.

How to ask for user permission again in flutter?



Solution 1:[1]

For simplicity, I used location permission. To request another permission, just replace location with that permission. Here is the list of all permissions.

1. Android setup:

  1. Add these to android/grade.properties file:

    android.useAndroidX=true
    android.enableJetifier=true
    
  2. In android/app/build.gradle file:

    android {
      compileSdkVersion 30 // Set this to at least 30
      ...
    }
    
  3. Add the permission to android/app/src/main/AndroidManifest.xml file

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
      ...
     </manifest>
    

2. iOS setup:

  1. Add this to info.plist file:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App needs location permission to work</string>
    
  2. Add PERMISSION_LOCATION=1 to Podfile:

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        flutter_additional_ios_build_settings(target)
    
        target.build_configurations.each do |config|
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
            '$(inherited)',
    
            ## Add the following line.
             'PERMISSION_LOCATION=1'
          ]
    
        end
      end
    end
    

3. Flutter setup:

Add this to the pubspec.yaml file:

permission_handler: ^8.0.0+2

Main work:

  • Check for a permission:

    To check if the location aka GPS is on.

    final serviceStatus = await Permission.locationWhenInUse.serviceStatus;
    bool isGpsOn = serviceStatus == ServiceStatus.enabled;
    
  • Request a permission:

    final status = await Permission.locationWhenInUse.request();
    if (status == PermissionStatus.granted) {
      print('Permission granted');
    } else if (status == PermissionStatus.denied) {
      print('Denied. Show a dialog with a reason and again ask for the permission.');
    } else if (status == PermissionStatus.permanentlyDenied) {
      print('Take the user to the settings page.');
    }
    

Full code:

class HomePage extends StatelessWidget {
  Future<void> _checkPermission() async {
    final serviceStatus = await Permission.locationWhenInUse.serviceStatus;
    final isGpsOn = serviceStatus == ServiceStatus.enabled;
    if (!isGpsOn) {
      print('Turn on location services before requesting permission.');
      return;
    }

    final status = await Permission.locationWhenInUse.request();
    if (status == PermissionStatus.granted) {
      print('Permission granted');
    } else if (status == PermissionStatus.denied) {
      print('Permission denied. Show a dialog and again ask for the permission');
    } else if (status == PermissionStatus.permanentlyDenied) {
      print('Take the user to the settings page.');
      await openAppSettings();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: _checkPermission,
          child: Text('Check Permission'),
        ),
      ),
    );
  }
}

Solution 2:[2]

I recommend using permission_handler library and write abstract code (strategy pattern) to handle all permissions the same way. Usually, docs are blurry and they do not show how to deal with unrecoverable/disabled scenarios.

Code:

/// handles .isLimited for iOS 14+ where we can restrict access.
abstract class GrantPermissionStrategy {
  final Permission permission;

  GrantPermissionStrategy(this.permission);

  Future<void> request({
    required final OnPermanentlyDenied onPermanentlyDenied,
    required final OnGranted onGranted,
  }) async {
    PermissionStatus status = await permission.status;
    print("GrantPermissionStrategy status: $status");

    if (!status.isLimited && !status.isGranted) {
      final PermissionStatus result = await permission.request();
      if (result.isPermanentlyDenied) {
        onPermanentlyDenied.call();
        return;
      }
      if (!result.isGranted) {
        return;
      }
    }
    onGranted.call();
  }
}

typedef OnPermanentlyDenied = void Function();

typedef OnGranted = void Function();


And the, you can make concrete implementation, like:

class GrantPermissionCameraStrategy extends GrantPermissionStrategy {
  GrantPermissionCameraStrategy() : super(Permission.camera);
}

class GrantPermissionPhotosStrategy extends GrantPermissionStrategy {
  GrantPermissionPhotosStrategy() : super(Platform.isAndroid ? Permission.storage : Permission.photos);
}

And finally, invoke it!:

    await GrantPermissionPhotosStrategy().request(onPermatentlyDenied: () {
      // launch dialog, make user go to app settings
    }, onGranted: () async {
      // we have passed! Launch the feature.
    });
  }

Solution 3:[3]

Here is my approach: Using the Location package.

import 'package:location/location.dart' as loc;

bool? _serviceEnabled = false;
loc.PermissionStatus? _permissionGranted;
loc.Location? location;

@override
  void initState() {
    location = loc.Location();
    init();
    super.initState();
  }

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

  init() async {
    setInitialLocation();
  }

  void setInitialLocation() async {
    _serviceEnabled = await location?.serviceEnabled();
    if (!_serviceEnabled!) {
      logger.d("permission service not enabled ###############");
      isLocationOn = false;
    }

    _permissionGranted = await location?.hasPermission();
    if (_permissionGranted == loc.PermissionStatus.denied) {
      logger.d("permission service denied ###############");
      isLocationOn = false;
    } else if (_permissionGranted == loc.PermissionStatus.granted) {
      logger.d("permission service granted ###############");
      isLocationOn = true;
    } else if (_permissionGranted == loc.PermissionStatus.deniedForever) {
      logger.d("permission service denied forever ###############");
      isLocationOn = false;
    } else if (_permissionGranted == loc.PermissionStatus.grantedLimited) {
      logger.d("permission service limited granted ###############");
      isLocationOn = true;
    }
    setState(() {});

    if (_permissionGranted == loc.PermissionStatus.grantedLimited ||
        _permissionGranted == loc.PermissionStatus.granted) {}
  }

And make sure to include all the basic location settings in iOS and Android (refer @CopsOnRoad answer). Please let me know if anyone need more information on this.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 CopsOnRoad
Solution 2
Solution 3 happycoder