You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

问题原因分析

  1. iOS权限状态关联逻辑:iOS 13+中,用户授予Always权限时,locationWhenInUse权限会自动被视为已授予,但旧版permission_handler可能存在状态读取偏差,导致误判为denied或permanentlyDenied。
  2. 权限检测顺序问题:现有代码优先检测locationWhenInUse,若用户已在系统设置中直接设置为Always,应用首次读取状态时可能出现缓存或识别错误。
  3. 权限更新后未重新检测:调用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)
  }
}

验证步骤

  1. 应用启动后,若已在设置中授予Always权限,应直接输出LocationAlways already granted并返回true。
  2. 若权限未授予,按流程请求WhenInUse后再请求Always,确认权限弹窗正常显示。
  3. 进入设置修改权限后返回应用,应能正确识别权限状态并返回true。

内容的提问来源于stack exchange,提问作者StripyTiger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:23:15