Skip to content

[material_ui] Fix ExpansionTile WidgetSpan semantics assertion - #12765

Merged
auto-submit[bot] merged 3 commits into
flutter:mainfrom
ahamsel:fix/material-ui-expansion-tile-widgetspan-semantics
Oct 9, 2026
Merged

auto-submit[bot] merged 3 commits into
flutter:mainfrom
ahamsel:fix/material-ui-expansion-tile-widgetspan-semantics

Conversation

@ahamsel

@ahamsel ahamsel commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

On Android, embedding an ExpansionTile in a WidgetSpan can trigger a sibling-configuration conflict assertion when semantics are enabled. Its live-region Semantics uses AccessibilityFocusBlockType.blockNode but can merge into the surrounding semantics tree.

This makes the live region a semantics container, so blockNode applies to an explicit node. The regression test uses the simpler reproduction suggested in flutter/flutter#190639 and relies on the semantics handle managed by testWidgets.

Fixes flutter/flutter#188298.

This ports flutter/flutter#190639 to packages/material_ui. The earlier package PR, #12383, was closed while contributions were paused. On October 2, I rebased this replacement onto current main (10cde9a3b) to refresh its stale CI configuration. The contribution itself is unchanged: the semantics fix, its regression test, and a pending changelog entry. There are no custom CI changes in this PR.

I used AI to help with the fix and regression test.

Pre-Review Checklist

Local validation after the October 2 rebase, on macOS with the already-installed Flutter 3.47.5 / Dart 3.13.4:

  • Full material_ui suite: flutter test --no-pub --concurrency=2 — 8,222 passed, 12 existing skips, no failures.
  • Full example suite: flutter test --no-pub --concurrency=1 in packages/material_ui/example — 406 passed, two existing skips, no failures.
  • ExpansionTile file separately: 64 passed, one existing skip (also included in the full suite).
  • Defaults generator suite: dart test — 72 passed.
  • Full-package flutter analyze --no-pub: no issues.
  • Dart formatting and git diff --check: passed.
  • dart run script/tool/bin/flutter_plugin_tools.dart validate --packages material_ui --check-for-missing-changes --base-branch upstream/main: passed.

I repeated the regression's fail-without-fix check on the rebased branch: removing container: true produces the original !conflict assertion; restoring it makes the test pass. The existing flutter_goldens harness skips golden-image comparisons; I did not change that behavior. These are local test results, not a claim that repository-wide CI has passed.

GitHub automatically removed the CICD label after the October 2 push. Could a maintainer reapply it for the refreshed head so the repository presubmits can run?

The release metadata uses material_ui's batch-release convention: a pending changelog entry with version: patch.

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

@github-actions github-actions Bot added p: material_ui triage-design Should be looked at in design triage labels Sep 5, 2026
@ahamsel
ahamsel marked this pull request as ready for review September 5, 2026 12:06

@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 sets container: true on the Semantics widget in ExpansionTile to ensure the live region forms the node that blockNode removes from focus traversal, resolving an Android semantics assertion when embedded in a WidgetSpan. It also adds a regression test for this scenario and a changelog entry. There are no review comments, so no feedback is provided.

@QuncCccccc

Copy link
Copy Markdown
Contributor

Thanks for the contribution! You haven’t completed all of the items in the PR checklist above, so I’m assuming this is a work in progress and am marking it as a Draft. Please review the checklist, updating the PR as appropriate, and when that’s complete please feel free to mark the PR as ready for review.

@QuncCccccc
QuncCccccc marked this pull request as draft September 8, 2026 21:32
@ahamsel
ahamsel marked this pull request as ready for review September 8, 2026 22:37
@ahamsel

ahamsel commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for flagging this. I've completed the checklist and marked the PR ready for review.

The full material_ui, example, accessibility-app, and defaults-generator suites passed locally: 8,769 tests passed, with 13 existing skips. Full-package analysis, formatting, and repository validation also passed. I've added the results and the existing golden-comparison limitation to the description.

CI is still awaiting the CICD label. Could a maintainer enable it when convenient?

@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 sets container: true on the Semantics widget within ExpansionTile to resolve an Android semantics assertion when embedded in a WidgetSpan, and includes a regression test. The review feedback recommends using addTearDown to ensure the SemanticsHandle is properly disposed of even if the test fails.

Comment on lines +2237 to +2258
final SemanticsHandle handle = tester.ensureSemantics();

