From b236465bed897d67c7eb8768d62f4d502162558d Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Mon, 3 Feb 2020 20:40:10 -0800 Subject: [PATCH] Revert "Track lastKnownRemoteTextEditingValue separately from received data (#49406)" (#50082) This change broke some internal tests that set the text editing value to the same thing (the empty string) twice in a row. Note that in that case, the developer had subclassed EditableTextState and overridden the updateEditingValue method, which may or may not be relevant to the failure. This reverts commit 83d4d63a716ec8551f0d59f852a40925ddd73d8a. --- .../lib/src/widgets/editable_text.dart | 32 ++---- .../test/widgets/editable_text_test.dart | 108 ------------------ 2 files changed, 10 insertions(+), 130 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 0998a5bcf9..2586a170e7 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1204,13 +1204,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien // TextInputClient implementation: - // _lastFormattedUnmodifiedTextEditingValue tracks the last value - // that the formatter ran on and is used to prevent double-formatting. - TextEditingValue _lastFormattedUnmodifiedTextEditingValue; - // _receivedRemoteTextEditingValue is the direct value last passed in - // updateEditingValue. This value does not get updated with the formatted - // version. - TextEditingValue _receivedRemoteTextEditingValue; + TextEditingValue _lastKnownRemoteTextEditingValue; @override TextEditingValue get currentTextEditingValue => _value; @@ -1222,7 +1216,6 @@ class EditableTextState extends State with AutomaticKeepAliveClien if (widget.readOnly) { return; } - _receivedRemoteTextEditingValue = value; if (value.text != _value.text) { hideToolbar(); _showCaretOnScreen(); @@ -1231,7 +1224,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien _obscureLatestCharIndex = _value.selection.baseOffset; } } - + _lastKnownRemoteTextEditingValue = value; _formatAndSetValue(value); // To keep the cursor from blinking while typing, we want to restart the @@ -1258,7 +1251,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien break; default: // Finalize editing, but don't give up focus because this keyboard - // action does not imply the user is done inputting information. + // action does not imply the user is done inputting information. _finalizeEditing(false); break; } @@ -1358,8 +1351,9 @@ class EditableTextState extends State with AutomaticKeepAliveClien if (!_hasInputConnection) return; final TextEditingValue localValue = _value; - if (localValue == _receivedRemoteTextEditingValue) + if (localValue == _lastKnownRemoteTextEditingValue) return; + _lastKnownRemoteTextEditingValue = localValue; _textInputConnection.setEditingState(localValue); } @@ -1420,7 +1414,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien } if (!_hasInputConnection) { final TextEditingValue localValue = _value; - _lastFormattedUnmodifiedTextEditingValue = localValue; + _lastKnownRemoteTextEditingValue = localValue; _textInputConnection = TextInput.attach( this, TextInputConfiguration( @@ -1460,8 +1454,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien if (_hasInputConnection) { _textInputConnection.close(); _textInputConnection = null; - _lastFormattedUnmodifiedTextEditingValue = null; - _receivedRemoteTextEditingValue = null; + _lastKnownRemoteTextEditingValue = null; } } @@ -1479,8 +1472,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien if (_hasInputConnection) { _textInputConnection.connectionClosedReceived(); _textInputConnection = null; - _lastFormattedUnmodifiedTextEditingValue = null; - _receivedRemoteTextEditingValue = null; + _lastKnownRemoteTextEditingValue = null; _finalizeEditing(true); } } @@ -1624,21 +1616,17 @@ class EditableTextState extends State with AutomaticKeepAliveClien } void _formatAndSetValue(TextEditingValue value) { - // Check if the new value is the same as the current local value, or is the same - // as the post-formatting value of the previous pass. final bool textChanged = _value?.text != value?.text; - final bool isRepeat = value?.text == _lastFormattedUnmodifiedTextEditingValue?.text; - if (textChanged && !isRepeat && widget.inputFormatters != null && widget.inputFormatters.isNotEmpty) { + if (textChanged && widget.inputFormatters != null && widget.inputFormatters.isNotEmpty) { for (final TextInputFormatter formatter in widget.inputFormatters) value = formatter.formatEditUpdate(_value, value); _value = value; _updateRemoteEditingValueIfNeeded(); - } else if (!isRepeat || !textChanged) { + } else { _value = value; } if (textChanged && widget.onChanged != null) widget.onChanged(value.text); - _lastFormattedUnmodifiedTextEditingValue = _receivedRemoteTextEditingValue; } void _onCursorColorTick() { diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index c7a1298c30..439beb7a45 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -4178,114 +4178,6 @@ void main() { } expect(tester.testTextInput.editingState['text'], 'flutter is the best!...'); }); - - testWidgets('updateEditingValue filters multiple calls from formatter', (WidgetTester tester) async { - final MockTextFormatter formatter = MockTextFormatter(); - await tester.pumpWidget( - MediaQuery( - data: const MediaQueryData(devicePixelRatio: 1.0), - child: Directionality( - textDirection: TextDirection.ltr, - child: FocusScope( - node: focusScopeNode, - autofocus: true, - child: EditableText( - backgroundCursorColor: Colors.grey, - controller: controller, - focusNode: focusNode, - maxLines: 1, // Sets text keyboard implicitly. - style: textStyle, - cursorColor: cursorColor, - inputFormatters: [formatter], - ), - ), - ), - ), - ); - - await tester.tap(find.byType(EditableText)); - await tester.showKeyboard(find.byType(EditableText)); - controller.text = ''; - await tester.idle(); - - final EditableTextState state = - tester.state(find.byType(EditableText)); - expect(tester.testTextInput.editingState['text'], equals('')); - expect(state.wantKeepAlive, true); - - state.updateEditingValue(const TextEditingValue(text: '')); - state.updateEditingValue(const TextEditingValue(text: 'a')); - state.updateEditingValue(const TextEditingValue(text: 'aa')); - state.updateEditingValue(const TextEditingValue(text: 'aaa')); - state.updateEditingValue(const TextEditingValue(text: 'aa')); - state.updateEditingValue(const TextEditingValue(text: 'aaa')); - state.updateEditingValue(const TextEditingValue(text: 'aaaa')); - state.updateEditingValue(const TextEditingValue(text: 'aa')); - state.updateEditingValue(const TextEditingValue(text: 'aaaaaaa')); - state.updateEditingValue(const TextEditingValue(text: 'aa')); - state.updateEditingValue(const TextEditingValue(text: 'aaaaaaaaa')); - state.updateEditingValue(const TextEditingValue(text: 'aaaaaaaaa')); // Skipped - - const List referenceLog = [ - '[1]: , a', - '[1]: normal aa', - '[2]: aa, aaa', - '[2]: normal aaaa', - '[3]: aaaa, aa', - '[3]: deleting a', - '[4]: a, aaa', - '[4]: normal aaaaaaaa', - '[5]: aaaaaaaa, aaaa', - '[5]: deleting aaa', - '[6]: aaa, aa', - '[6]: deleting aaaa', - '[7]: aaaa, aaaaaaa', - '[7]: normal aaaaaaaaaaaaaa', - '[8]: aaaaaaaaaaaaaa, aa', - '[8]: deleting aaaaaa', - '[9]: aaaaaa, aaaaaaaaa', - '[9]: normal aaaaaaaaaaaaaaaaaa', - ]; - - expect(formatter.log, referenceLog); - }); -} - -class MockTextFormatter extends TextInputFormatter { - MockTextFormatter() : _counter = 0, log = []; - - int _counter; - List log; - - @override - TextEditingValue formatEditUpdate( - TextEditingValue oldValue, - TextEditingValue newValue, - ) { - _counter++; - log.add('[$_counter]: ${oldValue.text}, ${newValue.text}'); - TextEditingValue finalValue; - if (newValue.text.length < oldValue.text.length) { - finalValue = _handleTextDeletion(oldValue, newValue); - } else { - finalValue = _formatText(newValue); - } - return finalValue; - } - - - TextEditingValue _handleTextDeletion( - TextEditingValue oldValue, TextEditingValue newValue) { - final String result = 'a' * (_counter - 2); - log.add('[$_counter]: deleting $result'); - return TextEditingValue(text: result); - } - - TextEditingValue _formatText(TextEditingValue value) { - final String result = 'a' * _counter * 2; - log.add('[$_counter]: normal $result'); - return TextEditingValue(text: result); - } } class MockTextSelectionControls extends Mock implements TextSelectionControls {