Run this sample code on Android 10 or higher:
PermissionStatus.permanentlyDenied (as we have permanently denied the permission in the last step), but it displays PermissionStatus.denied. class HomePage extends StatelessWidget {
final PermissionWithService _permission = Permission.locationWhenInUse;
void _checkPermission() async {
final status = await _permission.status;
print('Status = $status');
if (status == PermissionStatus.denied) {
await _permission.request();
} else if (status == PermissionStatus.permanentlyDenied) {
print('Permanently Denied'); // Never runs.
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: _checkPermission,
child: Text('Check permission'),
),
),
);
}
}
Version: ^8.0.0+2
Platform:
The Permission.<your_permission>.status method is no longer able to return permanentlyDenied on Android (due to changes in Android API 30). The Permission.<your_permission>.status method is only returns denied or granted.
The status permanentlyDenied was never supported by Android, but pre-API 30 it was possible to use a workaround by storing earlier requested permissions and checking the shouldShowRequestPermissionRationale. Since API 30 and especially the introduction of the "Ask Every Time" permission level this workaround is causing serious issues. This is why we decided to no longer support it from version 6.0.0 and higher.
The only way to reliably return when permissions are permanently denied is from the return value of the Permission.<your_permission>.request() API. This will return you that permissions are denied permanently when the user selected the "don't ask again" checkbox (pre API 30) or after the user denied permission for a second time on API 30 and higher.
For a more detailed explanation please checkout our wiki page: https://github.com/Baseflow/flutter-permission-handler/wiki/Changes-in-6.0.0#breaking-changes.
I will close this issue, however feel free to leave a comment if you feel this doesn't answer your question.
@mvanbeusekom Thanks for your explanation. It all made sense.
However, I'd suggest you to add something to the docs because some stupid people like me won't read the breaking changes and currently the documentation reads:
The user denied access to the requested feature and selected to never again show a request for this permission. The user may still change the permission status in the settings.
Only supported on Android.
A link to the breaking change page or something like the following could be helpful.
The only way to reliably return when permissions are permanently denied is from the return value of the Permission.
.request()
@mvanbeusekom
I also noticed that on iOS you get PermissionStatus.permanentlyDenied (which should not happen in iOS). Here's the minimal code:
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () async => print(await Permission.locationWhenInUse.request()), // PermissionStatus.permanentlyDenied
child: Text('Location')
),
),
);
}
}
@dark-chocolate this is on purpose since it makes the platform independent flow easier to understand:
PermissionStatus.denied means permissions are unknown or denied but you can try again;PermissionStatus.permanentlyDenied means permissions have been denied and the user should grant permissions through the OS "Settings" (permissions dialogs will not be shown when requesting permission).Otherwise you get in the situation where on Android PermissionStatus.denied means "permission status unknown or denied but you are allowed to request them" while on iOS the PermissionStatus.denied would mean "permission status denied, send the user to the Settings screen".
@mvanbeusekom I must say that you shouldn't let others write the documentation because docs mentions something else:

@dark-chocolate that unfortunately slipped through the cracks with the recent Android API 30 changes (version 6.0.0). I will make sure it gets updated.
@mvanbeusekom I forgot to mention that, in the shared code snippet, the device doesn't show the location permission dialog. Earlier the status was PermissionStatus.unknown which in my opinion was much easier to understand. But currently it returns PermissionStatus.permanentlyDenied without even showing a location prompt. Is there anything I'm not doing right (which again docs didn't mention)?
Could it be that you didn't enable the permissions in your Podfile?
Starting from version 8.0.0 we flipped the configuration in the Podfile:
P.S. this is documented in the README and we added a warning in the CHANGELOG, but maybe you missed it.
@mvanbeusekom I'm sorry I should have given a read. I truly appreciate your efforts. Without being a patron, is there any way to donate some money via PayPal?
Thanks :)
@dark-chocolate no worries, you are not the only one with this issue but you are the only one willing to help out and verify this is indeed the problem. So thanks to your response we can now help out some other users as well.
In other words, there is no need to donate, just keep up the good work on keeping us sharp and helping out by reporting problems you face. It really is much appreciated. Of course there is still the option to become a sponsor ;)
Hi, i have a problem with permissions on ios. I have pub version 8.1.2 and I have updated the podfile including the permissions i need. With the localization I have no problems but I can't access the camera because it tells me "PermissionStatus.permanentlyDenied"
I'm getting permanentlyDenied now for contacts on all iOS simlators on v8+
Going to try a new one and a physical device too.
@ghenry did you update your ios/Podfile configuration to enable contact permissions explicitly (see this comment)?
Most helpful comment
The
Permission.<your_permission>.statusmethod is no longer able to returnpermanentlyDeniedon Android (due to changes in Android API 30). ThePermission.<your_permission>.statusmethod is only returnsdeniedorgranted.The status
permanentlyDeniedwas never supported by Android, but pre-API 30 it was possible to use a workaround by storing earlier requested permissions and checking theshouldShowRequestPermissionRationale. Since API 30 and especially the introduction of the "Ask Every Time" permission level this workaround is causing serious issues. This is why we decided to no longer support it from version 6.0.0 and higher.The only way to reliably return when permissions are permanently denied is from the return value of the
Permission.<your_permission>.request()API. This will return you that permissions are denied permanently when the user selected the "don't ask again" checkbox (pre API 30) or after the user denied permission for a second time on API 30 and higher.For a more detailed explanation please checkout our wiki page: https://github.com/Baseflow/flutter-permission-handler/wiki/Changes-in-6.0.0#breaking-changes.
I will close this issue, however feel free to leave a comment if you feel this doesn't answer your question.