Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/word_break_properties.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/word_breaker.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_action.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_type.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart
Expand Down
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export 'engine/text/word_break_properties.dart';
export 'engine/text/word_breaker.dart';
export 'engine/text_editing/autofill_hint.dart';
export 'engine/text_editing/composition_aware_mixin.dart';
export 'engine/text_editing/input_action.dart';
export 'engine/text_editing/input_type.dart';
export 'engine/text_editing/text_capitalization.dart';
export 'engine/text_editing/text_editing.dart';
Expand Down
155 changes: 155 additions & 0 deletions lib/web_ui/lib/src/engine/text_editing/input_action.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../browser_detection.dart';
import '../dom.dart';

/// Various input action types used in text fields.
///
/// These types are coming from Flutter's [TextInputAction]. Currently, the web doesn't
/// support all the types. We fallback to [EngineInputAction.none] when Flutter
/// sends a type that isn't supported.
abstract class EngineInputAction {
const EngineInputAction();

static EngineInputAction fromName(String name) {
switch (name) {
case 'TextInputAction.continueAction':
case 'TextInputAction.next':
return next;
case 'TextInputAction.previous':
return previous;
case 'TextInputAction.done':
return done;
case 'TextInputAction.go':
return go;
case 'TextInputAction.newline':
return enter;
case 'TextInputAction.search':
return search;
case 'TextInputAction.send':
return send;
case 'TextInputAction.emergencyCall':
case 'TextInputAction.join':
case 'TextInputAction.none':
case 'TextInputAction.route':
case 'TextInputAction.unspecified':
default:
return none;
}
}

/// No input action
static const NoInputAction none = NoInputAction();

/// Action to go to next
static const NextInputAction next = NextInputAction();

/// Action to go to previous
static const PreviousInputAction previous = PreviousInputAction();

/// Action to be finished
static const DoneInputAction done = DoneInputAction();

/// Action to Go
static const GoInputAction go = GoInputAction();

/// Action to insert newline
static const EnterInputAction enter = EnterInputAction();

/// Action to search
static const SearchInputAction search = SearchInputAction();

/// Action to send
static const SendInputAction send = SendInputAction();


/// The HTML `enterkeyhint` attribute to be set on the DOM element.
///
/// This HTML attribute helps the browser decide what kind of keyboard action
/// to use for this text field
///
/// For various `enterkeyhint` values supported by browsers, see:
/// <https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint>.
String? get enterkeyhintAttribute;

/// Given a [domElement], set attributes that are specific to this input action.
void configureInputAction(DomHTMLElement domElement) {
if (enterkeyhintAttribute == null) {
return;
}

// Only apply `enterkeyhint` in mobile browsers so that the right virtual
// keyboard shows up.
if (operatingSystem == OperatingSystem.iOs ||
operatingSystem == OperatingSystem.android ||
enterkeyhintAttribute == EngineInputAction.none.enterkeyhintAttribute) {
domElement.setAttribute('enterkeyhint', enterkeyhintAttribute!);
}
}
}

/// No action specified
class NoInputAction extends EngineInputAction {
const NoInputAction();

@override
String? get enterkeyhintAttribute => null;
}

/// Typically inserting a new line.
class EnterInputAction extends EngineInputAction {
const EnterInputAction();

@override
String? get enterkeyhintAttribute => 'enter';
}

/// Typically meaning there is nothing more to input and the input method editor (IME) will be closed.
class DoneInputAction extends EngineInputAction {
const DoneInputAction();

@override
String? get enterkeyhintAttribute => 'done';
}

/// Typically meaning to take the user to the target of the text they typed.
class GoInputAction extends EngineInputAction {
const GoInputAction();

@override
String? get enterkeyhintAttribute => 'go';
}

/// Typically taking the user to the next field that will accept text.
class NextInputAction extends EngineInputAction {
const NextInputAction();

@override
String? get enterkeyhintAttribute => 'next';
}

/// Typically taking the user to the previous field that will accept text.
class PreviousInputAction extends EngineInputAction {
const PreviousInputAction();

@override
String? get enterkeyhintAttribute => 'previous';
}

/// Typically taking the user to the results of searching for the text they have typed.
class SearchInputAction extends EngineInputAction {
const SearchInputAction();

@override
String? get enterkeyhintAttribute => 'search';
}

/// Typically delivering the text to its target.
class SendInputAction extends EngineInputAction {
const SendInputAction();

@override
String? get enterkeyhintAttribute => 'send';
}
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/text_editing/text_editing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import '../text/paragraph.dart';
import '../util.dart';
import 'autofill_hint.dart';
import 'composition_aware_mixin.dart';
import 'input_action.dart';
import 'input_type.dart';
import 'text_capitalization.dart';

Expand Down Expand Up @@ -1181,6 +1182,9 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
activeDomElement.setAttribute('inputmode', 'none');
}

final EngineInputAction action = EngineInputAction.fromName(config.inputAction);
action.configureInputAction(activeDomElement);

final AutofillInfo? autofill = config.autofill;
if (autofill != null) {
autofill.applyToDomElement(activeDomElement, focusedElement: true);
Expand Down
21 changes: 21 additions & 0 deletions lib/web_ui/test/text_editing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,27 @@ Future<void> testMain() async {
editingStrategy!.disable();
});

test('Knows how to create non-default text actions', () {
final InputConfiguration config = InputConfiguration(
inputAction: 'TextInputAction.send'
);
editingStrategy!.enable(
config,
onChange: trackEditingState,
onAction: trackInputAction,
);
expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1));
final DomElement input = defaultTextEditingRoot.querySelector('input')!;
expect(editingStrategy!.domElement, input);
if (operatingSystem == OperatingSystem.iOs || operatingSystem == OperatingSystem.android){
expect(input.getAttribute('enterkeyhint'), 'send');
} else {
expect(input.getAttribute('enterkeyhint'), null);
}

editingStrategy!.disable();
});

test('Knows to turn autocorrect off', () {
final InputConfiguration config = InputConfiguration(
autocorrect: false,
Expand Down