Skip to content

[material_ui] Fixes SearchAnchor and SearchBar semantics - #12680

Open
chunhtai wants to merge 3 commits into
flutter:mainfrom
chunhtai:issues/190884
Open

[material_ui] Fixes SearchAnchor and SearchBar semantics#12680
chunhtai wants to merge 3 commits into
flutter:mainfrom
chunhtai:issues/190884

Conversation

@chunhtai

Copy link
Copy Markdown
Contributor

fixes flutter/flutter#190884

Too many inkwell and gesture recognizer wrapping the searchAnchor subtree that all tries to provide the same semantics action. This is ok in in renderobject because gesture arena will pick a winer, but not going to work in semantics

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Aug 28, 2026
@github-actions github-actions Bot added p: material_ui triage-design Should be looked at in design triage labels Aug 28, 2026

@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 addresses unlabeled tap target semantics in SearchBar and SearchAnchor by setting excludeFromSemantics: true on their internal interactive widgets and adding corresponding tests. Feedback suggests making excludeFromSemantics configurable on SearchAnchor to prevent accessibility regressions for custom builders, and adding an assertion in the new test to ensure the tap action nodes list is not empty.

Comment on lines +609 to +613
child: GestureDetector(
excludeFromSemantics: true,
onTap: _openView,
child: widget.builder(context, _searchController),
),

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.

high

Hardcoding excludeFromSemantics: true on the SearchAnchor's GestureDetector will cause an accessibility regression for custom builders. If a developer provides a custom non-tappable widget (like a simple Text or Icon) in widget.builder, it relies on this GestureDetector to provide the semantic tap action. With excludeFromSemantics: true, the widget will become completely non-interactive for screen readers.

To fix this while still resolving the duplicate semantics issue for SearchBar, we should introduce an excludeFromSemantics property to SearchAnchor (defaulting to false). Then, we can set it to true in the SearchAnchor.bar factory constructor.

        child: GestureDetector(
          excludeFromSemantics: widget.excludeFromSemantics,
          onTap: _openView,
          child: widget.builder(context, _searchController),
        ),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

right, this is something I worry, but is this a common case? @QuncCccccc , if people indeed tend to provide passive component in the builder, we will have turn this into a widget parameter that something like SearchAnchor(automaticallyTapHandler:)

which will base on this parameter to build or not build a gesture recognizer. and user will need to be smart about when to set it to true or false

Comment on lines +3472 to +3480
for (final SemanticsNode node in semantics.nodesWith(
actions: <SemanticsAction>[SemanticsAction.tap],
)) {
final bool isTextField = node.hasFlag(SemanticsFlag.isTextField);
final bool hasLabel = node.label.isNotEmpty;
final bool hasTooltip = node.tooltip.isNotEmpty;
final bool hasValue = node.value.isNotEmpty;
expect(isTextField || hasLabel || hasTooltip || hasValue, isTrue);
}

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.

medium

The test currently iterates over the nodes returned by semantics.nodesWith. If nodesWith returns an empty list (for example, if all tap actions were accidentally removed), the loop will not execute and the test will pass vacuously. To make the test more robust, we should assert that the list of nodes with tap actions is not empty.

    final List<SemanticsNode> tapNodes = semantics.nodesWith(
      actions: <SemanticsAction>[SemanticsAction.tap],
    );
    expect(tapNodes, isNotEmpty);
    for (final SemanticsNode node in tapNodes) {
      final bool isTextField = node.hasFlag(SemanticsFlag.isTextField);
      final bool hasLabel = node.label.isNotEmpty;
      final bool hasTooltip = node.tooltip.isNotEmpty;
      final bool hasValue = node.value.isNotEmpty;
      expect(isTextField || hasLabel || hasTooltip || hasValue, isTrue);
    }

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

Labels

CICD Run CI/CD p: material_ui triage-design Should be looked at in design triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[material_ui] SearchAnchor and SearchBar creates problematic semantics node

1 participant