await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: Text.rich(
TextSpan(
children: <InlineSpan>[
WidgetSpan(
child: ExpansionTile(
title: Text('Header'),
children: <Widget>[Text('Details')],
),
),
],
),
),
),
),
);
expect(find.text('Header'), findsOneWidget);
handle.dispose();

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

To ensure that the SemanticsHandle is always disposed of, even if an assertion or expectation fails during the test, it is recommended to use addTearDown(handle.dispose); immediately after creating the handle. This prevents semantics from leaking into subsequent tests in the suite, which can cause unexpected failures or flakiness.

      final SemanticsHandle handle = tester.ensureSemantics();
      addTearDown(handle.dispose);

      await tester.pumpWidget(
        const MaterialApp(
          home: Scaffold(
            body: Text.rich(
              TextSpan(
                children: <InlineSpan>[
                  WidgetSpan(
                    child: ExpansionTile(
                      title: Text('Header'),
                      children: <Widget>[Text('Details')],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      );
      expect(find.text('Header'), findsOneWidget);

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.

Thanks. I tried addTearDown(handle.dispose), but this test then failed with "A SemanticsHandle was active at the end of the test": Flutter checks for active handles before that teardown runs.

testWidgets already enables semantics by default and manages its own handle, so I removed the redundant manual handle instead. All 64 ExpansionTile tests pass, with one existing skip, and formatting and analysis pass. I also rechecked that the simplified regression still fails with the original !conflict assertion when container: true is removed, then passes when it is restored.

@elliette
elliette self-requested a review September 29, 2026 22:16
@elliette elliette added the CICD Run CI/CD label Oct 1, 2026
@elliette
elliette removed their request for review October 1, 2026 23:39
@elliette

elliette commented Oct 1, 2026

Copy link
Copy Markdown
Member

Removing myself as reviewer since I will be out for the next couple weeks. This should get picked up in the next triage meeting.

@ahamsel
ahamsel force-pushed the fix/material-ui-expansion-tile-widgetspan-semantics branch from 1d2f389 to fffb2b2 Compare October 3, 2026 01:33
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 3, 2026
@QuncCccccc
QuncCccccc self-requested a review October 6, 2026 21:27
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Oct 6, 2026

@QuncCccccc QuncCccccc 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.

LGTM. Thanks for your contribution!

Adding an a11y expert as a second reviewer:)

@hannah-hyj hannah-hyj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thanks!

@QuncCccccc
QuncCccccc force-pushed the fix/material-ui-expansion-tile-widgetspan-semantics branch from fffb2b2 to d5513e1 Compare October 9, 2026 01:39
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 9, 2026
@QuncCccccc QuncCccccc added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Oct 9, 2026
@auto-submit
auto-submit Bot merged commit 1f55bfa into flutter:main Oct 9, 2026
13 checks passed
jmagman pushed a commit to jmagman/flutter that referenced this pull request Oct 9, 2026
flutter/packages@54706bd...d8d9e97

2026-10-09 [email protected] [pigeon] Reject unsupported native
interop features and fix generated code that doesn't compile
(flutter/packages#13153)
2026-10-09 [email protected] [material_ui] Fix
ExpansionTile WidgetSpan semantics assertion (flutter/packages#12765)
2026-10-08 [email protected] [in_app_purchase_storekit] Complete
StoreKit 2 `finish` when the transaction is not found
(flutter/packages#13037)
2026-10-08 [email protected] [video_player] Fix crash when
videoTrack has zero-duration minFrameDuration (flutter/packages#12796)
2026-10-08 [email protected]
[google_maps_flutter_platform_interface] Add map background color
configuration (flutter/packages#13014)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC [email protected] on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
flutter/packages@54706bd...d8d9e97

2026-10-09 [email protected] [pigeon] Reject unsupported native
interop features and fix generated code that doesn't compile
(flutter/packages#13153)
2026-10-09 [email protected] [material_ui] Fix
ExpansionTile WidgetSpan semantics assertion (flutter/packages#12765)
2026-10-08 [email protected] [in_app_purchase_storekit] Complete
StoreKit 2 `finish` when the transaction is not found
(flutter/packages#13037)
2026-10-08 [email protected] [video_player] Fix crash when
videoTrack has zero-duration minFrameDuration (flutter/packages#12796)
2026-10-08 [email protected]
[google_maps_flutter_platform_interface] Add map background color
configuration (flutter/packages#13014)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC [email protected] on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App 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.

ExpansionTile semantics assertion with nested WidgetSpan title in Flutter 3.44.2

4 participants