TextInputClient currentTextEditingValue

Summary

#

Add a field, currentTextEditingValue, to the TextInputClient interface to get the current value of an editable text field from a platform client.

Context

#

The TextInputClient class is used by the Flutter framework to communicate with platform code about the current state of text input widgets like EditableText.

The platform side can lose its state when an Android app moves to the background. As of this change, the app can ask the framework for the last known state. In order to obtain this information, the TextEditingValue was surfaced for the TextInputClient.

Description of change

#

On some supported platforms, the application can be moved into the background where it is expected to consume fewer resources. For example, a backgrounded application on Android should avoid consuming unnecessary memory and has no need to retain references to views. Before this change, the Android-specific platform code could lose state information about editable text fields when the app moved back to the foreground. This is seen, for example, when text entered in a TextField widget is lost to the Java code, but is still remembered in the Dart code.

As of this change, the platform side now sends a textInput channel message called TextInput.requestExistingState. This notifies the Dart code that, when the app wakes up, it should re-establish any text input connections and notify the platform of its most recently known editing state.

The TextInput class interacts with client widgets using the TextInputClient interface. This interface previously provided no insight into the current value that a client had. To allow the TextInput class to appropriately respond to TextInput.requestExistingState, a new getter was added to TextInputClient called currentTextEditingValue. You cannot safely use the last value passed to TextInputConnection.setEditingState, since the client only calls that method under specific circumstances, such as when Dart code directly modifies the value of a TextEditingController in a way that does not directly mirror the platform's native handling of a response to a key input event. This is how a TextInputFormatter generally works, or what happens when Dart code directly sets TextEditingController.value.

Migration guide

#

If you previously implemented or extended TextEditingClient, you must now add the appropriate override for currentTextEditingValue.

This value may be null.

If you want to migrate before this change lands, you can add a class to your class similar to the following:

dart
abstract class _TemporaryTextEditingClient {
  TextEditingValue get currentTextEditingValue;
}

This allows you to add the new member with an @override annotation before the change lands in the framework. Later, you can remove the temporary interface definition.

Code before migration:

dart
class _MyCustomTextWidgetState extends State<MyCustomWidget> implements TextEditingClient {
  ...

  @override
  void updateEditingValue(TextEditingValue value) {
    ...
  }

  @override
  void performAction(TextInputAction action) {
    ...
  }

  @override
  void updateFloatingCursor(RawFloatingCursorPoint point) {
    ...
  }
}

Code after migration:

dart
class _MyCustomTextWidgetState extends State<MyCustomWidget> implements TextEditingClient {
  ...

  @override
  TextEditingValue get currentTextEditingValue => widget.textEditingController.value;

  @override
  void updateEditingValue(TextEditingValue value) {
    ...
  }

  @override
  void performAction(TextInputAction action) {
    ...
  }

  @override
  void updateFloatingCursor(RawFloatingCursorPoint point) {
    ...
  }
}

Timeline

#

Landed in version: 1.16.3
In stable release: 1.17

References

#

API documentation:

Relevant issue:

Relevant PR: