From 13fcedc7dc8cb5eed9b076fb8a8efd074ad0b568 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 15 Sep 2020 22:22:04 -0700 Subject: [PATCH] [flutter] elide semantic information from certain widget spans (#65857) --- .../flutter/lib/src/rendering/paragraph.dart | 22 +++++---- packages/flutter/test/widgets/text_test.dart | 47 +++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart index d724dd00824..63cafbc4eab 100644 --- a/packages/flutter/lib/src/rendering/paragraph.dart +++ b/packages/flutter/lib/src/rendering/paragraph.dart @@ -918,16 +918,18 @@ class RenderParagraph extends RenderBox ); if (info.isPlaceholder) { - final SemanticsNode childNode = children.elementAt(placeholderIndex++); - final TextParentData parentData = child!.parentData as TextParentData; - childNode.rect = Rect.fromLTWH( - childNode.rect.left, - childNode.rect.top, - childNode.rect.width * parentData.scale!, - childNode.rect.height * parentData.scale!, - ); - newChildren.add(childNode); - child = childAfter(child); + if (children.isNotEmpty) { + final SemanticsNode childNode = children.elementAt(placeholderIndex++); + final TextParentData parentData = child!.parentData as TextParentData; + childNode.rect = Rect.fromLTWH( + childNode.rect.left, + childNode.rect.top, + childNode.rect.width * parentData.scale!, + childNode.rect.height * parentData.scale!, + ); + newChildren.add(childNode); + child = childAfter(child); + } } else { final SemanticsConfiguration configuration = SemanticsConfiguration() ..sortKey = OrdinalSortKey(ordinal++) diff --git a/packages/flutter/test/widgets/text_test.dart b/packages/flutter/test/widgets/text_test.dart index 4ec4e5681d0..c4f4596805e 100644 --- a/packages/flutter/test/widgets/text_test.dart +++ b/packages/flutter/test/widgets/text_test.dart @@ -958,6 +958,53 @@ void main() { paragraph.layout(const ui.ParagraphConstraints(width: 1000)); expect(paragraph.getBoxesForRange(2, 2), isEmpty); }); + + // Regression test for https://github.com/flutter/flutter/issues/65818 + testWidgets('WidgetSpans with no semantic information are elided from semantics', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + // Without the fix for this bug the pump widget will throw a RangeError. + await tester.pumpWidget( + RichText( + textDirection: TextDirection.ltr, + text: TextSpan(children: [ + const WidgetSpan(child: SizedBox.shrink()), + TextSpan( + text: 'HELLO', + style: const TextStyle(color: Colors.black), + recognizer: TapGestureRecognizer()..onTap = () {}, + ) + ]), + ), + ); + + expect(semantics, hasSemantics(TestSemantics.root( + children: [ + TestSemantics( + id: 1, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0), + transform: Matrix4( + 3.0,0.0,0.0,0.0, + 0.0,3.0,0.0,0.0, + 0.0,0.0,1.0,0.0, + 0.0,0.0,0.0,1.0, + ), + children: [ + TestSemantics( + rect: const Rect.fromLTRB(-4.0, -4.0, 74.0, 18.0), + id: 2, + label: 'HELLO', + actions: [ + SemanticsAction.tap, + ], + flags: [ + SemanticsFlag.isLink, + ], + ), + ], + ), + ], + ))); + }, semanticsEnabled: true, skip: isBrowser); // Browser semantics have different sizes. } Future _pumpTextWidget({ WidgetTester tester, String text, TextOverflow overflow }) {