lufocus
lufocus

Reputation: 1

Persist the drawer between navigation

I'm trying to have the drawer persisted, meaning it stays open when navigating through the screens. For this I have a custom scaffold that takes GlobalKey parameter. The implementation of this is like so:

MyScaffold(
...

late GlobalKey<ScaffoldState> _scaffoldKey;

 @override
  void initState() {
    super.initState();
    _scaffoldKey = widget.scaffoldKey;
  }

Scaffold(
      key: _scaffoldKey,
      drawer: MouseRegion(
                  onExit: (_) {

                  _scaffoldKey.currentState?.closeDrawer();
                    },
                    child: Drawer(...),
                    ),
                  ),
            body: ...,
            )
)
...
)

As you can see, basically what I want to accomplish is that when the drawer is hovered it remains open regardless of the navigating screens, and when the mouse leaves it closes. The functionality works as intended, however, when the drawer closes I get this exception:

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)

Is there anyway I could prevent this exception?

I have tried:

  1. Adding WidgetsBinding.instance.postFrameCall
  2. Using mounted attribute
  3. Adding a delayed future

Upvotes: 0

Views: 232

Answers (1)

Texv
Texv

Reputation: 1904

I believe you get this error because your Drawer widget is built using the first page's builder. When navigating to a new page, it disposes all previous widget trees and re-build new widgets, hence the error because it failed to dispose the Drawer widget properly.

I suggest wrapping your Drawer with an Overlay widget and placing it in the global (app level) area outside your class to make it independent of route changes, then create a function to show/hide the drawer.

So step 1: Instead of using the drawer parameter, use the leading parameter so you can have a button to call the Overlay Drawer:

 Scaffold(
    appBar: AppBar(
      title: Text('Test'),
      leading: IconButton(
        onPressed: () {
          OverlayDrawer.show(context);
        },
        icon: Icon(Icons.menu),
      ),
    ),
    body: body,
  );

Step 2: Create the OverlayDrawer class. This here is just an example (see full working demo at https://dartpad.dev/?id=612b6b178e2396081c3b6b55beb7a31f)

class OverlayDrawer {
  static void show(BuildContext context) {
    isOpen = true;
    _overlayEntry = OverlayEntry(
      builder: (context) {
        List pagesList = const [ScreenA(), ScreenB(), ScreenC(), ScreenD()];
        return SafeArea(
          child: Drawer(..);
    );
    Overlay.of(context).insert(_overlayEntry!);
  }
}

You can find a similar topic for persisting a Dialog between navigation here: is there any way in flutter to persist dialog when moving from one screen to another?

Upvotes: 0

Related Questions