Fixes inputDecoration sibling explicit child not included in semantic… (#170079)

…s tree

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

fixes https://github.com/flutter/flutter/issues/169499

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

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

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
chunhtai 2025-06-09 13:10:46 -07:00 committed by GitHub
parent 89c91b8386
commit 60cbfb76a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 7 deletions

View File

@ -4690,8 +4690,8 @@ class _SemanticsConfigurationProvider {
///
/// This is typically use to recalculate certain properties when mutating
/// [effective] since [effective] may contain stale data from previous update.
/// Examples are [SemanticsConfiguration.isBlockingUserActions] or
/// [SemanticsConfiguration.elevation]. Otherwise, use [effective] instead.
/// An example is [SemanticsConfiguration.isBlockingUserActions]. Otherwise,
/// use [effective] instead.
SemanticsConfiguration get original {
if (_originalConfiguration == null) {
_effectiveConfiguration = _originalConfiguration = SemanticsConfiguration();
@ -4822,8 +4822,7 @@ typedef _MergeUpAndSiblingMergeGroups =
///
/// Merge all fragments from [mergeUp] and decide which [_RenderObjectSemantics]
/// should form a node, i.e. [shouldFormSemanticsNode] is true. Stores the
/// [_RenderObjectSemantics] that should form a node with elevation adjustments
/// into [_children].
/// [_RenderObjectSemantics] that should form a node into [_children].
///
/// At this point, walking the [_children] forms a tree
/// that exactly resemble the resulting semantics node tree.
@ -5279,16 +5278,35 @@ class _RenderObjectSemantics extends _SemanticsFragment with DiagnosticableTreeM
void _updateChildGeometry() {
assert(geometry != null);
final _SemanticsGeometry parentGeometry = geometry!;
for (final _RenderObjectSemantics child in _children) {
final _SemanticsGeometry childGeometry = _SemanticsGeometry.computeChildGeometry(
parentPaintClipRect: geometry!.paintClipRect,
parentSemanticsClipRect: geometry!.semanticsClipRect,
parentPaintClipRect: parentGeometry.paintClipRect,
parentSemanticsClipRect: parentGeometry.semanticsClipRect,
parentTransform: null,
parent: this,
child: child,
);
child._updateGeometry(newGeometry: childGeometry);
}
for (final _RenderObjectSemantics explicitSiblingChild in siblingMergeGroups
.expand<_SemanticsFragment>((List<_SemanticsFragment> group) => group)
.whereType<_RenderObjectSemantics>()
.expand(
(_RenderObjectSemantics siblingChild) =>
siblingChild.shouldFormSemanticsNode
? <_RenderObjectSemantics>[siblingChild]
: siblingChild._children,
)) {
final _SemanticsGeometry childGeometry = _SemanticsGeometry.computeChildGeometry(
parentPaintClipRect: parentGeometry.paintClipRect,
parentSemanticsClipRect: parentGeometry.semanticsClipRect,
parentTransform: parentGeometry.transform,
parent: this,
child: explicitSiblingChild,
);
explicitSiblingChild._updateGeometry(newGeometry: childGeometry);
}
}
void _updateGeometry({required _SemanticsGeometry newGeometry}) {
@ -5433,7 +5451,16 @@ class _RenderObjectSemantics extends _SemanticsFragment with DiagnosticableTreeM
for (final List<_SemanticsFragment> group in siblingMergeGroups) {
SemanticsConfiguration? configuration;
SemanticsNode? node;
final List<_RenderObjectSemantics> explicitChildren = <_RenderObjectSemantics>[];
for (final _SemanticsFragment fragment in group) {
if (fragment is _RenderObjectSemantics) {
if (fragment.shouldFormSemanticsNode) {
explicitChildren.add(fragment);
assert(fragment.configToMergeUp == null);
continue;
}
explicitChildren.addAll(fragment._children);
}
if (fragment.configToMergeUp != null) {
fragment.mergesToSibling = true;
node ??= fragment.owner.cachedSemanticsNode;
@ -5441,6 +5468,11 @@ class _RenderObjectSemantics extends _SemanticsFragment with DiagnosticableTreeM
configuration.absorb(fragment.configToMergeUp!);
}
}
final List<SemanticsNode> childrenNodes = <SemanticsNode>[];
for (final _RenderObjectSemantics explicitChild in explicitChildren) {
explicitChild._buildSemantics(usedSemanticsIds: usedSemanticsIds);
childrenNodes.addAll(explicitChild.semanticsNodes);
}
// Can be null if all fragments in switchableFragments are marked as explicit.
if (configuration != null) {
if (node == null || usedSemanticsIds.contains(node.id)) {
@ -5453,7 +5485,7 @@ class _RenderObjectSemantics extends _SemanticsFragment with DiagnosticableTreeM
fragment.owner.cachedSemanticsNode = node;
}
}
node.updateWith(config: configuration);
node.updateWith(config: configuration, childrenInInversePaintOrder: childrenNodes);
_producedSiblingNodesAndOwners[node] = group;
semanticsNodes.add(node);

View File

@ -663,6 +663,42 @@ void main() {
},
);
testWidgets('suffix has correct semantics', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/169499.
final UniqueKey suffix = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Scaffold(
body: Center(
child: TextField(
autofocus: true,
decoration: InputDecoration(
suffix: Semantics(
key: suffix,
identifier: 'myId',
container: true,
child: const SizedBox(width: 50, height: 50, child: Text('suffix')),
),
),
),
),
),
),
),
);
await tester.pumpAndSettle(); // Wait for autofocus and suffix animation.
expect(find.text('suffix'), findsOneWidget);
expect(
tester.semantics.find(find.byKey(suffix)),
containsSemantics(
label: 'suffix',
identifier: 'myId',
rect: const Rect.fromLTWH(0, 0, 50, 50),
),
);
});
testWidgets('sets cursorOpacityAnimates on EditableText correctly', (WidgetTester tester) async {
// True