英文:
Failing to show second popup to ask for always allow Location permission on iOS Flutter
问题
我已经实现了使用Geolocator请求渐进式“位置”权限的功能,因为我的应用需要后台位置,并且Android和iOS平台都要求只有在“使用中”权限被授予后才能请求它。问题是,尽管在Android上一切都按预期工作,第二次使用“显著披露”请求权限时会打开“位置权限”屏幕,但在iOS上没有显示第二个弹出窗口要求将权限更改为“始终允许”,只返回“LocationPermission.whileInUse”状态。
我在运行iOS 12.5的iPhone6和运行iOS 16的模拟器上都尝试过,但第二个系统弹出窗口未出现。
我是否错误地期望第二次请求权限时会出现第二个系统弹出窗口?
以下是控制台中的输出:
// 在开始时
flutter: LocationBloc.getLocationPermission value is denied
// 在第一次请求时系统弹出窗口出现
flutter: LocationBloc._requestLocationPermission value is whileInUse
// 在第二次请求时系统弹出窗口不出现
flutter: TrackingRepository.getLocationPermission() LocationPermission is: LocationPermission.whileInUse
flutter: TrackingBloc._getBackgroundLocationPermission value is whileInUse
flutter: TrackingRepository.requestLocationPermission() LocationPermission is: LocationPermission.whileInUse
flutter: TrackingBloc._requestLocationPermission value is whileInUse
这是用于请求权限的方法:
Future<String> requestLocationPermission() async {
return await locationManager.checkPermission().then((value) async {
late String permission;
if (value != LocationPermission.always) {
permission =
await locationManager.requestPermission().then((value) async {
print('TrackingRepository.requestLocationPermission() LocationPermission is: $value');
switch (value) {
case LocationPermission.denied:
return 'denied';
case LocationPermission.deniedForever:
return 'deniedForever';
case LocationPermission.whileInUse:
return 'whileInUse';
case LocationPermission.always:
return 'always';
case LocationPermission.unableToDetermine:
return 'unableToDetermine';
}
}).catchError((e) {
print('TrackingRepository.requestLocationPermission() error: $e');
});
}
return permission;
});
}
我已将“Deployment target”设置为12.4,并在“info.plist”中添加了两个条目,如下所示:
<key>NSLocationWhenInUseUsageDescription</key>
<string>fixit需要您的位置以启用其功能</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>fixit需要您的位置以便在后台跟踪您的路线</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>fixit需要您的位置以便在后台跟踪您的路线</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
</array>
我还在“Signing & Capabilities/Background Modes”中添加了“Location updates”。
这是位置流方法,区分了Android和iOS的设置:
Stream<Position> startTracking() {
_positionStreamController = new StreamController<Position>.broadcast();
locationManager.checkPermission().then((value) async {
if (value == LocationPermission.denied ||
value == LocationPermission.deniedForever ||
value == LocationPermission.whileInUse) {
await locationManager.requestPermission().then((value) {
print('TrackingRepository.startTracking() requestPermission is: $value');
}).catchError((e) {
print('TrackingRepository.startTracking() requestPermission error: $e');
});
}
}).catchError((e) {
print('TrackingRepository.startTracking() error: $e');
});
late var locationSettings = Platform.isIOS
? AppleSettings(
accuracy: LocationAccuracy.bestForNavigation,
allowBackgroundLocationUpdates: true,
showBackgroundLocationIndicator: true,
activityType: ActivityType.otherNavigation)
: LocationSettings(accuracy: LocationAccuracy.best, distanceFilter: 0);
_positionSubscription = locationManager
.getPositionStream(locationSettings: locationSettings)
.listen((event) {
_positionStreamController.sink.add(event);
});
return _positionStreamController.stream;
}
现在,当我在运行iOS 12.5的iPhone 6上尝试时,系统弹出窗口只显示“接受”和“拒绝”选项,但我原本期望也会有一个“始终允许”选项,因此我必须手动选择它,否则将不会接收后台位置更新。
是否应该出现第二个系统弹出窗口以允许更改权限?
是否我遗漏了某些设置?
非常感谢。
英文:
I've implemented a progressive Location
permission request using Geolocator
as I need background location for my app and both Android
and iOS
platforms require it to be asked only after being granted the while in use
permission. The problem is that while on Android
it all works as expected and the second time I request permission with a Prominent Disclosure
it opens the Location Permission
screen, on iOS
is not showing a second pop-up asking to change the permission to always allow
and just returns the LocationPermission.whileInUse
status.
Tried both on iPhone6 running iOS
12.5 and Simulator running iOS 16
but the second system popup doesn't appear.
I'm I wrong expecting to see a second system popup when requesting permission a second time?
Here are the prints from the console:
// at start
flutter: LocationBloc.getLocationPermission value is denied
// at first request system popup appears
flutter: LocationBloc._requestLocationPermission value is whileInUse
// at second request system popup doesn't appear
flutter: TrackingRepository.getLocationPermission() LocationPermission is: LocationPermission.whileInUse
flutter: TrackingBloc._getBackgroundLocationPermission value is whileInUse
flutter: TrackingRepository.requestLocationPermission() LocationPermission is: LocationPermission.whileInUse
flutter: TrackingBloc._requestLocationPermission value is whileInUse
This is the method used to request permission:
Future<String> requestLocationPermission() async {
return await locationManager.checkPermission().then((value) async {
late String permission;
if (value != LocationPermission.always) {
permission =
await locationManager.requestPermission().then((value) async {
print(
'TrackingRepository.requestLocationPermission() LocationPermission is: $value');
switch (value) {
case LocationPermission.denied:
return 'denied';
case LocationPermission.deniedForever:
return 'deniedForever';
case LocationPermission.whileInUse:
return 'whileInUse';
case LocationPermission.always:
return 'always';
case LocationPermission.unableToDetermine:
return 'unableToDetermine';
}
}).catchError((e) {
print('TrackingRepository.requestLocationPermission() error: $e');
});
}
return permission;
});
}
I have set Deployment target: 12.4
, added two entries in info.plist
as
<key>NSLocationWhenInUseUsageDescription</key>
<string>fixit needs you position to enable its functionalities</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>fixit needs your position to enable you to track your routes even when is in background</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>fixit needs your position to enable you to track your routes even when is in background</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
</array>
I also added the Location updates
in Signing & Capabilities/Background Modes
.
This is the location stream method, which differentiate between Android
and iOS
settings:
Stream<Position> startTracking() {
_positionStreamController = new StreamController<Position>.broadcast();
locationManager.checkPermission().then((value) async {
if (value == LocationPermission.denied ||
value == LocationPermission.deniedForever ||
value == LocationPermission.whileInUse) {
await locationManager.requestPermission().then((value) {
print(
'TrackingRepository.startTracking() requestPermission is: $value');
}).catchError((e) {
print(
'TrackingRepository.startTracking() requestPermission error: $e');
});
}
}).catchError((e) {
print('TrackingRepository.startTracking() error: $e');
});
late var locationSettings = Platform.isIOS
? AppleSettings(
accuracy: LocationAccuracy.bestForNavigation,
allowBackgroundLocationUpdates: true,
showBackgroundLocationIndicator: true,
activityType: ActivityType.otherNavigation)
: LocationSettings(accuracy: LocationAccuracy.best, distanceFilter: 0);
_positionSubscription = locationManager
.getPositionStream(locationSettings: locationSettings)
.listen((event) {
_positionStreamController.sink.add(event);
});
return _positionStreamController.stream;
}
Now, as I'm trying it on an iPhone 6 with iOS 12.5 I only see Accept
and Deny
options at system popup, but I was expecting also an Always allow
option, so I have to manually choose it otherwise background position updates are not received.
Isn't supposed to appear a second system popup to allow changing the permissions?
Am I missing out some settings?
Many thanks.
答案1
得分: 1
以下是您要翻译的代码部分:
Location
/// 使用 permission_handler 插件来处理两个平台的权限
Future<String> getLocationPermission() async {
print('\n\nLocationRepository.getLocationPermission() started\n\n');
late String permission;
permission = await Permission.locationWhenInUse.status.then((value) {
print('\n\n LocationRepository.getLocationPermission Permission.locationWhenInUse.status is ${value.name}');
// permission = await Permission.location.status.then((value) {
// print(
// '\n\n LocationRepository.getLocationPermission Permission.location.status is ${value.name}');
switch (value) {
case PermissionStatus.denied:
return 'denied';
case PermissionStatus.permanentlyDenied:
return 'deniedForever';
case PermissionStatus.limited:
return 'limited';
case PermissionStatus.granted:
return 'granted';
case PermissionStatus.restricted:
return 'restricted';
}
});
return permission;
}
Future<String> requestLocationPermission() async {
print('LocationRepository.requestLocationPermission started');
late String permission;
// 一般的位置权限不会弹出系统弹窗
// var status = await Permission.location.status;
// print('Permission.location.status is $status');
var status = await Permission.locationWhenInUse.status;
print('Permission.locationWhenInUse.status is $status');
/// 未授予权限
if (!status.isGranted) {
// var status = await Permission.location.request();
// print('Permission.location.request() status is $status');
var status = await Permission.locationWhenInUse.request();
print('Permission.locationWhenInUse.request() status is $status');
if (status.isGranted) {
permission = 'granted';
} else {
permission = 'denied';
}
}
/// 已授予权限
else {
permission = 'granted';
}
return permission;
}
Bg location
/// 使用 permission_handler 插件来处理两个平台的权限
Future<String> getLocationPermissionStatus() async {
print('\n\nTrackingRepository.getLocationPermissionStatus() started\n\n');
late String permission;
permission = await Permission.locationAlways.status.then((value) {
print('TrackingRepository.getLocationPermissionStatus() Permission.locationAlways.status is: ${value.name}\n\n');
switch (value) {
case PermissionStatus.denied:
return 'denied';
case PermissionStatus.permanentlyDenied:
return 'deniedForever';
case PermissionStatus.limited:
return 'limited';
case PermissionStatus.granted:
return 'granted';
case PermissionStatus.restricted:
return 'restricted';
}
});
return permission;
}
Future<String> requestLocationPermission() async {
late String permission;
var locationWhenInUseStatus =
await Permission.locationWhenInUse.status.then((value) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.status is: ${value.name}');
return value;
});
/// locationWhenInUseStatus 未授予权限
if (!locationWhenInUseStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationWhenInUseRequest 未授予权限,我们现在请求它');
/// 请求位置WhileInUse权限
var locationWhenInUseRequest =
await Permission.locationWhenInUse.request();
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.request() status is: $locationWhenInUseRequest');
/// locationWhenInUseRequest 已授予权限
if (locationWhenInUseRequest.isGranted) {
/// 当使用中已授予权限
print('\n\nTrackingRepository.requestLocationPermission() ios\n'
'When in use 已授予权限');
permission = 'whileInUse';
PermissionStatus status = await Permission.locationAlways.request();
print(
'\n\nTrackingRepository.requestLocationPermission() ios locationWhenInUse 已授予权限\n'
'Permission.locationAlways.request() status is: $status');
if (status.isGranted) {
/// 始终已授予权限
print('\n\nTrackingRepository.requestLocationPermission() ios\n'
'Always use 已授予权限');
permission = 'granted';
print(
'\n\nTrackingRepository.requestLocationPermission() ios locationAlways 已授予权限\n'
'Permission.locationAlways.request() status is: $status');
} else {
// 执行其他操作
}
}
/// locationWhenInUseRequest 未授予权限
else {
// 用户拒绝权限
permission = 'denied';
}
if (locationWhenInUseRequest.isPermanentlyDenied) {
// 当用户之前拒绝了权限并选择不再询问时
// 打开设置屏幕
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.request 已永久拒绝');
permission = 'deniedForever';
bool didOpen = await openAppSettings();
print(
'\n\nTrackingRepository.requestLocationPermission() ios 已永久拒绝\n'
'openAppSettings() didOpen: $didOpen');
// TODO: 重新检查locationWhenInUse权限状态?
}
}
/// locationWhenInUseStatus 已授予权限
else {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationWhenInUse 已授予权限,我们现在检查locationAlways权限');
permission = 'whenInUse';
var locationAlwaysStatus =
await Permission.locationAlways.status.then((value) {
print(
'\n\nTrackingRepository.requestLocationPermission() iOS\nlocationWhenInUse already granted\n'
'Permission.locationAlways.status is: ${value.name}');
return value;
});
/// locationAlways 未授予权限
if (!locationAlwaysStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationAlways 未授予权限,我们现在请求权限');
/// 请求locationAlways权限
var locationAlwaysStatus = await Permission.locationAlways.request();
/// 最终打开系统弹窗
print('\n\nTrackingRepository.requestLocationPermission() iOs\n'
'Permission.locationAlways.request() status is: $locationAlwaysStatus');
/// locationAlways 已授予权限
if (locationAlwaysStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationAlways 已授予权限');
permission = 'granted';
}
/// locationAlways 未授予权限
else {
print('\n\nTrackingRepository.request
<details>
<summary>英文:</summary>
After quite a few tries (all failed) to open up the second system popup for requesting the `always` permission, I started thinking that as commented by `Paulw11`, possibly the problem was the generic location permission request that `Geolocator` does, so I decided to try `permission_handler` plugin (just for `iOS` to start) which does allow to check the status and request specific location permission types. It did work..it now finally does open the second system popup so user can change the permission to always, on iOS (on both the Simulator and my soon to be retired iPhone6) as well as on Android platforms. That general location permission might actually be the real cause for `iOS` not showing a second system popup as even with `permission_handler` if I use `Permission.location.request()` no system popup gets shown while `Permission.locationWhenInUse.request()` does.
Here's the working code:
Location
/// Using permission_handler for both platforms
Future<String> getLocationPermission() async {
print('\n\nLocationRepository.getLocationPermission() started\n\n');
late String permission;
permission = await Permission.locationWhenInUse.status.then((value) {
print(
'\n\n LocationRepository.getLocationPermission Permission.locationWhenInUse.status is ${value.name}');
// permission = await Permission.location.status.then((value) {
// print(
// '\n\n LocationRepository.getLocationPermission Permission.location.status is ${value.name}');
switch (value) {
case PermissionStatus.denied:
return 'denied';
case PermissionStatus.permanentlyDenied:
return 'deniedForever';
case PermissionStatus.limited:
return 'limited';
case PermissionStatus.granted:
return 'granted';
case PermissionStatus.restricted:
return 'restricted';
}
});
return permission;
}
Future<String> requestLocationPermission() async {
print('LocationRepository.requestLocationPermission started');
late String permission;
// general location doesn't open the popup
// var status = await Permission.location.status;
// print('Permission.location.status is $status');
var status = await Permission.locationWhenInUse.status;
print('Permission.locationWhenInUse.status is $status');
/// NOT Granted
if (!status.isGranted) {
// var status = await Permission.location.request();
// print('Permission.location.request() status is $status');
var status = await Permission.locationWhenInUse.request();
print('Permission.locationWhenInUse.request() status is $status');
if (status.isGranted) {
permission = 'granted';
} else {
permission = 'denied';
}
}
/// Granted
else {
permission = 'granted';
}
return permission;
}
Bg location
/// Using permission_handler for both
Future<String> getLocationPermissionStatus() async {
print('\n\nTrackingRepository.getLocationPermissionStatus() started\n\n');
late String permission;
permission = await Permission.locationAlways.status.then((value) {
print(
'TrackingRepository.getLocationPermissionStatus() Permission.locationAlways.status is: ${value.name}\n\n');
switch (value) {
case PermissionStatus.denied:
return 'denied';
case PermissionStatus.permanentlyDenied:
return 'deniedForever';
case PermissionStatus.limited:
return 'limited';
case PermissionStatus.granted:
return 'granted';
case PermissionStatus.restricted:
return 'restricted';
}
});
return permission;
}
Future<String> requestLocationPermission() async {
late String permission;
var locationWhenInUseStatus =
await Permission.locationWhenInUse.status.then((value) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.status is: ${value.name}');
return value;
});
/// locationWhenInUseStatus NOT Granted
if (!locationWhenInUseStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationWhenInUseRequest NOT Granted, we now request it');
/// Ask locationWhileInUse permission
var locationWhenInUseRequest =
await Permission.locationWhenInUse.request();
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.request() status is: $locationWhenInUseRequest');
/// locationWhenInUseRequest granted
if (locationWhenInUseRequest.isGranted) {
/// When in use NOW Granted
print('\n\nTrackingRepository.requestLocationPermission() ios\n'
'When in use NOW Granted');
permission = 'whileInUse';
PermissionStatus status = await Permission.locationAlways.request();
print(
'\n\nTrackingRepository.requestLocationPermission() ios locationWhenInUse is Now Granted\n'
'Permission.locationAlways.request() status is: $status');
if (status.isGranted) {
/// Always is NOW Granted
print('\n\nTrackingRepository.requestLocationPermission() ios\n'
'Always use NOW Granted');
permission = 'granted';
print(
'\n\nTrackingRepository.requestLocationPermission() ios locationAlways is Now Granted\n'
'Permission.locationAlways.request() status is: $status');
} else {
//Do another stuff
}
}
/// locationWhenInUseRequest not granted
else {
//The user deny the permission
permission = 'denied';
}
if (locationWhenInUseRequest.isPermanentlyDenied) {
//When the user previously rejected the permission and select never ask again
//Open the screen of settings
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationWhenInUse.request is isPermanentlyDenied');
permission = 'deniedForever';
bool didOpen = await openAppSettings();
print(
'\n\nTrackingRepository.requestLocationPermission() ios isPermanentlyDenied\n'
'openAppSettings() didOpen: $didOpen');
// TODO: re-check for locationWhenInUse permission status?
}
}
/// locationWhenInUseStatus is ALREADY Granted
else {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationWhenInUse ALREADY Granted, we now check for locationAlways permission');
permission = 'whenInUse';
var locationAlwaysStatus =
await Permission.locationAlways.status.then((value) {
print(
'\n\nTrackingRepository.requestLocationPermission() iOS\nlocationWhenInUse already granted\n'
'Permission.locationAlways.status is: ${value.name}');
return value;
});
/// locationAlways is NOT Already Granted
if (!locationAlwaysStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationAlways not granted, we now ask for permission');
/// ask locationAlways permission
var locationAlwaysStatus = await Permission.locationAlways.request();
/// finally it opens the system popup
print('\n\nTrackingRepository.requestLocationPermission() iOs\n'
'Permission.locationAlways.request() status is: $locationAlwaysStatus');
/// locationAlways is NOW Granted
if (locationAlwaysStatus.isGranted) {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'locationAlways was Granted upon request');
permission = 'granted';
}
/// locationAlways was NOT Granted
else {
print('\n\nTrackingRepository.requestLocationPermission() iOS\n'
'Permission.locationAlways.request() status was NOT Granted upon request, we now open AppSettings');
await openAppSettings().then((value) {
print(
'\n\nTrackingRepository.requestLocationPermission() ios locationAlways isPermanentlyDenied\n'
'openAppSettings() didOpen: $value');
});
// TODO: re-check locationAlways permission status??
}
}
/// locationAlways is ALREADY Granted
else {
permission = 'granted';
}
}
return permission;
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论