Skip to content

Fix DropdownButton crash on orientation change while menu is open - #187366

Closed
ishivamg wants to merge 2 commits into
flutter:masterfrom
ishivamg:fix/171011-dropdown-orientation-crash
Closed

Fix DropdownButton crash on orientation change while menu is open#187366
ishivamg wants to merge 2 commits into
flutter:masterfrom
ishivamg:fix/171011-dropdown-orientation-crash

Conversation

@ishivamg

@ishivamg ishivamg commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Description

When a DropdownButton's menu is open and the device orientation changes, the framework throws:

setState() or markNeedsBuild() called during build.

_DropdownButtonState.build() detects the orientation change and dismisses the open menu by calling _removeDropdownRoute()_DropdownRoute._dismiss()Navigator.removeRoute(). Mutating the Navigator synchronously during build marks route-transition listeners (ListenableBuilder) dirty mid-build, which trips the assertion. It is especially reproducible when the dropdown is hosted inside a dialog (the dialog route's transition builder is merged with the dropdown's secondary animation), as in the linked issue.

Fix

Defer the dismissal to a post-frame callback so the Navigator is mutated after the current frame completes. This preserves the existing behavior — the menu still closes on orientation change — but no longer mutates the Navigator during build. A mounted guard avoids touching a disposed State, and the callback is only scheduled when a route is actually open.

Tests

  • Added a regression test (Dropdown menu opened inside a dialog is dismissed on orientation change without throwing) that opens a DropdownButton menu inside an AlertDialog, rotates the view, and asserts no exception is thrown and the menu is dismissed. It fails on master and passes with this change.
  • The existing test Dropdown menus are dismissed on screen orientation changes, but not on keyboard hide continues to pass unmodified.

Related Issues

Fixes #171011

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages labels Jun 1, 2026
@google-cla

google-cla Bot commented Jun 1, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request defers the dismissal of an open dropdown menu during an orientation change by using a post-frame callback, preventing Navigator mutations during the build phase. It also adds a regression test to verify that a dropdown menu opened inside a dialog is dismissed without throwing an exception when the orientation changes. There are no review comments, and I have no feedback to provide.

When a DropdownButton's menu is open and the device orientation changes,
_DropdownButtonState.build() dismissed the open route synchronously by
calling _removeDropdownRoute() -> Navigator.removeRoute(). Mutating the
Navigator during build triggers a "setState() or markNeedsBuild() called
during build" assertion (surfaced via the route's animation listeners),
which is especially reproducible when the dropdown lives inside a dialog.

Defer the route dismissal to a post-frame callback so the Navigator is
mutated after the current frame, preserving the existing behavior of
closing the menu on orientation change without throwing.

Fixes flutter#171011
@ishivamg
ishivamg force-pushed the fix/171011-dropdown-orientation-crash branch from 1a5f765 to 14e4430 Compare June 1, 2026 06:04
@ishivamg

ishivamg commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

The Check Code Freeze job is failing here because this PR touches the frozen Material paths (packages/flutter/lib/src/material/dropdown.dart and packages/flutter/test/material/dropdown_test.dart), per the Material & Cupertino freeze tracked in #184093.

This change is a crash fix for #171011: opening a DropdownButton menu inside a dialog and then rotating the device throws setState() or markNeedsBuild() called during build, because the open route is dismissed synchronously during build(). The fix defers the dismissal to a post-frame callback and is covered by a regression test (fails on master, passes with the change); existing Material tests are unaffected.

Since I can't apply the override code freeze label myself, could a maintainer from team-design/team-framework advise whether this qualifies for an override during the freeze, or whether it should instead be re-targeted to material_ui in flutter/packages? Happy to do either. Thanks!

@navaronbracke navaronbracke left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just one remark about the post frame callback

