@@ -7,6 +7,9 @@ part of engine;
77/// Make the content editable span visible to facilitate debugging.
88const bool _debugVisibleTextEditing = false ;
99
10+ /// The `keyCode` of the "Enter" key.
11+ const int _kReturnKeyCode = 13 ;
12+
1013void _emptyCallback (dynamic _) {}
1114
1215/// These style attributes are constant throughout the life time of an input
@@ -171,23 +174,29 @@ class EditingState {
171174/// This corresponds to Flutter's [TextInputConfiguration] .
172175class InputConfiguration {
173176 InputConfiguration ({
174- this .inputType,
175- this .obscureText = false ,
177+ @required this .inputType,
178+ @required this .inputAction,
179+ @required this .obscureText,
176180 });
177181
178182 InputConfiguration .fromFlutter (Map <String , dynamic > flutterInputConfiguration)
179183 : inputType = EngineInputType .fromName (
180184 flutterInputConfiguration['inputType' ]['name' ]),
185+ inputAction = flutterInputConfiguration['inputAction' ],
181186 obscureText = flutterInputConfiguration['obscureText' ];
182187
183188 /// The type of information being edited in the input control.
184189 final EngineInputType inputType;
185190
191+ /// The default action for the input field.
192+ final String inputAction;
193+
186194 /// Whether to hide the text being edited.
187195 final bool obscureText;
188196}
189197
190198typedef _OnChangeCallback = void Function (EditingState editingState);
199+ typedef _OnActionCallback = void Function (String inputAction);
191200
192201/// Wraps the DOM element used to provide text editing capabilities.
193202///
@@ -219,11 +228,16 @@ class TextEditingElement {
219228 const Duration (milliseconds: 100 );
220229
221230 final HybridTextEditing owner;
222- bool _enabled = false ;
231+
232+ @visibleForTesting
233+ bool isEnabled = false ;
223234
224235 html.HtmlElement domElement;
236+ InputConfiguration _inputConfiguration;
225237 EditingState _lastEditingState;
238+
226239 _OnChangeCallback _onChange;
240+ _OnActionCallback _onAction;
227241
228242 final List <StreamSubscription <html.Event >> _subscriptions =
229243 < StreamSubscription <html.Event >> [];
@@ -261,12 +275,15 @@ class TextEditingElement {
261275 void enable (
262276 InputConfiguration inputConfig, {
263277 @required _OnChangeCallback onChange,
278+ @required _OnActionCallback onAction,
264279 }) {
265- assert (! _enabled );
280+ assert (! isEnabled );
266281
267282 _initDomElement (inputConfig);
268- _enabled = true ;
283+ isEnabled = true ;
284+ _inputConfiguration = inputConfig;
269285 _onChange = onChange;
286+ _onAction = onAction;
270287
271288 // Chrome on Android will hide the onscreen keyboard when you tap outside
272289 // the text box. Instead, we want the framework to tell us to hide the
@@ -279,7 +296,7 @@ class TextEditingElement {
279296 if (browserEngine == BrowserEngine .blink ||
280297 browserEngine == BrowserEngine .unknown) {
281298 _subscriptions.add (domElement.onBlur.listen ((_) {
282- if (_enabled ) {
299+ if (isEnabled ) {
283300 _refocus ();
284301 }
285302 }));
@@ -297,6 +314,8 @@ class TextEditingElement {
297314 // Subscribe to text and selection changes.
298315 _subscriptions.add (domElement.onInput.listen (_handleChange));
299316
317+ _subscriptions.add (domElement.onKeyDown.listen (_maybeSendAction));
318+
300319 /// Detects changes in text selection.
301320 ///
302321 /// Currently only used in Firefox.
@@ -330,9 +349,9 @@ class TextEditingElement {
330349 ///
331350 /// Calling [disable] also removes any registered event listeners.
332351 void disable () {
333- assert (_enabled );
352+ assert (isEnabled );
334353
335- _enabled = false ;
354+ isEnabled = false ;
336355 _lastEditingState = null ;
337356
338357 for (int i = 0 ; i < _subscriptions.length; i++ ) {
@@ -390,7 +409,7 @@ class TextEditingElement {
390409
391410 void setEditingState (EditingState editingState) {
392411 _lastEditingState = editingState;
393- if (! _enabled || ! editingState.isValid) {
412+ if (! isEnabled || ! editingState.isValid) {
394413 return ;
395414 }
396415
@@ -416,6 +435,13 @@ class TextEditingElement {
416435 _onChange (_lastEditingState);
417436 }
418437 }
438+
439+ void _maybeSendAction (html.KeyboardEvent event) {
440+ if (event.keyCode == _kReturnKeyCode) {
441+ event.preventDefault ();
442+ _onAction (_inputConfiguration.inputAction);
443+ }
444+ }
419445}
420446
421447/// The implementation of a persistent mode for [TextEditingElement] .
@@ -512,7 +538,7 @@ class HybridTextEditing {
512538 ///
513539 /// Use [stopUsingCustomEditableElement] to switch back to default element.
514540 void useCustomEditableElement (TextEditingElement customEditingElement) {
515- if (_isEditing && customEditingElement != _customEditingElement) {
541+ if (isEditing && customEditingElement != _customEditingElement) {
516542 stopEditing ();
517543 }
518544 _customEditingElement = customEditingElement;
@@ -529,20 +555,21 @@ class HybridTextEditing {
529555 /// Flag which shows if there is an ongoing editing.
530556 ///
531557 /// Also used to define if a keyboard is needed.
532- bool _isEditing = false ;
558+ @visibleForTesting
559+ bool isEditing = false ;
533560
534561 /// Indicates whether the input element needs to be positioned.
535562 ///
536563 /// See [TextEditingElement._delayBeforePositioning] .
537564 bool get inputElementNeedsToBePositioned =>
538- ! inputPositioned && _isEditing && doesKeyboardShiftInput;
565+ ! inputPositioned && isEditing && doesKeyboardShiftInput;
539566
540567 /// Flag indicating whether the input element's position is set.
541568 ///
542569 /// See [inputElementNeedsToBePositioned] .
543570 bool inputPositioned = false ;
544571
545- Map < String , dynamic > _configuration;
572+ InputConfiguration _configuration;
546573
547574 /// All "flutter/textinput" platform messages should be sent to this method.
548575 void handleTextInput (ByteData data) {
@@ -551,11 +578,11 @@ class HybridTextEditing {
551578 case 'TextInput.setClient' :
552579 final bool clientIdChanged =
553580 _clientId != null && _clientId != call.arguments[0 ];
554- if (clientIdChanged && _isEditing ) {
581+ if (clientIdChanged && isEditing ) {
555582 stopEditing ();
556583 }
557584 _clientId = call.arguments[0 ];
558- _configuration = call.arguments[1 ];
585+ _configuration = InputConfiguration . fromFlutter ( call.arguments[1 ]) ;
559586 break ;
560587
561588 case 'TextInput.setEditingState' :
@@ -564,7 +591,7 @@ class HybridTextEditing {
564591 break ;
565592
566593 case 'TextInput.show' :
567- if (! _isEditing ) {
594+ if (! isEditing ) {
568595 _startEditing ();
569596 }
570597 break ;
@@ -579,25 +606,26 @@ class HybridTextEditing {
579606
580607 case 'TextInput.clearClient' :
581608 case 'TextInput.hide' :
582- if (_isEditing ) {
609+ if (isEditing ) {
583610 stopEditing ();
584611 }
585612 break ;
586613 }
587614 }
588615
589616 void _startEditing () {
590- assert (! _isEditing );
591- _isEditing = true ;
617+ assert (! isEditing );
618+ isEditing = true ;
592619 editingElement.enable (
593- InputConfiguration . fromFlutter ( _configuration) ,
620+ _configuration,
594621 onChange: _syncEditingStateToFlutter,
622+ onAction: _sendInputActionToFlutter,
595623 );
596624 }
597625
598626 void stopEditing () {
599- assert (_isEditing );
600- _isEditing = false ;
627+ assert (isEditing );
628+ isEditing = false ;
601629 editingElement.disable ();
602630 }
603631
@@ -666,6 +694,19 @@ class HybridTextEditing {
666694 );
667695 }
668696
697+ void _sendInputActionToFlutter (String inputAction) {
698+ ui.window.onPlatformMessage (
699+ 'flutter/textinput' ,
700+ const JSONMethodCodec ().encodeMethodCall (
701+ MethodCall (
702+ 'TextInputClient.performAction' ,
703+ < dynamic > [_clientId, inputAction],
704+ ),
705+ ),
706+ _emptyCallback,
707+ );
708+ }
709+
669710 /// Positioning of input element is only done if we are not expecting input
670711 /// to be shifted by a virtual keyboard or if the input is already positioned.
671712 ///
0 commit comments