TextField should update IME when controller changes (#9261)
Fixes #9246
This commit is contained in:
parent
249bbac362
commit
470db7d96e
@ -239,9 +239,8 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
if (config.controller != oldConfig.controller) {
|
||||
oldConfig.controller.removeListener(_didChangeTextEditingValue);
|
||||
config.controller.addListener(_didChangeTextEditingValue);
|
||||
if (_hasInputConnection && config.controller.value != oldConfig.controller.value)
|
||||
_textInputConnection.setEditingState(config.controller.value);
|
||||
}
|
||||
_updateRemoteEditingValueIfNeeded();
|
||||
}
|
||||
if (config.focusNode != oldConfig.focusNode) {
|
||||
oldConfig.focusNode.removeListener(_handleFocusChanged);
|
||||
config.focusNode.addListener(_handleFocusChanged);
|
||||
@ -263,10 +262,13 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
|
||||
// TextInputClient implementation:
|
||||
|
||||
TextEditingValue _lastKnownRemoteTextEditingValue;
|
||||
|
||||
@override
|
||||
void updateEditingValue(TextEditingValue value) {
|
||||
if (value.text != _value.text)
|
||||
_hideSelectionOverlayIfNeeded();
|
||||
_lastKnownRemoteTextEditingValue = value;
|
||||
_value = value;
|
||||
if (config.onChanged != null)
|
||||
config.onChanged(value.text);
|
||||
@ -280,6 +282,16 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
config.onSubmitted(_value.text);
|
||||
}
|
||||
|
||||
void _updateRemoteEditingValueIfNeeded() {
|
||||
if (!_hasInputConnection)
|
||||
return;
|
||||
final TextEditingValue localValue = _value;
|
||||
if (localValue == _lastKnownRemoteTextEditingValue)
|
||||
return;
|
||||
_lastKnownRemoteTextEditingValue = localValue;
|
||||
_textInputConnection.setEditingState(localValue);
|
||||
}
|
||||
|
||||
TextEditingValue get _value => config.controller.value;
|
||||
set _value(TextEditingValue value) {
|
||||
config.controller.value = value;
|
||||
@ -306,8 +318,10 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
|
||||
void _openInputConnectionIfNeeded() {
|
||||
if (!_hasInputConnection) {
|
||||
final TextEditingValue localValue = _value;
|
||||
_lastKnownRemoteTextEditingValue = localValue;
|
||||
_textInputConnection = TextInput.attach(this, new TextInputConfiguration(inputType: config.keyboardType))
|
||||
..setEditingState(_value)
|
||||
..setEditingState(localValue)
|
||||
..show();
|
||||
}
|
||||
}
|
||||
@ -316,6 +330,7 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
if (_hasInputConnection) {
|
||||
_textInputConnection.close();
|
||||
_textInputConnection = null;
|
||||
_lastKnownRemoteTextEditingValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,6 +444,7 @@ class EditableTextState extends State<EditableText> implements TextInputClient {
|
||||
}
|
||||
|
||||
void _didChangeTextEditingValue() {
|
||||
_updateRemoteEditingValueIfNeeded();
|
||||
_startOrStopCursorTimerIfNeeded();
|
||||
_updateOrDisposeSelectionOverlayIfNeeded();
|
||||
// TODO(abarth): Teach RenderEditable about ValueNotifier<TextEditingValue>
|
||||
|
@ -886,4 +886,59 @@ void main() {
|
||||
|
||||
expect(topLeft.x, equals(399.0));
|
||||
});
|
||||
|
||||
testWidgets('Controller can update server', (WidgetTester tester) async {
|
||||
final TextEditingController controller = new TextEditingController(
|
||||
text: 'Initial Text',
|
||||
);
|
||||
final TextEditingController controller2 = new TextEditingController(
|
||||
text: 'More Text',
|
||||
);
|
||||
|
||||
TextEditingController currentController = controller;
|
||||
StateSetter setState;
|
||||
|
||||
await tester.pumpWidget(
|
||||
overlay(new Center(
|
||||
child: new Material(
|
||||
child: new StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setter) {
|
||||
setState = setter;
|
||||
return new TextField(controller: currentController);
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], isEmpty);
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], equals('Initial Text'));
|
||||
|
||||
controller.text = 'Updated Text';
|
||||
await tester.idle();
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], equals('Updated Text'));
|
||||
|
||||
setState(() {
|
||||
currentController = controller2;
|
||||
});
|
||||
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], equals('More Text'));
|
||||
|
||||
controller.text = 'Ignored Text';
|
||||
await tester.idle();
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], equals('More Text'));
|
||||
|
||||
controller2.text = 'Final Text';
|
||||
await tester.idle();
|
||||
|
||||
expect(tester.testTextInput.editingState['text'], equals('Final Text'));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user