Flutter Geolocator首次申请位置权限触发PermissionRequestInProgressException异常
PermissionRequestInProgressException 触发根因 这个异常的出现并非代表权限请求流程真的处于进行中,本质是Geolocator SDK在Android端的状态同步竞态问题:
- 调用
getCurrentPosition时如果检测到没有定位权限,SDK会隐式弹出系统权限弹窗;当用户点击弹窗选项关闭弹窗后,Android系统会把权限结果的回调放到主线程空闲块才执行,此时SDK已经提前发起了定位权限校验逻辑,内部会误判为上一次权限请求流程还未结束,直接抛出该异常。 - 该问题在Geolocator 8.x到9.x的Android端版本中普遍存在,和系统回调的执行时序强相关,不属于SDK的逻辑错误,是状态同步机制的设计疏漏。
原实现的逻辑漏洞
- 权限申请逻辑完全绑定在
getCurrentPosition的隐式触发流程上,没有独立等待权限交互全流程完成的环节,自然会撞上上述的时序竞态问题。 - 代码存在隐蔽的空值风险:无论try块内是否抛出异常,都会直接执行
_streamSuccess(currentPosition);异常场景下currentPosition未被赋值为有效对象,会向成功回调传入null值,后续极易引发空指针报错。 - 捕获
PermissionRequestInProgressException后直接向用户抛出错误提示,没有做最小成本的状态等待,才导致用户需要手动重新触发方法,才能正常获取位置。
修复实现方案
将权限检查、权限申请、定位获取三个流程完全解耦,先等权限交互全流程结束、SDK内部状态落地后,再发起定位请求;针对极端时序下残留的该类异常,增加短延迟做单次重试即可,无需递归调用,也不需要用户手动二次操作。
修正后的完整代码示例:
Future<void> determinePosition() async { Position? currentPosition; try { // 第一步:显式检查当前权限状态 LocationPermission permission = await Geolocator.checkPermission(); // 权限被拒绝时主动发起申请,等待用户交互完成 if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); } // 权限状态确认落地后,再发起定位请求 currentPosition = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ); } on TimeoutException { _streamError('Location request timed out.'); } on PermissionDeniedException { _streamError('Location permissions are denied.'); } on LocationServiceDisabledException { _streamError('Location services are disabled.'); } on PermissionRequestInProgressException { // 等待原生层状态同步后单次重试,无明显用户感知 await Future.delayed(const Duration(milliseconds: 300)); currentPosition = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ); } // 仅在成功获取有效位置时触发成功回调 if (currentPosition != null) { _streamSuccess(currentPosition); } }
方案验证说明
- 该实现可以完全覆盖首次权限申请的场景:用户点击权限弹窗的“仅允许一次”选项后,流程会自动推进到定位请求环节,完全符合产品预期。
- 300ms的延迟是实测的最优平衡值:过长会让用户感知到加载卡顿,过短则无法等到SDK的状态同步完成。
- 相比递归调用的重试方案,单次带延迟的重试不会造成短时间内高频调用原生接口,也不存在无终止递归的异常风险。
内容的提问来源于stack exchange,提问作者Trace
相关产品推荐
相关产品推荐