// current frame.
// See https://github.com/flutter/flutter/issues/171011
if (_dropdownRoute != null) {
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do recall that there's a little trick to check what scheduler phase we are in, before posting a post frame callback: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/raw_menu_anchor.dart#L565-L578

@navaronbracke

navaronbracke commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

The Check Code Freeze job is failing here because this PR touches the frozen Material paths (packages/flutter/lib/src/material/dropdown.dart and packages/flutter/test/material/dropdown_test.dart), per the Material & Cupertino freeze tracked in #184093.

This change is a crash fix for #171011: opening a DropdownButton menu inside a dialog and then rotating the device throws setState() or markNeedsBuild() called during build, because the open route is dismissed synchronously during build(). The fix defers the dismissal to a post-frame callback and is covered by a regression test (fails on master, passes with the change); existing Material tests are unaffected.

Since I can't apply the override code freeze label myself, could a maintainer from team-design/team-framework advise whether this qualifies for an override during the freeze, or whether it should instead be re-targeted to material_ui in flutter/packages? Happy to do either. Thanks!

Per the process for decoupling, I would mark this PR as a draft (so we can cross reference it), and then open a new PR in material_ui once contributions there are allowed.
I think the timeline for opening contributions in material_ui is sometime in June?

Apply the scheduler-phase check (matching raw_menu_anchor.dart) so the
dropdown route is removed synchronously unless we are mid-build, in which
case the removal is deferred to a post-frame callback.

Addresses review feedback on flutter#187366

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ishivamg
ishivamg marked this pull request as draft June 1, 2026 18:57
@ishivamg

ishivamg commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

The Check Code Freeze job is failing here because this PR touches the frozen Material paths (packages/flutter/lib/src/material/dropdown.dart and packages/flutter/test/material/dropdown_test.dart), per the Material & Cupertino freeze tracked in #184093.
This change is a crash fix for #171011: opening a DropdownButton menu inside a dialog and then rotating the device throws setState() or markNeedsBuild() called during build, because the open route is dismissed synchronously during build(). The fix defers the dismissal to a post-frame callback and is covered by a regression test (fails on master, passes with the change); existing Material tests are unaffected.
Since I can't apply the override code freeze label myself, could a maintainer from team-design/team-framework advise whether this qualifies for an override during the freeze, or whether it should instead be re-targeted to material_ui in flutter/packages? Happy to do either. Thanks!

Per the process for decoupling, I would mark this PR as a draft (so we can cross reference it), and then open a new PR in material_ui once contributions there are allowed. I think the timeline for opening contributions in material_ui is sometime in June?

Thanks for the review @navaronbracke ! I've applied the scheduler-phase check — the dropdown route is now removed synchronously unless we're in SchedulerPhase.persistentCallbacks (mid-build), in which case the removal is deferred to a post-frame callback, matching the raw_menu_anchor.dart pattern. flutter test packages/flutter/test/material/dropdown_test.dart passes, including the #171011 regression test.

Per your suggestion, I'm marking this PR as a draft for cross-reference, and I'll port the change to material_ui in flutter/packages once contributions there are open. Thanks!

@Piinks Piinks added the Decoupling: Not ready to port yet Instructions will be provided when this is ready to move to flutter/packages. label Jun 24, 2026
@Piinks

Piinks commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

I've marked this PR as not ready to port to flutter/packages yet.
We'll provide instructions to move this change over to material_ui/cupertino_ui once ready to receive PRs. Thank you!

@Piinks Piinks added Decoupling: Port to flutter/packages This PR is ready to be ported to flutter/packages. We will provide instructions to do so. and removed Decoupling: Not ready to port yet Instructions will be provided when this is ready to move to flutter/packages. labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

This pull request contains changes to Material or Cupertino, which are currently frozen in this repository.

Changes should be made in material_ui and/or cupertino_ui in the flutter/packages repository.

Please refer to #188444 for instructions.

@Piinks

Piinks commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

This PR is now ready to port over to flutter/packages! 🥳
See guidance and instructions in #188444
You'll need to sign the CLA before we can review the PR as well. Thanks!

@ishivamg

Copy link
Copy Markdown
Contributor Author

Ported to flutter/packages: flutter/packages#12821
Closing per #188444

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

Labels

Decoupling: Port to flutter/packages This PR is ready to be ported to flutter/packages. We will provide instructions to do so. framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Opening dropdownMenu from alertDialog and rotating the screen throws error.

3 participants