Flutter请求Location权限设为Always却被永久拒绝的问题求助
iOS 后台定位权限状态无法识别问题排查与修复
开发类Strava的轨迹记录应用,使用background_locator_2处理后台定位,iOS真机测试时,已在系统设置中授予Always权限,但应用仍判定locationWhenInUse为永久拒绝,无法启动轨迹追踪。相关配置与问题细节如下:
现有配置
Info.plist 权限配置
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>We need your location to provide the best experience, including background tracking.</string> <key>NSLocationAlwaysUsageDescription</key> <string>We need your location to track your pathway even when the app is in the background.</string> <key>NSLocationWhenInUseUsageDescription</key> <string>We need your location to track your pathway.</string> <key>UIApplicationSupportsIndirectInputEvents</key> <true/> <key>UIBackgroundModes</key> <array> <string>location</string> </array>
权限请求代码
Future<bool> _requestPermissions() async { PermissionStatus whenInUseStatus = await Permission.locationWhenInUse.status; if (whenInUseStatus.isDenied) { // If denide, request it print("Denied - requesting locationWhenInUse permission"); whenInUseStatus = await Permission.locationWhenInUse.request(); } if (whenInUseStatus.isGranted) { print("LocationWhenInUse granted - requesting locationAlways permission"); PermissionStatus alwaysStatus = await Permission.locationAlways.status; if (alwaysStatus.isDenied) { print("LocationAlways is denied - requesting permission"); alwaysStatus = await Permission.locationAlways.request(); } if (alwaysStatus.isGranted) { print("LocationAlways granted"); return true; } if (alwaysStatus.isPermanentlyDenied) { print("LocationAlways is permanently denied. Opening app settings."); await openAppSettings(); return false; } } if (whenInUseStatus.isPermanentlyDenied) { print("LocationWhenInUse is permanently denied. Opening app settings."); await openAppSettings(); return false; } print("Permissions not granted - returning false"); return false; }
运行日志
flutter: Denied - requesting locationWhenInUse permission flutter: LocationWhenInUse is permanently denied. Opening app settings.
问题原因分析
- iOS权限状态关联逻辑:iOS 13+中,用户授予
Always权限时,locationWhenInUse权限会自动被视为已授予,但旧版permission_handler可能存在状态读取偏差,导致误判为denied或permanentlyDenied。 - 权限检测顺序问题:现有代码优先检测
locationWhenInUse,若用户已在系统设置中直接设置为Always,应用首次读取状态时可能出现缓存或识别错误。 - 权限更新后未重新检测:调用
openAppSettings()后直接返回false,未重新读取权限状态,导致用户从设置返回后应用仍无法识别已授予的权限。
修复方案
1. 升级permission_handler包
确保使用permission_handler 10.0.0及以上版本,新版本修复了iOS权限状态读取的bug,能更准确识别Always权限对应的locationWhenInUse状态。
2. 调整权限检测与请求顺序
先检测locationAlways权限状态,若已授予直接返回成功;否则再按WhenInUse→Always的顺序请求,适配iOS权限逻辑:
Future<bool> _requestPermissions() async { // 优先检测Always权限,避免WhenInUse状态误判 PermissionStatus alwaysStatus = await Permission.locationAlways.status; if (alwaysStatus.isGranted) { print("LocationAlways already granted"); return true; } // 检测WhenInUse权限 PermissionStatus whenInUseStatus = await Permission.locationWhenInUse.status; if (whenInUseStatus.isDenied) { print("Denied - requesting locationWhenInUse permission"); whenInUseStatus = await Permission.locationWhenInUse.request(); } if (whenInUseStatus.isGranted) { print("LocationWhenInUse granted - requesting locationAlways permission"); if (alwaysStatus.isDenied) { print("LocationAlways is denied - requesting permission"); alwaysStatus = await Permission.locationAlways.request(); } if (alwaysStatus.isGranted) { print("LocationAlways granted"); return true; } } // 处理永久拒绝情况,打开设置后重新检测权限 if (alwaysStatus.isPermanentlyDenied || whenInUseStatus.isPermanentlyDenied) { print("Location permission permanently denied. Opening app settings."); await openAppSettings(); // 从设置返回后重新读取权限状态 final updatedAlwaysStatus = await Permission.locationAlways.status; final updatedWhenInUseStatus = await Permission.locationWhenInUse.status; return updatedAlwaysStatus.isGranted || updatedWhenInUseStatus.isGranted; } print("Permissions not granted - returning false"); return false; }
3. 清除应用权限缓存(临时解决)
- 删除应用后重新安装,清除iOS系统中残留的权限状态缓存。
- 重启设备,确保系统权限状态同步到应用。
4. 验证background_locator_2配置
确保AppDelegate.swift中添加了background_locator_2的初始化代码:
import UIKit import Flutter import background_locator_2 @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) // 初始化background_locator_2 BackgroundLocatorPlugin.setPluginRegistrantCallback { registry in GeneratedPluginRegistrant.register(with: registry) } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
验证步骤
- 应用启动后,若已在设置中授予
Always权限,应直接输出LocationAlways already granted并返回true。 - 若权限未授予,按流程请求
WhenInUse后再请求Always,确认权限弹窗正常显示。 - 进入设置修改权限后返回应用,应能正确识别权限状态并返回
true。
内容的提问来源于stack exchange,提问作者StripyTiger
相关产品推荐
相关产品推荐

