Flutter禁用返回按钮时触发'!_debugLocked'错误的解决方法
问题场景
需求:当_timerStarted或_isBrushing为true时,禁用页面的返回导航。
当前实现代码:
return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { // Allow pop only if the timer has finished or the user taps finishBrushing if (_timerStarted || _isBrushing) { // Show snackBar with 'Finish brushing to exit the screen' return; // Prevent exiting the screen by not invoking the pop } final navigator = Navigator.of(context); navigator.pop(result); }, child: Scaffold( ... ) );
功能拦截正常,但允许返回退出时触发以下错误:
E/flutter ( 5204): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)]
Unhandled Exception: 'package:flutter/src/widgets/navigator.dart':
Failed assertion: line 5350 pos 12: '!_debugLocked': is not true.
E/flutter ( 5204): #0 _AssertionError._doThrowNew
(dart:core-patch/errors_patch.dart:50:61) E/flutter ( 5204): #1
_AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) E/flutter ( 5204): #2 NavigatorState.pop
(package:flutter/src/widgets/navigator.dart:5350:12) E/flutter (
5204): #3 TimerScreenState.build.
(package:toothbrush/timer.dart:594:21) E/flutter ( 5204): #4
PopScope._callPopInvoked
(package:flutter/src/widgets/pop_scope.dart:137:30) E/flutter ( 5204):
#5 _PopScopeState.onPopInvokedWithResult (package:flutter/src/widgets/pop_scope.dart:172:12) E/flutter ( 5204):
#6 ModalRoute.onPopInvokedWithResult (package:flutter/src/widgets/routes.dart:1780:16) E/flutter ( 5204):
#7 _RouteEntry.handlePop (package:flutter/src/widgets/navigator.dart:3171:11) E/flutter (
5204): #8 NavigatorState._flushHistoryUpdates
(package:flutter/src/widgets/navigator.dart:4340:22) E/flutter (
5204): #9 NavigatorState.pop
(package:flutter/src/widgets/navigator.dart:5368:7) E/flutter ( 5204):
#10 TimerScreenState.build. (package:toothbrush/timer.dart:594:21) E/flutter ( 5204): #11
PopScope._callPopInvoked
(package:flutter/src/widgets/pop_scope.dart:137:30) E/flutter ( 5204):
#12 _PopScopeState.onPopInvokedWithResult (package:flutter/src/widgets/pop_scope.dart:172:12) E/flutter ( 5204):
#13 ModalRoute.onPopInvokedWithResult (package:flutter/src/widgets/routes.dart:1780:16) E/flutter ( 5204):
#14 NavigatorState.maybePop (package:flutter/src/widgets/navigator.dart:5319:25) E/flutter (
5204): E/flutter ( 5204): #15
WidgetsBinding.handlePopRoute
(package:flutter/src/widgets/binding.dart:848:11) E/flutter ( 5204):
E/flutter ( 5204): #16
MethodChannel._handleAsMethodCall
(package:flutter/src/services/platform_channel.dart:571:42) E/flutter
( 5204): E/flutter ( 5204): #17
_DefaultBinaryMessenger.setMessageHandler. (package:flutter/src/services/binding.dart:618:22) E/flutter ( 5204):
E/flutter ( 5204):
错误原因
核心问题在于错误组合使用PopScope的配置:
- 设置
canPop: false后,导航框架已进入pop拦截流程,此时导航器处于锁定状态(_debugLocked为true)。 - 在
onPopInvokedWithResult中手动调用navigator.pop(result),相当于在导航器的锁定流程中再次触发pop操作,触发框架的断言检查,导致报错。
修复方案
正确的做法是通过canPop属性直接控制是否允许返回,onPopInvokedWithResult仅处理拦截时的提示逻辑,无需手动调用pop:
- 将
canPop设置为!(_timerStarted || _isBrushing),当不需要拦截时,框架会自动处理返回操作。 - 在
onPopInvokedWithResult中,仅当需要拦截时执行提示(比如显示SnackBar)。
修复后代码示例
return PopScope( // 仅当不需要拦截时允许返回 canPop: !(_timerStarted || _isBrushing), onPopInvokedWithResult: (didPop, result) { // didPop为false时,表示pop被拦截,此时显示提示 if (!didPop && (_timerStarted || _isBrushing)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Finish brushing to exit the screen')), ); } }, child: Scaffold( // 页面内容... ), );
补充说明
didPop参数用于标识pop操作是否已被框架执行:canPop为true时,didPop为true,无需额外处理;canPop为false时,didPop为false,可执行拦截提示逻辑。- 这种写法符合
PopScope的设计意图,避免了手动调用navigator.pop带来的状态冲突问题。
内容的提问来源于stack exchange,提问作者Tudor Mihail

