flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.75k stars 27.63k forks source link

[web:a11y] MenuAnchor individual entries read as "group" instead of "button" #157743

Open ChristianEdwardPadilla opened 1 month ago

ChristianEdwardPadilla commented 1 month ago

Steps to reproduce

  1. On MacOS or Windows, launch a screen reader which can read the browser.
  2. Launch the sample Flutter app.
  3. Navigate to the menu in the top left.
  4. Listen to the screen reader identify each of the three buttons as "group" rather than "button".

Expected results

The buttons should be identified as buttons.

Actual results

The buttons are identified as "group".

Code sample

This can be reprod directly in the MenuAnchor docs: https://api.flutter.dev/flutter/material/MenuAnchor-class.html.

Or, by running the sample app:

Code sample ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Flutter code sample for [MenuAnchor]. void main() => runApp(const MenuApp()); /// An enhanced enum to define the available menus and their shortcuts. /// /// Using an enum for menu definition is not required, but this illustrates how /// they could be used for simple menu systems. enum MenuEntry { about('About'), showMessage( 'Show Message', SingleActivator(LogicalKeyboardKey.keyS, control: true)), hideMessage( 'Hide Message', SingleActivator(LogicalKeyboardKey.keyS, control: true)), colorMenu('Color Menu'), colorRed('Red Background', SingleActivator(LogicalKeyboardKey.keyR, control: true)), colorGreen('Green Background', SingleActivator(LogicalKeyboardKey.keyG, control: true)), colorBlue('Blue Background', SingleActivator(LogicalKeyboardKey.keyB, control: true)); const MenuEntry(this.label, [this.shortcut]); final String label; final MenuSerializableShortcut? shortcut; } class MyCascadingMenu extends StatefulWidget { const MyCascadingMenu({super.key, required this.message}); final String message; @override State createState() => _MyCascadingMenuState(); } class _MyCascadingMenuState extends State { MenuEntry? _lastSelection; final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button'); ShortcutRegistryEntry? _shortcutsEntry; Color get backgroundColor => _backgroundColor; Color _backgroundColor = Colors.red; set backgroundColor(Color value) { if (_backgroundColor != value) { setState(() { _backgroundColor = value; }); } } bool get showingMessage => _showingMessage; bool _showingMessage = false; set showingMessage(bool value) { if (_showingMessage != value) { setState(() { _showingMessage = value; }); } } @override void didChangeDependencies() { super.didChangeDependencies(); // Dispose of any previously registered shortcuts, since they are about to // be replaced. _shortcutsEntry?.dispose(); // Collect the shortcuts from the different menu selections so that they can // be registered to apply to the entire app. Menus don't register their // shortcuts, they only display the shortcut hint text. final Map shortcuts = { for (final MenuEntry item in MenuEntry.values) if (item.shortcut != null) item.shortcut!: VoidCallbackIntent(() => _activate(item)), }; // Register the shortcuts with the ShortcutRegistry so that they are // available to the entire application. _shortcutsEntry = ShortcutRegistry.of(context).addAll(shortcuts); } @override void dispose() { _shortcutsEntry?.dispose(); _buttonFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MenuAnchor( childFocusNode: _buttonFocusNode, menuChildren: [ MenuItemButton( child: Text(MenuEntry.about.label), onPressed: () => _activate(MenuEntry.about), ), if (_showingMessage) MenuItemButton( onPressed: () => _activate(MenuEntry.hideMessage), shortcut: MenuEntry.hideMessage.shortcut, child: Text(MenuEntry.hideMessage.label), ), if (!_showingMessage) MenuItemButton( onPressed: () => _activate(MenuEntry.showMessage), shortcut: MenuEntry.showMessage.shortcut, child: Text(MenuEntry.showMessage.label), ), SubmenuButton( menuChildren: [ MenuItemButton( onPressed: () => _activate(MenuEntry.colorRed), shortcut: MenuEntry.colorRed.shortcut, child: Text(MenuEntry.colorRed.label), ), MenuItemButton( onPressed: () => _activate(MenuEntry.colorGreen), shortcut: MenuEntry.colorGreen.shortcut, child: Text(MenuEntry.colorGreen.label), ), MenuItemButton( onPressed: () => _activate(MenuEntry.colorBlue), shortcut: MenuEntry.colorBlue.shortcut, child: Text(MenuEntry.colorBlue.label), ), ], child: const Text('Background Color'), ), ], builder: (BuildContext context, MenuController controller, Widget? child) { return TextButton( focusNode: _buttonFocusNode, onPressed: () { if (controller.isOpen) { controller.close(); } else { controller.open(); } }, child: const Text('OPEN MENU'), ); }, ), Expanded( child: Container( alignment: Alignment.center, color: backgroundColor, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(12.0), child: Text( showingMessage ? widget.message : '', style: Theme.of(context).textTheme.headlineSmall, ), ), Text(_lastSelection != null ? 'Last Selected: ${_lastSelection!.label}' : ''), ], ), ), ), ], ); } void _activate(MenuEntry selection) { setState(() { _lastSelection = selection; }); switch (selection) { case MenuEntry.about: showAboutDialog( context: context, applicationName: 'MenuBar Sample', applicationVersion: '1.0.0', ); case MenuEntry.hideMessage: case MenuEntry.showMessage: showingMessage = !showingMessage; case MenuEntry.colorMenu: break; case MenuEntry.colorRed: backgroundColor = Colors.red; case MenuEntry.colorGreen: backgroundColor = Colors.green; case MenuEntry.colorBlue: backgroundColor = Colors.blue; } } } class MenuApp extends StatelessWidget { const MenuApp({super.key}); static const String kMessage = '"Talk less. Smile more." - A. Burr'; @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(useMaterial3: true), home: const Scaffold( body: SafeArea(child: MyCascadingMenu(message: kMessage))), ); } } ```

