feat: Add autofocus for MenuItemButton (#139396)

MenuAnchor for Material 3 is great for contextual menus but there are some minor issues related to accessibility.
This PR aims to close the gap by adding `autofocus` to the menu item button so keyboard navigation is more intuitive. Otherwise, it becomes a mess to navigate through just keyboards.

Partially resolves #139395

## Additional Notes

I should mention, I have not written tests for this due to it's trivial nature. I also lack the experience of writing Flutter tests in general, so if someone feels inclined to take over this PR and add it they're welcome to.
This commit is contained in:
FernTheDev 2024-06-26 20:46:40 -04:00 committed by GitHub
parent def5a38e02
commit e706d7d0b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 0 deletions

View file

@ -856,6 +856,7 @@ class MenuItemButton extends StatefulWidget {
this.requestFocusOnHover = true,
this.onFocusChange,
this.focusNode,
this.autofocus = false,
this.shortcut,
this.semanticsLabel,
this.style,
@ -897,6 +898,9 @@ class MenuItemButton extends StatefulWidget {
/// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode;
/// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus;
/// The optional shortcut that selects this [MenuItemButton].
///
/// {@macro flutter.material.MenuBar.shortcuts_note}
@ -1140,6 +1144,7 @@ class _MenuItemButtonState extends State<MenuItemButton> {
onFocusChange: widget.enabled ? widget.onFocusChange : null,
focusNode: _focusNode,
style: mergedStyle,
autofocus: widget.enabled && widget.autofocus,
statesController: widget.statesController,
clipBehavior: widget.clipBehavior,
isSemanticButton: null,

View file

@ -2229,6 +2229,45 @@ void main() {
expect(find.text('leadingIcon'), findsOneWidget);
});
testWidgets('autofocus is used when set and widget is enabled',
(WidgetTester tester) async {
listenForFocusChanges();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Column(
children: <Widget>[
MenuAnchor(
controller: controller,
menuChildren: <Widget>[
MenuItemButton(
autofocus: true,
// Required for clickability.
onPressed: () {},
child: Text(TestMenu.mainMenu0.label),
),
MenuItemButton(
onPressed: () {},
child: Text(TestMenu.mainMenu1.label),
),
],
),
const Expanded(child: Placeholder()),
],
),
),
),
);
controller.open();
await tester.pump();
expect(controller.isOpen, equals(true));
expect(focusedMenu, equals('MenuItemButton(Text("${TestMenu.mainMenu0.label}"))'));
});
testWidgets('trailingIcon is used when set', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(