Unverified Commit b236465b authored by Chris Bracken's avatar Chris Bracken Committed by GitHub

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 83d4d63a.
parent 26619b3c
......@@ -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() {
......
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment