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.
This commit is contained in:
Chris Bracken 2020-02-03 20:40:10 -08:00 committed by GitHub
parent 26619b3c35
commit b236465bed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 10 additions and 130 deletions

View File

@ -1204,13 +1204,7 @@ class EditableTextState extends State<EditableText> 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<EditableText> with AutomaticKeepAliveClien
if (widget.readOnly) {
return;
}
_receivedRemoteTextEditingValue = value;
if (value.text != _value.text) {
hideToolbar();
_showCaretOnScreen();
@ -1231,7 +1224,7 @@ class EditableTextState extends State<EditableText> 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<EditableText> 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<EditableText> 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<EditableText> 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<EditableText> with AutomaticKeepAliveClien
if (_hasInputConnection) {
_textInputConnection.close();
_textInputConnection = null;
_lastFormattedUnmodifiedTextEditingValue = null;
_receivedRemoteTextEditingValue = null;
_lastKnownRemoteTextEditingValue = null;
}
}
@ -1479,8 +1472,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (_hasInputConnection) {
_textInputConnection.connectionClosedReceived();
_textInputConnection = null;
_lastFormattedUnmodifiedTextEditingValue = null;
_receivedRemoteTextEditingValue = null;
_lastKnownRemoteTextEditingValue = null;
_finalizeEditing(true);
}
}
@ -1624,21 +1616,17 @@ class EditableTextState extends State<EditableText> 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() {

View File

@ -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: <TextInputFormatter>[formatter],
),
),
),
),
);
await tester.tap(find.byType(EditableText));
await tester.showKeyboard(find.byType(EditableText));
controller.text = '';
await tester.idle();
final EditableTextState state =
tester.state<EditableTextState>(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<String> referenceLog = <String>[
'[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 = <String>[];
int _counter;
List<String> 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 {