Unverified Commit cbe0999d authored by guolinaileen's avatar guolinaileen Committed by GitHub

Introduce onAppPrivateCommand to TextField (#62712)

parent 8c795e3e
......@@ -351,6 +351,7 @@ class TextField extends StatefulWidget {
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.onAppPrivateCommand,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
......@@ -632,6 +633,9 @@ class TextField extends StatefulWidget {
/// [TextInputAction.previous] for [textInputAction].
final ValueChanged<String> onSubmitted;
/// {@macro flutter.widgets.editableText.onAppPrivateCommand}
final AppPrivateCommandCallback onAppPrivateCommand;
/// {@macro flutter.widgets.editableText.inputFormatters}
final List<TextInputFormatter> inputFormatters;
......@@ -1147,6 +1151,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
onSelectionChanged: _handleSelectionChanged,
onEditingComplete: widget.onEditingComplete,
onSubmitted: widget.onSubmitted,
onAppPrivateCommand: widget.onAppPrivateCommand,
onSelectionHandleTapped: _handleSelectionHandleTapped,
inputFormatters: formatters,
rendererIgnoresPointer: true,
......
......@@ -802,6 +802,9 @@ abstract class TextInputClient {
/// Requests that this client perform the given action.
void performAction(TextInputAction action);
/// Requests that this client perform the private command.
void performPrivateCommand(String action, Map<String, dynamic> data);
/// Updates the floating cursor position and state.
void updateFloatingCursor(RawFloatingCursorPoint point);
......@@ -1157,6 +1160,10 @@ class TextInput {
case 'TextInputClient.performAction':
_currentConnection!._client.performAction(_toTextInputAction(args[1] as String));
break;
case 'TextInputClient.performPrivateCommand':
_currentConnection!._client.performPrivateCommand(
args[1]['action'] as String, args[1]['data'] as Map<String, dynamic>);
break;
case 'TextInputClient.updateFloatingCursor':
_currentConnection!._client.updateFloatingCursor(_toTextPoint(
_toTextCursorAction(args[1] as String),
......
......@@ -40,6 +40,9 @@ export 'package:flutter/services.dart' show TextEditingValue, TextSelection, Tex
/// (including the cursor location).
typedef SelectionChangedCallback = void Function(TextSelection selection, SelectionChangedCause cause);
/// Signature for the callback that reports the app private command results.
typedef AppPrivateCommandCallback = void Function(String, Map<String, dynamic>);
// The time it takes for the cursor to fade from fully opaque to fully
// transparent and vice versa. A full cursor blink, from transparent to opaque
// to transparent, is twice this duration.
......@@ -413,6 +416,7 @@ class EditableText extends StatefulWidget {
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.onAppPrivateCommand,
this.onSelectionChanged,
this.onSelectionHandleTapped,
List<TextInputFormatter> inputFormatters,
......@@ -1030,6 +1034,11 @@ class EditableText extends StatefulWidget {
/// {@end-tool}
final ValueChanged<String> onSubmitted;
/// {@template flutter.widgets.editableText.onAppPrivateCommand}
/// Called when the result of an app private command is received.
/// {@endtemplate}
final AppPrivateCommandCallback onAppPrivateCommand;
/// Called when the user changes the selection of text (including the cursor
/// location).
final SelectionChangedCallback onSelectionChanged;
......@@ -1616,6 +1625,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
@override
void performPrivateCommand(String action, Map<String, dynamic> data) {
widget.onAppPrivateCommand(action, data);
}
// The original position of the caret on FloatingCursorDragState.start.
Rect _startCaretRect;
......
......@@ -148,6 +148,11 @@ class FakeAutofillClient implements TextInputClient, AutofillClient {
latestMethodCall = 'performAction';
}
@override
void performPrivateCommand(String action, Map<String, dynamic> data) {
latestMethodCall = 'performPrivateCommand';
}
@override
void updateFloatingCursor(RawFloatingCursorPoint point) {
latestMethodCall = 'updateFloatingCursor';
......
......@@ -5,6 +5,7 @@
// @dart = 2.8
import 'dart:convert' show utf8;
import 'dart:convert' show jsonDecode;
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart' show TestWidgetsFlutterBinding;
......@@ -170,7 +171,7 @@ void main() {
expect(client.latestMethodCall, 'connectionClosed');
});
test('TextInputClient showAutocorrectionPromptRect method is called', () async {
test('TextInputClient performPrivateCommand method is called', () async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
......@@ -178,8 +179,149 @@ void main() {
expect(client.latestMethodCall, isEmpty);
// Send onConnectionClosed message.
// Send performPrivateCommand message.
final ByteData messageBytes = const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[
1,
jsonDecode(
'{"action": "actionCommand", "data": {\"input_context\" : \"abcdefg\"}}')
],
'method': 'TextInputClient.performPrivateCommand',
});
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'flutter/textinput',
messageBytes,
(ByteData _) {},
);
expect(client.latestMethodCall, 'performPrivateCommand');
});
test('TextInputClient performPrivateCommand method is called with float',
() async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
TextInput.attach(client, configuration);
expect(client.latestMethodCall, isEmpty);
// Send performPrivateCommand message.
final ByteData messageBytes = const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[
1,
jsonDecode(
'{"action": "actionCommand", "data": {\"input_context\" : 0.5}}')
],
'method': 'TextInputClient.performPrivateCommand',
});
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'flutter/textinput',
messageBytes,
(ByteData _) {},
);
expect(client.latestMethodCall, 'performPrivateCommand');
});
test(
'TextInputClient performPrivateCommand method is called with CharSequence array',
() async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
TextInput.attach(client, configuration);
expect(client.latestMethodCall, isEmpty);
// Send performPrivateCommand message.
final ByteData messageBytes = const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[
1,
jsonDecode(
'{"action": "actionCommand", "data": {\"input_context\" : ["abc", "efg"]}}')
],
'method': 'TextInputClient.performPrivateCommand',
});
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'flutter/textinput',
messageBytes,
(ByteData _) {},
);
expect(client.latestMethodCall, 'performPrivateCommand');
});
test(
'TextInputClient performPrivateCommand method is called with CharSequence',
() async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
TextInput.attach(client, configuration);
expect(client.latestMethodCall, isEmpty);
// Send performPrivateCommand message.
final ByteData messageBytes =
const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[
1,
jsonDecode(
'{"action": "actionCommand", "data": {\"input_context\" : "abc"}}')
],
'method': 'TextInputClient.performPrivateCommand',
});
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'flutter/textinput',
messageBytes,
(ByteData _) {},
);
expect(client.latestMethodCall, 'performPrivateCommand');
});
test(
'TextInputClient performPrivateCommand method is called with float array',
() async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
TextInput.attach(client, configuration);
expect(client.latestMethodCall, isEmpty);
// Send performPrivateCommand message.
final ByteData messageBytes =
const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[
1,
jsonDecode(
'{"action": "actionCommand", "data": {\"input_context\" : [0.5, 0.8]}}')
],
'method': 'TextInputClient.performPrivateCommand',
});
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'flutter/textinput',
messageBytes,
(ByteData _) {},
);
expect(client.latestMethodCall, 'performPrivateCommand');
});
test('TextInputClient showAutocorrectionPromptRect method is called',
() async {
// Assemble a TextInputConnection so we can verify its change in state.
final FakeTextInputClient client = FakeTextInputClient(null);
const TextInputConfiguration configuration = TextInputConfiguration();
TextInput.attach(client, configuration);
expect(client.latestMethodCall, isEmpty);
// Send onConnectionClosed message.
final ByteData messageBytes =
const JSONMessageCodec().encodeMessage(<String, dynamic>{
'args': <dynamic>[1, 0, 1],
'method': 'TextInputClient.showAutocorrectionPromptRect',
});
......@@ -237,6 +379,11 @@ class FakeTextInputClient implements TextInputClient {
latestMethodCall = 'performAction';
}
@override
void performPrivateCommand(String action, Map<String, dynamic> data) {
latestMethodCall = 'performPrivateCommand';
}
@override
void updateEditingValue(TextEditingValue value) {
latestMethodCall = 'updateEditingValue';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment