Skip to content

[A11y][iOS]: SnackBar shows more Voice Control labels than expected #157630

Closed

Description

@ChristianEdwardPadilla

Tracking b/325610878.

Steps to reproduce

  1. Enable iOS Voice Control in the settings app.
  2. In a Flutter app (minimal sample app below), cause a Material SnackBar widget to be displayed.
  3. Observe the Voice Control labels.

Expected results

There should be labels for interactive UI elements.

Actual results

There are more labels than there are interactive UI elements.

This was observed for Material2 and Material3 themes.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: false),
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void displaySnackBar(BuildContext context) {
    ScaffoldMessenger.of(context).clearSnackBars();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('test bar'),
        // // Enable these in order to see more numbered labels with iOS Voice Control.
        // action: SnackBarAction(
        //   label: 'close',
        //   onPressed: () => print('close'),
        // ),
        // showCloseIcon: true,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title, style: TextStyle(fontFamily: 'ProductSans')),
      ),
      body: Center(
        child: Text(
          'test',
          key: Key('CountText'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => displaySnackBar(context),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Screenshots or Video

screenshot of extra labels

IMG_0093

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel google3, on macOS 14.7 23H124 darwin-x64, locale en)
    • Framework revision 3cd12f2c06 (0 days ago), 2024-10-25T00:00:00.000
    • Engine revision c4b0184c87
    • Dart version 3067d697ae

[✓] Android toolchain - develop for Android devices (Android SDK version Stable)
    • Android SDK at google3
    • Platform Stable, build-tools Stable
    • Java binary at: /Applications/Android Studio with Blaze
      Beta.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915915-b509.11)

[✓] VS Code (version 1.94.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.86.0

[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer

[✓] Connected device (1 available)
    ! Error: Browsing on the local area network for CP’s iPhone (2). 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)

Activity

  1. added
    in triagePresently being triaged by the triage team
    on Oct 28, 2024
  2. huycozy commented on Oct 28, 2024

    @huycozy
    Member

    Reproduced this on Flutter stable and master channels.

    Flutter versions (stable and master)
    [✓] 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
    [!] Flutter (Channel master, 3.27.0-1.0.pre.231, on macOS 15.0.1 24A348 darwin-x64, locale en-VN)
        • Flutter version 3.27.0-1.0.pre.231 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 41b362b12c (22 minutes ago), 2024-10-24 22:59:25 -0400
        • Engine revision b1c2ba8c4d
        • Dart version 3.7.0 (build 3.7.0-65.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.
  3. added
    platform-iosiOS applications specifically
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    fyi-iosFor the attention of iOS platform team
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    and removed
    in triagePresently being triaged by the triage team
    on Oct 28, 2024
  4. jmagman commented on Oct 28, 2024

    @jmagman
    Member

    @huycozy does this reproduce on Android TalkBack?

  5. huycozy commented on Oct 28, 2024

    @huycozy
    Member

    does this reproduce on Android TalkBack?

    @jmagman I tried finding the equivalent of iOS's Voice Control on Android TalkBack but couldn't find the exact one. I also used Accessibility Scanner app (based on one issue I tried before at #104908), I encountered #104908 that I think unrelated to this issue. Please let me know if I'm missing something and I will check the issue again on Android.

  6. 12 remaining items

  7. meredithslota commented on Sep 3, 2026

    @meredithslota

    Following up, since the internal bug in question is our, ha.

    @chunhtai -- I investigated the mechanics of this in the engine and framework (with some agentic assistance), and I think there may be a slight misunderstanding of what is causing the number to appear:

    1. SnackBar is not tap-to-dismiss: A standard SnackBar content body has no onTap or tap-to-dismiss behavior. If a user actually touches the body text or uses Voice Control to say "Tap " on that badge, nothing happens (it fails silently because SemanticsObject.accessibilityActivate returns NO when there is no kTap action).
    2. Why Voice Control numbers it: In SemanticsObject.mm, accessibilityRespondsToUserInteraction returns YES whenever node.actions & ~kSystemActions != 0. Because SnackBar includes onDismiss for the VoiceOver escape gesture, the node has SemanticsAction::kDismiss. Since kDismiss is not in kSystemActions, Flutter tells iOS that the static container responds to user interaction, prompting Voice Control to place a number badge on an un-tappable element.
    3. The close button is not redundant: Because the body text is not tap-to-dismiss, the close icon is the only direct-touch affordance the user has to dismiss the bar. We don't want to merge or drop any buttons; we simply need the static container to not claim it is an interactive control.

    According to Apple's UIAccessibility.h, accessibilityRespondsToUserInteraction is meant for elements that perform an action on touch/tap (buttons, sliders, etc.), whereas kDismiss maps exclusively to accessibilityPerformEscape (the VoiceOver two-finger scrub / Esc key).

    Excluding kDismiss (and kShowOnScreen) from accessibilityRespondsToUserInteraction in SemanticsObject.mm resolves the issue, I think:

    • Voice Control stops placing a dead number badge over the static message.
    • The close icon retains its number badge and responds to "Tap ".
    • VoiceOver two-finger scrub dismiss continues to work unchanged via accessibilityPerformEscape.

    Does this seem reasonable?

  8. added
    team-iosOwned by iOS platform team
    and removed
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    on Sep 10, 2026
  9. chunhtai commented on Sep 10, 2026

    @chunhtai
    Contributor

    reassign to the ios team as the the fix will be in SemanticsObject.mm

  10. removed
    P2Important issues not at the top of the work list
    on Sep 10, 2026
  11. self-assigned this
    on Sep 10, 2026
  12. added
    P2Important issues not at the top of the work list
    triaged-iosTriaged by iOS platform team
    on Sep 10, 2026
  13. LouiseHsu commented on Oct 16, 2026

    @LouiseHsu
    Contributor

    Okay, I did a little more digging into this. I think just excluding kDismiss will not be enough to fix this.

    If you look at the SnackBar widget, it actually has 3 actions attributed to it. Its Semantics widget gives it kDismiss, but it actually leaks the kScrollUp/kScrollDown actions from the Dismissible it wraps. You can actually see the SnackBar assert this behaviour:

        expect(
          tester.getSemantics(find.text('snack')),
          matchesSemantics(
            isLiveRegion: true,
            hasDismissAction: true,
            hasScrollDownAction: true,
            hasScrollUpAction: true,
            label: 'snack',
            textDirection: TextDirection.ltr,
          ),
        );
        handle.dispose();
      });

    So I think to fix this, we have to exclude kDismiss, kScrollUp, and kScrollDown from accessibilityRespondsToUserInteraction, which should be fine because in general scrollable elements are rendered as a native UIScrollView, and shouldn't recieve a VoiceControl label, anyways.

  14. github-actions commented on Nov 7, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  15. locked as resolved and limited conversation to collaborators on Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)customer: quake (g3)found in release: 3.24Found to occur in 3.24found in release: 3.27Found to occur in 3.27p: material_uimaterial_ui package in flutter/packagespackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions