Flutter导航时持久化Drawer关闭触发断言异常的解决办法
问题:持久化Drawer关闭时触发Overlay断言异常
我需要实现Drawer的持久化,即切换页面导航时Drawer保持打开状态。为此实现了一个接收GlobalKey参数的自定义Scaffold,代码如下:
MyScaffold( // ... late GlobalKey<ScaffoldState> _scaffoldKey; @override void initState() { super.initState(); _scaffoldKey = widget.scaffoldKey; } @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, drawer: MouseRegion( onExit: (_) { _scaffoldKey.currentState?.closeDrawer(); }, child: Drawer(/* ... */), ), body: /* ... */, ); } )
功能逻辑正常,但关闭Drawer时会触发如下断言异常:
2024-03-11 14:05:17.401891: 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 190 pos 12: '!_disposedByOwner': is not true. 2024-03-11 14:05:17.402279: #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61) #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) #2 OverlayEntry.markNeedsBuild (package:flutter/src/widgets/overlay.dart:190:12) #3 ModalRoute.changedInternalState (package:flutter/src/widgets/routes.dart:1711:19) #4 LocalHistoryRoute.removeLocalHistoryEntry (package:flutter/src/widgets/routes.dart:715:9) #5 LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:519:13) #6 DrawerControllerState._animationStatusChanged (package:flutter/src/material/drawer.dart:527:24) #7 AnimationLocalStatusListenersMixin.notifyStatusListeners (package:flutter/src/animation/listener_helpers.dart:240:19) #8 AnimationController._checkStatusChanged (package:flutter/src/animation/animation_controller.dart:815:7) #9 AnimationController._startSimulation (package:flutter/src/animation/animation_controller.dart:749:5) #10 AnimationController.fling (package:flutter/src/animation/animation_controller.dart:712:12) #11 DrawerControllerState.close (package:flutter/src/material/drawer.dart:630:17) #12 ScaffoldState.closeDrawer (package:flutter/src/material/scaffold.dart:2271:31) #13 TigerScaffoldState.build.<anonymous closure>.<anonymous closure> (package:tigerui/widgets/tiger_scaffold.dart:157:50) #14 MouseTracker._handleDeviceUpdateMouseEvents.<anonymous closure> (package:flutter/src/rendering/mouse_tracker.dart:417:29) #15 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13) #16 MouseTracker._handleDeviceUpdateMouseEvents (package:flutter/src/rendering/mouse_tracker.dart:414:21) #17 MouseTracker._handleDeviceUpdate (package:flutter/src/rendering/mouse_tracker.dart:282:5) #18 MouseTracker.updateWithEvent.<anonymous closure>.<anonymous closure> (package:flutter/src/rendering/mouse_tracker.dart:351:9) #19 MouseTracker._deviceUpdatePhase (package:flutter/src/rendering/mouse_tracker.dart:210:9) #20 MouseTracker.updateWithEvent.<anonymous closure> (package:flutter/src/rendering/mouse_tracker.dart:328:7) #21 MouseTracker._monitorMouseConnection (package:flutter/src/rendering/mouse_tracker.dart:193:9) #22 MouseTracker.updateWithEvent (package:flutter/src/rendering/mouse_tracker.dart:327:5) #23 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:432:20) #24 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:413:7) #25 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:376:5) #26 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:323:7) #27 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:292:9) #28 _invoke1 (dart:ui/hooks.dart:328:13) #29 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:410:7) #30 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31)
我已经尝试过以下方法但未解决:
- 添加
WidgetsBinding.instance.postFrameCall - 使用
mounted属性 - 添加延迟
Future
解决方案
这个异常的核心原因是:页面导航后,原Scaffold或Drawer对应的OverlayEntry已被销毁,但鼠标移出的回调仍在尝试调用closeDrawer,导致操作已被销毁的Overlay对象。可以通过以下方式解决:
1. 直接控制DrawerController并验证状态
给Drawer添加单独的GlobalKey,直接通过DrawerController控制关闭,并在操作前验证状态是否活跃:
final GlobalKey<DrawerControllerState> _drawerControllerKey = GlobalKey(); // 自定义Scaffold的build方法中: Drawer( key: _drawerControllerKey, child: /* 你的Drawer内容 */, ) // 鼠标移出回调修改为: onExit: (_) { final drawerState = _drawerControllerKey.currentState; if (drawerState != null && drawerState.mounted) { drawerState.close(); } },
2. 双重验证组件状态
在调用closeDrawer前,同时验证自定义Scaffold的mounted状态和ScaffoldState的有效性:
onExit: (_) { if (mounted && _scaffoldKey.currentState != null && _scaffoldKey.currentState!.mounted) { _scaffoldKey.currentState?.closeDrawer(); } },
3. 将Drawer抽离到全局Overlay(彻底解决持久化问题)
如果要实现真正的Drawer持久化,建议将Drawer从Scaffold中抽离,手动添加到全局Overlay中,这样页面导航时Drawer不会随页面销毁:
// 在全局状态类或根组件中管理Drawer的OverlayEntry OverlayEntry? _drawerOverlayEntry; void showPersistentDrawer(BuildContext context) { if (_drawerOverlayEntry == null) { _drawerOverlayEntry = OverlayEntry( builder: (context) => MouseRegion( onExit: (_) => hidePersistentDrawer(), child: Drawer(/* 你的Drawer内容 */), ), ); Overlay.of(context)?.insert(_drawerOverlayEntry!); } } void hidePersistentDrawer() { _drawerOverlayEntry?.remove(); _drawerOverlayEntry = null; }
这种方式完全脱离Scaffold的管理,从根本上避免了页面导航导致的组件销毁问题,更符合"持久化Drawer"的需求。
内容的提问来源于stack exchange,提问作者lufocus
相关产品推荐
相关产品推荐

