diff --git a/lib/web_ui/lib/src/engine/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing.dart
index 116401de8c609..4049793ef457e 100644
--- a/lib/web_ui/lib/src/engine/text_editing.dart
+++ b/lib/web_ui/lib/src/engine/text_editing.dart
@@ -42,6 +42,14 @@ void _setStaticStyleAttributes(html.HtmlElement domElement) {
}
}
+enum _InputAction {
+ /// Procced to next enput element.
+ next,
+
+ /// Newline has been entered in current input element.
+ newline,
+}
+
/// The current text and selection state of a text field.
class EditingState {
EditingState({this.text, this.baseOffset = 0, this.extentOffset = 0});
@@ -162,6 +170,7 @@ class InputConfiguration {
}
typedef _OnChangeCallback = void Function(EditingState editingState);
+typedef _OnActionCallback = void Function(_InputAction inputAction);
enum ElementType {
/// The backing element is an ``.
@@ -225,6 +234,7 @@ class TextEditingElement {
html.HtmlElement domElement;
EditingState _lastEditingState;
_OnChangeCallback _onChange;
+ _OnActionCallback _onAction;
final List> _subscriptions =
>[];
@@ -268,12 +278,14 @@ class TextEditingElement {
void enable(
InputConfiguration inputConfig, {
@required _OnChangeCallback onChange,
+ @required _OnActionCallback onAction,
}) {
assert(!_enabled);
_initDomElement(inputConfig);
_enabled = true;
_onChange = onChange;
+ _onAction = onAction;
// Chrome on Android will hide the onscreen keyboard when you tap outside
// the text box. Instead, we want the framework to tell us to hide the
@@ -304,7 +316,8 @@ class TextEditingElement {
// Subscribe to text and selection changes.
_subscriptions
..add(html.document.onSelectionChange.listen(_handleChange))
- ..add(domElement.onInput.listen(_handleChange));
+ ..add(domElement.onInput.listen(_handleChange))
+ ..add(domElement.onKeyDown.listen(_handleKeyDown));
}
/// Disables the element so it's no longer used for text editing.
@@ -440,6 +453,27 @@ class TextEditingElement {
_onChange(_lastEditingState);
}
+ // Map KeyboardEvent to InputAction.
+ void _handleKeyDown(html.KeyboardEvent event) {
+ // Tab and enter key can only be mapped to actions for single line inputs.
+ if (_elementType == ElementType.input) {
+ // Trigger newline action if enter key was pressed.
+ if (event.which == 13) {
+ _onAction(_InputAction.newline);
+ }
+ // Trigger next action if tab key was pressed.
+ if (event.which == 9) {
+ _onAction(_InputAction.next);
+ // The default action of the browser is to focus the next element.
+ // As the browser is not able to focus flutter widgets, it will
+ // focus some part of its UI (e.g. the location bar).
+ // We prevent this action and leave it to the corresponding widget
+ // to change the focus.
+ event.preventDefault();
+ }
+ }
+ }
+
@visibleForTesting
EditingState calculateEditingState() {
assert(domElement != null);
@@ -700,6 +734,7 @@ class HybridTextEditing {
editingElement.enable(
InputConfiguration.fromFlutter(_configuration),
onChange: _syncEditingStateToFlutter,
+ onAction: _sendInputActionToFlutter,
);
}
@@ -790,6 +825,30 @@ class HybridTextEditing {
bool get doesKeyboardShiftInput =>
browserEngine == BrowserEngine.webkit &&
operatingSystem == OperatingSystem.iOs;
+ _InputActionToFlutter(_InputAction inputAction) {
+ switch (inputAction) {
+ case _InputAction.next:
+ return 'TextInputAction.next';
+ break;
+ case _InputAction.newline:
+ return 'TextInputAction.newline';
+ break;
+ }
+ return 'TextInputAction.unspecified';
+ }
+
+ void _sendInputActionToFlutter(_InputAction inputAction) {
+ ui.window.onPlatformMessage(
+ 'flutter/textinput',
+ const JSONMethodCodec().encodeMethodCall(
+ MethodCall('TextInputClient.performAction', [
+ _clientId,
+ _InputActionToFlutter(inputAction),
+ ]),
+ ),
+ _emptyCallback,
+ );
+ }
/// These style attributes are dynamic throughout the life time of an input
/// element.