Skip to content

[a11y] Semantic nodes are merged with conflicting actions even when one is blocked #170485

Open

Description

@Renzo-Olivares

Steps to reproduce

Run the following test:

testWidgets('does not merge conflicting actions even if one of them is blocked', (
  WidgetTester tester,
) async {
  final UniqueKey key = UniqueKey();
  await tester.pumpWidget(
    MaterialApp(
      home: Semantics(
        key: key,
        container: true,
        child: Column(
          children: <Widget>[
            IndexedSemantics(
              index: 0,
              child: Semantics(
                blockUserActions: true,
                label: 'label1',
                onTap: () {},
                child: const SizedBox(width: 10, height: 10),
              ),
            ),
            IndexedSemantics(
              index: 1,
              child: Semantics(
                label: 'label2',
                onTap: () {},
                child: const SizedBox(width: 10, height: 10),
              ),
            ),
          ],
        ),
      ),
    ),
  );
  final SemanticsNode node = tester.getSemantics(find.byKey(key));
  expect(
    node,
    matchesSemantics(
      children: <Matcher>[containsSemantics(label: 'label1'), containsSemantics(label: 'label2')],
    ),
  );
});

similarly for SliverSemantics

testWidgets(
  'does not merge conflicting actions even if one of them is blocked with Semantics widget',
  (WidgetTester tester) async {
    final UniqueKey key = UniqueKey();
    await tester.pumpWidget(
      boilerPlate(
        slivers: <Widget>[
          SliverSemantics(
            key: key,
            container: true,
            child: SliverList(
              delegate: SliverChildListDelegate(
                <Widget>[
                  Semantics(
                    blockUserActions: true,
                    label: 'label1',
                    onTap: () {},
                    child: const SizedBox(height: 10),
                  ),
                  Semantics(label: 'label2', onTap: () {}, child: const SizedBox(height: 10)),
                ],
                // addSemanticIndexes: true, default, setting to false causes this test to pass.
              ),
            ),
          ),
        ],
      ),
    );
    final SemanticsNode node = tester.getSemantics(find.byKey(key));
    expect(
      node,
      matchesSemantics(
        children: <Matcher>[
          containsSemantics(label: 'label1'),
          containsSemantics(label: 'label2'),
        ],
      ),
    );
  },
);

Expected results

Semantic nodes under Column/SliverList are not merged.

Actual results

Semantic nodes under Column/SliverList are merged.

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: has semantics without actions: [tap, focus, longPress, scrollLeft, scrollRight, scrollUp,
scrollDown, increase, decrease, showOnScreen, moveCursorForwardByCharacter,
moveCursorBackwardByCharacter, setSelection, copy, cut, paste, didGainAccessibilityFocus,
didLoseAccessibilityFocus, dismiss, moveCursorForwardByWord, moveCursorBackwardByWord, setText]
without flags: [hasCheckedState, isChecked, isCheckStateMixed, isSelected, hasSelectedState,
isButton, isSlider, isKeyboardKey, isLink, isTextField, isReadOnly, isFocused, isFocusable,
hasEnabledState, isEnabled, isInMutuallyExclusiveGroup, isHeader, isObscured, isMultiline,
namesRoute, scopesRoute, isHidden, isImage, isLiveRegion, hasToggledState, isToggled,
hasImplicitScrolling, hasExpandedState, isExpanded, hasRequiredState, isRequired]  with children:
            Child 1 has semantics with label: label1
            Child 2 has semantics with label: label2
  Actual: SemanticsNode:<SemanticsNode#4(Rect.fromLTRB(0.0, 0.0, 800.0, 600.0), actions: [tap],
label: "label1\nlabel2", textDirection: ltr, indexInParent: 0)>
   Which: missing actions: [] unexpected actions: [tap]

Activity

  1. added
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    on Jun 11, 2025
  2. chunhtai commented on Jun 18, 2025

    @chunhtai
    Contributor

    This is due to when semantics configuration merging, we lose the information about whether a missing action is not assigned or is blocked. If we want to fix this, we have to propagate this information when two configuration is merged together.

  3. added
    P2Important issues not at the top of the work list
    on Jun 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listteam-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)triaged-accessibilityTriaged by Framework Accessibility team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions