mirror of
https://github.com/flutter/flutter.git
synced 2026-02-11 21:33:08 +08:00
This PR adjusts the implementation of handling navigational shortcuts (i.e. arrow keys) on `MenuAnchor` and `DropdownMenu`. ## Motivation The direct outcome of this PR is to allow keyboard to enter submenus on Web: When the focus is on a `MenuAnchor` while the menu is open, pressing arrow keys should move the focus to the menu item. * Before the PR, this works for all platforms but Web, a problem described in https://github.com/flutter/flutter/issues/119532#issuecomment-2274705565. It is caused by the fact that `MenuAnchor` does not wrap itself with a `Shortcuts`, and therefore key events when the focus is on a `MenuAnchor` has been working only because the event falls back to the `Shortcuts` widget defined by `WidgetsApp`, whose default value happens to satisfy `MenuAnchor`'s needs - except on Web where arrow keys are defined to scroll instead of traverse. Instead of defining this problem as "just a patch for Web", I think it's better to define it as a problem of all platforms: `MenuAnchor`'s shortcuts should be independent of `WidgetsApp.shortcuts`. Because even if `WidgetsApp.shortcuts` is redefined as something else, people should probably still expect arrow keys to work on `MenuAnchor`. Therefore this PR makes `MenuAnchor` produce a `Shortcuts` by itself. ### Dropdown menu The fix above breaks `DropdownMenu`. `DropdownMenu` uses `MenuAnchor`, while defining its own shortcuts because, when filter is enabled: * The left and right arrow keys need to move the text carets instead * The up and down arrow keys need to "fake" directional navigation - the focus needs to stay on the text field, while some menu item is highlighted as if it is focused. Before the PR, `DropdownMenu` defines these shortcuts out of `MenuAnchor`. In order for the `DropdownMenu`'s shortcuts to take priority, these shortcuts are moved to between `MenuAnchor` and the `Textfield`. A test is added to verify that the left/right keys move text carets. Below are psuedo-widget-trees after the PR: ``` MenuAnchor |- Shortcuts(arrows->DirectionalFocusIntent) |- MenuAnchor.child |- menu DropdownMenu |- Actions(DirectionalFocusIntent->_dropdownMenuNavigation) |- MenuAnchor |- Shortcuts(arrows->DirectionalFocusIntent) |- Shortcuts(leftright->ExtendSelectionByCharacterIntent, updown->_dropdownMenuArrowIntent) | |- TextField | |- EditableText | |- Actions(DirectionalFocusIntent->DirectionalFocusAction.forTextField) |- menu ``` ## Known issues After this PR, traversing the menu still have quite a few problems, which are left for other PRs.