Repository navigation
[material_ui] Fix ExpansionTile WidgetSpan semantics assertion - #12765
auto-submit[bot] merged 3 commits into
Conversation
There was a problem hiding this comment.
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.
|
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. |
|
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? |
There was a problem hiding this comment.
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.
| 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(); |
There was a problem hiding this comment.
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);There was a problem hiding this comment.
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.
|
Removing myself as reviewer since I will be out for the next couple weeks. This should get picked up in the next triage meeting. |
1d2f389 to
fffb2b2
Compare
QuncCccccc
left a comment
There was a problem hiding this comment.
LGTM. Thanks for your contribution!
Adding an a11y expert as a second reviewer:)
fffb2b2 to
d5513e1
Compare
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
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
On Android, embedding an
ExpansionTilein aWidgetSpancan trigger a sibling-configuration conflict assertion when semantics are enabled. Its live-regionSemanticsusesAccessibilityFocusBlockType.blockNodebut can merge into the surrounding semantics tree.This makes the live region a semantics container, so
blockNodeapplies to an explicit node. The regression test uses the simpler reproduction suggested in flutter/flutter#190639 and relies on the semantics handle managed bytestWidgets.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 currentmain(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
[shared_preferences]///).Local validation after the October 2 rebase, on macOS with the already-installed Flutter 3.47.5 / Dart 3.13.4:
material_uisuite:flutter test --no-pub --concurrency=2— 8,222 passed, 12 existing skips, no failures.flutter test --no-pub --concurrency=1inpackages/material_ui/example— 406 passed, two existing skips, no failures.dart test— 72 passed.flutter analyze --no-pub: no issues.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: trueproduces the original!conflictassertion; restoring it makes the test pass. The existingflutter_goldensharness 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
CICDlabel 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 withversion: patch.Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot 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
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