Skip to main content

Changing RawMenuAnchor close order

Closing a `RawMenuAnchor` now triggers `onClose` and `onCloseRequested` callbacks for all descendant `RawMenuAnchor`s in a coordinated sequence.

Summary

#

Closing a RawMenuAnchor now triggers the onCloseRequested and onClose callbacks for all descendant RawMenuAnchors. The onCloseRequested callback is triggered top-down, starting from the triggering RawMenuAnchor and moving to its descendants, while the onClose callback is triggered bottom-up. If a RawMenuAnchor is already closed, calls to MenuController.close and MenuController.closeChildren will not trigger the onCloseRequested callback.

Background

#

RawMenuAnchor is a low-level widget used to build custom menu systems. Previously, a RawMenuAnchor did not automatically notify its descendants when it was closed. Developers were required to manually call controller.closeChildren() within the onCloseRequested callback to trigger closure in descendant RawMenuAnchors.

Furthermore, the onClose callback timing was inconsistent. A parent RawMenuAnchor's onClose could be executed before its descendants had finished closing.

The updated behavior ensures that when a parent RawMenuAnchor begins to close, it subsequently triggers onCloseRequested for all of its descendant RawMenuAnchors in a top-down manner.

When hideOverlay is called from within onCloseRequested to close the menu, all descendant RawMenuAnchors will have their onClose callbacks executed in a bottom-up order. This means that the most recently opened RawMenuAnchor will have its onClose callback executed first, followed by its parent, and so on up the hierarchy.

This design allows for a coordinated closing sequence where child RawMenuAnchors can perform necessary cleanup before their parents finalize the closing process.

Finally, if a RawMenuAnchor is already closed, calls to MenuController.close and MenuController.closeChildren will not trigger the onCloseRequested callback, preventing unnecessary callback executions.

Migration guide

#

If your code does not override the default implementation of RawMenuAnchor.onCloseRequested or your RawMenuAnchor does not contain submenus, no changes are required.

If you have a custom implementation of onCloseRequested in a RawMenuAnchor containing submenus, controller.closeChildren() is now called automatically when the parent menu closes. You should ensure that your implementation of onCloseRequested still behaves correctly in this context. Immediate calls to controller.closeChildren() within your onCloseRequested callback are no longer necessary and should be removed.

Additionally, if your logic relied on the parent's onClose callback firing before its descendants, you may need to refactor your code to account for the new bottom-up execution order.

Code before migration:

dart
RawMenuAnchor(
  controller: menuController,
  onCloseRequested: (hideOverlay) {
    if (!animationController.isForwardOrCompleted) {
      return;
    }

    // Descendant submenus must be closed before the parent menu. This is now
    // handled automatically, so this call is no longer necessary.
    menuController.closeChildren();
    animationController.reverse().whenComplete(hideOverlay);
  },
  onClose: () {
    // This might have executed before descendants called onClose().
    _handleMenuClosed();
  },
  // ...
)

Code after migration:

dart
RawMenuAnchor(
  controller: menuController,
  onCloseRequested: (hideOverlay) {
    if (!animationController.isForwardOrCompleted) {
      return;
    }

    // menuController.closeChildren() is now called automatically.
    animationController.reverse().whenComplete(hideOverlay);
  },
  onClose: () {
    // This now executes only after all descendant submenus have called onClose().
    _handleMenuClosed();
  },
  // ...
)

References

#

API documentation:

Relevant issues:

Relevant PRs: