From d75cfa584c7d0dd36f3bc5628565410a45609ebc Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Tue, 23 Feb 2021 15:56:39 -0800 Subject: [PATCH] Hide selection handles from semantics (#76641) * Hide selection handles from semantics * update comment --- .../lib/src/widgets/text_selection.dart | 37 ++++++++++------- .../test/material/text_field_test.dart | 41 +++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 7f83b49648..7a77f88250 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -547,22 +547,31 @@ class TextSelectionOverlay { } Widget _buildHandle(BuildContext context, _TextSelectionHandlePosition position) { + Widget handle; if ((_selection.isCollapsed && position == _TextSelectionHandlePosition.end) || selectionControls == null) - return Container(); // hide the second handle when collapsed - return Visibility( - visible: handlesVisible, - child: _TextSelectionHandleOverlay( - onSelectionHandleChanged: (TextSelection newSelection) { _handleSelectionHandleChanged(newSelection, position); }, - onSelectionHandleTapped: onSelectionHandleTapped, - startHandleLayerLink: startHandleLayerLink, - endHandleLayerLink: endHandleLayerLink, - renderObject: renderObject, - selection: _selection, - selectionControls: selectionControls, - position: position, - dragStartBehavior: dragStartBehavior, - )); + handle = Container(); // hide the second handle when collapsed + else { + handle = Visibility( + visible: handlesVisible, + child: _TextSelectionHandleOverlay( + onSelectionHandleChanged: (TextSelection newSelection) { + _handleSelectionHandleChanged(newSelection, position); + }, + onSelectionHandleTapped: onSelectionHandleTapped, + startHandleLayerLink: startHandleLayerLink, + endHandleLayerLink: endHandleLayerLink, + renderObject: renderObject, + selection: _selection, + selectionControls: selectionControls, + position: position, + dragStartBehavior: dragStartBehavior, + ) + ); + } + return ExcludeSemantics( + child: handle, + ); } Widget _buildToolbar(BuildContext context) { diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index beef4ade02..588b32ce90 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -1119,6 +1119,47 @@ void main() { expect(fadeFinder, findsNothing); }); + testWidgets('selection handles are excluded from the semantics', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + final TextEditingController controller = TextEditingController(); + + await tester.pumpWidget( + overlay( + child: TextField( + controller: controller, + ), + ), + ); + + const String testValue = 'abcdefghi'; + await tester.enterText(find.byType(TextField), testValue); + expect(controller.value.text, testValue); + await skipPastScrollingAnimation(tester); + // Tap on the text field to show the handle. + await tester.tap(find.byType(TextField)); + await tester.pumpAndSettle(); + // The semantics should only have the text field. + expect(semantics, hasSemantics( + TestSemantics.root( + children: [ + TestSemantics( + id: 1, + flags: [SemanticsFlag.isTextField, SemanticsFlag.isFocused], + actions: [SemanticsAction.tap, + SemanticsAction.moveCursorBackwardByCharacter, SemanticsAction.setSelection, SemanticsAction.paste, + SemanticsAction.moveCursorBackwardByWord], + value: 'abcdefghi', + textDirection: TextDirection.ltr, + textSelection: const TextSelection.collapsed(offset: 9), + ), + ], + ), + ignoreRect: true, + ignoreTransform: true, + )); + semantics.dispose(); + }); + testWidgets('Mouse long press is just like a tap', (WidgetTester tester) async { final TextEditingController controller = TextEditingController();