Screenshots or Video

No response

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel google3, on Debian GNU/Linux locale en_US.UTF-8) • Framework revision 621e7ef951 (33 days ago), 2024-09-25T00:00:00.000 • Engine revision d6d5fdba6a • Dart version dd73afd20b [✓] Connected device (1 available) • Linux (desktop) • linux • linux-x64 • Debian GNU/Linux ```
huycozy commented 1 month ago

Reproduced this issue on Web app, on Flutter stable 3.24.4 and master 3.27.0-1.0.pre.277. On macOS app, the menu button is not read when navigating to it.

Demo screenshot ![Screenshot 2024-10-29 at 15 44 24](https://github.com/user-attachments/assets/f31e9724-96f4-4901-a30d-684e985c6640)
flutter doctor -v (stable and master) ```console [✓] Flutter (Channel stable, 3.24.4, on macOS 15.0.1 24A348 darwin-x64, locale en-VN) • Flutter version 3.24.4 on channel stable at /Users/huynq/Documents/GitHub/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 603104015d (12 hours ago), 2024-10-24 08:01:25 -0700 • Engine revision db49896cf2 • Dart version 3.5.4 • DevTools version 2.37.3 [✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) • Android SDK at /Users/huynq/Library/Android/sdk • Platform android-35, build-tools 35.0.0 • ANDROID_HOME = /Users/huynq/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 16.0) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 16A242d • CocoaPods version 1.15.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2024.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • android-studio-dir = /Applications/Android Studio.app • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314) [✓] Android Studio (version 2024.2) • Android Studio at /Applications/Android Studio Ladybug.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 21.0.3+-79915915-b509.11) [✓] IntelliJ IDEA Community Edition (version 2024.2.3) • IntelliJ at /Applications/IntelliJ IDEA CE.app • Flutter plugin version 81.1.3 • Dart plugin version 242.22855.32 [✓] VS Code (version 1.94.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.98.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-x64 • macOS 15.0.1 24A348 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 130.0.6723.70 ! Error: Browsing on the local area network for iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) [✓] Network resources • All expected network resources are available. • No issues found! ``` ```console [!] Flutter (Channel master, 3.27.0-1.0.pre.277, on macOS 15.0.1 24A348 darwin-x64, locale en-VN) • Flutter version 3.27.0-1.0.pre.277 on channel master at /Users/huynq/Documents/GitHub/flutter_master ! Warning: `flutter` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 4a8e2f983f (8 minutes ago), 2024-10-28 23:06:38 -0400 • Engine revision c05af6f939 • Dart version 3.7.0 (build 3.7.0-75.0.dev) • DevTools version 2.40.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) • Android SDK at /Users/huynq/Library/Android/sdk • Platform android-35, build-tools 35.0.0 • ANDROID_HOME = /Users/huynq/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 16.0) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 16A242d • CocoaPods version 1.15.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2024.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • android-studio-dir = /Applications/Android Studio.app • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314) [✓] Android Studio (version 2024.2) • Android Studio at /Applications/Android Studio Ladybug.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 21.0.3+-79915915-b509.11) [✓] IntelliJ IDEA Community Edition (version 2024.2.3) • IntelliJ at /Applications/IntelliJ IDEA CE.app • Flutter plugin version 81.1.3 • Dart plugin version 242.22855.32 [✓] VS Code (version 1.94.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.98.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-x64 • macOS 15.0.1 24A348 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 130.0.6723.70 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ```
franciscojma86 commented 4 weeks ago

b/364808123

chunhtai commented 4 weeks ago

should be an easy fix to set button = true on each item. cc @QuncCccccc since you have worked on menu anchor recently

QuncCccccc commented 4 weeks ago

SubmenuButton and MenuItemButton are ButtonStyleButtons, so the button flag should have been set to true🧐.

yjbanov commented 3 weeks ago

Acknowledged. If anything needs to be done in the web engine, please feel free to redirect the issue to team-web.