From 6656356bbf23dad8935e1ba7679ccc3dabb7d487 Mon Sep 17 00:00:00 2001 From: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 10 Feb 2022 20:30:17 -0800 Subject: [PATCH] EditableText does not request focus on autofill (#97846) --- .../lib/src/widgets/editable_text.dart | 23 ++++++- .../test/widgets/editable_text_test.dart | 67 ++++++++++++++----- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 6c11a9fa97..f0c2061660 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2391,8 +2391,27 @@ class EditableTextState extends State with AutomaticKeepAliveClien widget.controller.selection = selection; // This will show the keyboard for all selection changes on the - // EditableWidget, not just changes triggered by user gestures. - requestKeyboard(); + // EditableText except for those triggered by a keyboard input. + // Typically EditableText shouldn't take user keyboard input if + // it's not focused already. If the EditableText is being + // autofilled it shouldn't request focus. + switch (cause) { + case null: + case SelectionChangedCause.doubleTap: + case SelectionChangedCause.drag: + case SelectionChangedCause.forcePress: + case SelectionChangedCause.longPress: + case SelectionChangedCause.scribble: + case SelectionChangedCause.tap: + case SelectionChangedCause.toolbar: + requestKeyboard(); + break; + case SelectionChangedCause.keyboard: + if (_hasFocus) { + requestKeyboard(); + } + break; + } if (widget.selectionControls == null) { _selectionOverlay?.dispose(); _selectionOverlay = null; diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 40ee809d53..72591a6658 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -3013,8 +3013,6 @@ void main() { SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorBackwardByWord, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3049,8 +3047,6 @@ void main() { actions: [ SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3122,8 +3118,6 @@ void main() { SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorBackwardByWord, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3156,8 +3150,6 @@ void main() { actions: [ SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3240,8 +3232,6 @@ void main() { SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorBackwardByWord, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3276,8 +3266,6 @@ void main() { actions: [ SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3360,8 +3348,6 @@ void main() { SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorBackwardByWord, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -3394,8 +3380,6 @@ void main() { actions: [ SemanticsAction.moveCursorForwardByCharacter, SemanticsAction.moveCursorForwardByWord, - SemanticsAction.setSelection, - SemanticsAction.setText, ], ), ); @@ -8137,6 +8121,57 @@ void main() { ); }); + testWidgets( + 'Autofill does not request focus', + (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/91354 . + final FocusNode focusNode1 = FocusNode(); + final EditableText editableText1 = EditableText( + showSelectionHandles: true, + maxLines: 2, + controller: TextEditingController(), + focusNode: focusNode1, + cursorColor: Colors.red, + backgroundCursorColor: Colors.blue, + style: Typography.material2018().black.subtitle1!.copyWith(fontFamily: 'Roboto'), + keyboardType: TextInputType.text, + ); + + final FocusNode focusNode2 = FocusNode(); + final EditableText editableText2 = EditableText( + showSelectionHandles: true, + maxLines: 2, + controller: TextEditingController(), + focusNode: focusNode2, + cursorColor: Colors.red, + backgroundCursorColor: Colors.blue, + style: Typography.material2018().black.subtitle1!.copyWith(fontFamily: 'Roboto'), + keyboardType: TextInputType.text, + ); + + await tester.pumpWidget(MaterialApp( + home: Center( + child: Column( + children: [editableText1, editableText2], + ), + ), + )); + + // editableText1 has the focus. + await tester.tap(find.byWidget(editableText1)); + await tester.pumpAndSettle(); + + final EditableTextState state2 = tester.state(find.byWidget(editableText2)); + // Update editableText2 when it's not focused. It should not request focus. + state2.updateEditingValue( + const TextEditingValue(text: 'password', selection: TextSelection.collapsed(offset: 8)), + ); + await tester.pumpAndSettle(); + + expect(focusNode1.hasFocus, isTrue); + expect(focusNode2.hasFocus, isFalse); + }); + testWidgets('setEditingState is not called when text changes', (WidgetTester tester) async { // We shouldn't get a message here because this change is owned by the platform side. const String testText = 'flutter is the best!';