Flutter locationWhenInUse权限首次请求挂起二次运行正常问题
Flutter位置权限首次请求挂起问题排查与解决方案
基础环境信息
- Flutter版本:2.10.5
- pubspec.yaml 引入依赖版本:
google_maps_flutter: ^2.0.6 geolocator: ^8.2.1 geocoding: ^2.0.0 permission_handler: ^9.2.0
- Android端AndroidManifest.xml已配置权限:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" />
异常现象
- 首次安装启动应用,代码执行到
await Permission.locationWhenInUse.request()时直接挂起,后续无任何日志输出 - 不卸载应用二次启动时,可正常识别已授权状态,输出
have while in use access日志,证明首次授权操作实际已生效,但权限请求的回调未触发 - 首次启动控制台仅输出如下提示:
Accessing hidden method Landroid/content/ContextWrapper;->getDisplay()Landroid/view/Display; (light greylist, linking)
根因排查思路
- 依赖混用冲突:代码中同时使用
permission_handler和未在pubspec中声明的Location类(来自location插件),两个权限处理插件同时注册位置权限监听、抢占系统权限请求回调通道,是首次请求无返回的核心原因。 - 异步调用写法不规范:混用
await+.then()的链式调用写法,在系统权限弹窗弹出时,Flutter异步事件循环的回调链路被打断,导致权限结果返回时无法命中提前注册的.then()回调。 - permission_handler 9.x版本兼容问题:该版本在Android 12+设备上,未配置后台位置权限的场景下请求前台位置权限,存在已知的回调丢失bug。
- 控制台输出的hidden method提示为Android系统非限制级灰名单API访问警告,和权限请求挂起无直接关联,可直接忽略。
修复方案
第一步:清理冗余依赖
删除代码中未声明的location插件相关调用,位置服务状态检查、权限请求逻辑统一使用permission_handler实现,避免多插件抢占回调通道。
第二步:重构权限请求代码
统一使用async/await语法编写异步逻辑,不要混用await和.then(),参考实现如下:
Future<void> _checkMapAuthentication() async { // 检查位置服务开关状态 final ServiceStatus serviceStatus = await Permission.location.serviceStatus; print("Location service status -> $serviceStatus"); // 位置服务未开启时引导用户开启 if (serviceStatus == ServiceStatus.disabled) { final bool serviceOpened = await Permission.location.requestService(); if (!serviceOpened) { print("Location service not enabled by user"); return; } } // 检查使用时位置权限状态 final PermissionStatus permissionStatus = await Permission.locationWhenInUse.status; print("Location when in use permission status -> $permissionStatus"); if (permissionStatus == PermissionStatus.granted) { print("Have while in use location access"); return; } // 请求使用时位置权限 final PermissionStatus requestResult = await Permission.locationWhenInUse.request(); if (requestResult.isDenied) { print("While in use location access denied"); // 权限永久被拒时跳转系统设置页 if (await Permission.locationWhenInUse.isPermanentlyDenied) { await openAppSettings(); } return; } print("Got while in use location access"); }
第三步:补全Android端兼容配置
在app模块的build.gradle中将compileSdkVersion配置为31及以上,匹配permission_handler 9.x版本的编译要求:
android { compileSdkVersion 33 // 最低不可低于31 // 其余原有配置保持不变 }
如果业务不需要后台定位能力,在AndroidManifest.xml的<application>标签内添加如下配置,移除permission_handler构建时默认合并的后台位置权限,避免Android 12+版本的兼容问题:
<application ...> <!-- 移除不需要的后台位置权限,避免前台权限请求受影响 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove" /> <!-- 其余原有组件配置保持不变 --> </application>
注意添加上述配置后,需要在Manifest根标签中声明tools命名空间:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="你的应用包名">
第四步:清理构建缓存验证
执行如下命令清理构建缓存后重新运行即可:
flutter clean flutter pub get flutter run
修复后首次安装启动时,位置权限弹窗弹出后无论用户选择允许或拒绝,都可以正常收到回调,不会再出现挂起无响应的问题。
内容的提问来源于stack exchange,提问作者Janaka
相关产品推荐
相关产品推荐

