Skip to content

Conversation

@davidhicks980
Copy link
Contributor

Currently, we close RawMenuAnchor and derivatives during the persistent callback phase (didChangeDependencies).

image

As a result, there are phase checks in several parts of RawMenuAnchor:

image

I was under the impression that these phase checks were used for more than just closing on MediaQuery changes, but, as far as I can tell, that is their sole purpose:
#130534 (comment)
#136769

So, this PR closes the menu during didChangeMetrics, since didChangeMetrics is called during the idle phase.

The downside to didChangeMetrics is that it will not be called when MediaQuery.sizeOf(context) updates without the screen changing size. This may occur if a user inserts a different size into an ancestor MediaQuery. If this is untenable, we can close this PR.

List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.

Fixes #168539

If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.

The only change that was made is a test simplification. The new menu passes all old tests.

Old:
image

New:
image

As discussed, even if all original tests pass, this is a breaking change.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label May 10, 2025
@davidhicks980 davidhicks980 changed the title Switch from MediaQuery.sizeOf to didChangeMetrics for RawMenuAnchor [widgets/raw_menu_anchor.dart] Switch from MediaQuery.sizeOf to didChangeMetrics for RawMenuAnchor May 10, 2025
@justinmc justinmc requested a review from LongCatIsLooong May 16, 2025 22:22
@justinmc
Copy link
Contributor

@LongCatIsLooong I tagged you for review since this relates with OverlayPortal.

_overlayController.hide();
}, debugLabel: 'MenuAnchor.hide');
}
_overlayController.hide();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is close public? If so then it should be added to the API docs that it can't be called during build.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MenuController.close() redirects to close(), so I'll add documentation to MenuController.close()

Considering this is a breaking change, I'm curious whether you think we should close this PR. While all tests passed, this could lead to annoying bugs for end users.

That said, open() currently throws an error during builds since OverlayController.show() is called. I realize now that I overlooked the SchedulerPhase check in the open() method when copying over from MenuAnchor. Considering OverlayController.show() will throw, checking the SchedulerPhase in open() doesn't appear to have any effect.

If you do think we should close this PR, do you think we should add similar phase checking/post-frame callback logic to the open() method?

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the CI checks are passing so by definition this doesn't seem to be a breaking change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right:

A breaking change is one that breaks the tests in the flutter/tests repo, and those need a migration guide.

I'm misusing the term to mean a change that may reasonably break another developer's code. I believe calling close() during didChangeDependencies should have worked before, but implementing this change would break this behavior.


@override
void didChangeMetrics() {
super.didChangeMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hypothetically you can reparent a MenuAnchor from one window to another right? I don't think you'll get notified (or not in the .idle phase) if that happens? Or the menu is not supposed to hide if that happens?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is my bigger concern (even if multi-window isn't fully implemented). Should the menu close if I move the menu anchor from one window to another using a global key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to write a test to simulate this behavior, but I couldn't figure out if there was a simple way to do so using standard flutter testing libraries. Let me know if you know of a good example widget I can model my tests off of...

In my opinion, it seems more intuitive to leave the menu open if the window changes. This is based on the fact that the RawMenuAnchor's state lives within RawMenuAnchor (rather than on an external variable or on MenuController). AFAIK, the only reason why RawMenuAnchor closes on scroll and on resize is because the overlay isn't rebuilt when the menu anchor is repositioned, so the menu appears fixed on the screen when its anchor changes position. Assuming reparenting using a GlobalKey would lead to the overlay rebuilding with a new anchor position, I think the overlay should reposition upon entering a new window? I could be completely wrong.

Also, if #168785 is addressed, there would no longer be a need to automatically close the menu, since the menu would be expected to always follow its anchor.

To your point, as far as I know, the only way to observe the current view would be to call View.of(context), which would not be notified in the idle phase. If we did want to close the menu on reparenting, I believe I'd have to introduce a different way of listening to view changes.

void didChangeMetrics() {
super.didChangeMetrics();
// Close the root menu if the screen size changes
if (isRoot) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this looks a bit different from the original implementation, now the menu will also dismiss on view inset change, if I recall correctly. Is that intended that things like keyboard popping up also dismisses all menus? What if there's a text field in one of the menu items?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, and this is not the intended behavior. The didChangeMetrics docs made it sound as if didChangeMetrics was analogous to MediaQuery.sizeOf(context), but this is not the case. I'll update this code to diff the logical view size.

@davidhicks980
Copy link
Contributor Author

@LongCatIsLooong Sorry for the delay. We just bought a house, so I haven't had much time to contribute.

@Piinks
Copy link
Contributor

Piinks commented Jun 18, 2025

We just bought a house, so I haven't had much time to contribute.

Congrats on your new home @davidhicks980! 🎊
No worries at all, we'll be here when you are ready to come back.

@Piinks
Copy link
Contributor

Piinks commented Jul 29, 2025

Hey @davidhicks980 no pressure, just circling back on this one in triage. Would you like to keep this open to return to?

@davidhicks980
Copy link
Contributor Author

Sorry, @Piinks! I'm moved in but I've been prioritizing older issues, particularly the CupertinoMenuAnchor implementation. If possible, let's keep this one open for now.

@justinmc
Copy link
Contributor

@davidhicks980 Checking in again since it's been a few months. Are you still planning to come back to this?

@davidhicks980
Copy link
Contributor Author

@justinmc I'll go ahead and close it. At some point, we may have a declarative OverlayPortal, which would fix this issue. Also, this PR precludes the use of using MediaQueries to close the menu in testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RawMenuAnchor] Consider using didChangeMetrics instead of MediaQuery.sizeOf

4 participants