diff --git a/AUTHORS b/AUTHORS index ef0b8bee03..fd9c0c9ce1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -77,7 +77,6 @@ Hidenori Matsubayashi Perqin Xie Seongyun Kim Ludwik Trammer -J-P Nurmi Marian Triebe Alexis Rouillard Mirko Mucaria diff --git a/examples/api/lib/services/text_input/text_input_control.0.dart b/examples/api/lib/services/text_input/text_input_control.0.dart deleted file mode 100644 index 112ba35ba8..0000000000 --- a/examples/api/lib/services/text_input/text_input_control.0.dart +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2014 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. - -// Flutter code sample for TextInputControl - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(const MyApp()); - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - home: MyStatefulWidget(), - ); - } -} - -class MyStatefulWidget extends StatefulWidget { - const MyStatefulWidget({super.key}); - - @override - MyStatefulWidgetState createState() => MyStatefulWidgetState(); -} - -class MyStatefulWidgetState extends State { - final TextEditingController _controller = TextEditingController(); - final FocusNode _focusNode = FocusNode(); - - @override - void dispose() { - super.dispose(); - _controller.dispose(); - _focusNode.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: TextField( - autofocus: true, - controller: _controller, - focusNode: _focusNode, - decoration: InputDecoration( - suffix: IconButton( - icon: const Icon(Icons.clear), - tooltip: 'Clear and unfocus', - onPressed: () { - _controller.clear(); - _focusNode.unfocus(); - }, - ), - ), - ), - ), - bottomSheet: const MyVirtualKeyboard(), - ); - } -} - -class MyVirtualKeyboard extends StatefulWidget { - const MyVirtualKeyboard({super.key}); - - @override - MyVirtualKeyboardState createState() => MyVirtualKeyboardState(); -} - -class MyVirtualKeyboardState extends State { - final MyTextInputControl _inputControl = MyTextInputControl(); - - @override - void initState() { - super.initState(); - _inputControl.register(); - } - - @override - void dispose() { - super.dispose(); - _inputControl.unregister(); - } - - void _handleKeyPress(String key) { - _inputControl.processUserInput(key); - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _inputControl.visible, - builder: (_, bool visible, __) { - return Visibility( - visible: visible, - child: FocusScope( - canRequestFocus: false, - child: TextFieldTapRegion( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - for (final String key in ['A', 'B', 'C']) - ElevatedButton( - child: Text(key), - onPressed: () => _handleKeyPress(key), - ), - ], - ), - ), - ), - ); - }, - ); - } -} - -class MyTextInputControl with TextInputControl { - TextEditingValue _editingState = TextEditingValue.empty; - final ValueNotifier _visible = ValueNotifier(false); - - /// The input control's visibility state for updating the visual presentation. - ValueListenable get visible => _visible; - - /// Register the input control. - void register() => TextInput.setInputControl(this); - - /// Restore the original platform input control. - void unregister() => TextInput.restorePlatformInputControl(); - - @override - void show() => _visible.value = true; - - @override - void hide() => _visible.value = false; - - @override - void setEditingState(TextEditingValue value) => _editingState = value; - - /// Process user input. - /// - /// Updates the internal editing state by inserting the input text, - /// and by replacing the current selection if any. - void processUserInput(String input) { - _editingState = _editingState.copyWith( - text: _insertText(input), - selection: _replaceSelection(input), - ); - - // Request the attached client to update accordingly. - TextInput.updateEditingValue(_editingState); - } - - String _insertText(String input) { - final String text = _editingState.text; - final TextSelection selection = _editingState.selection; - return text.replaceRange(selection.start, selection.end, input); - } - - TextSelection _replaceSelection(String input) { - final TextSelection selection = _editingState.selection; - return TextSelection.collapsed(offset: selection.start + input.length); - } -} diff --git a/examples/api/test/services/text_input/text_input_control.0_test.dart b/examples/api/test/services/text_input/text_input_control.0_test.dart deleted file mode 100644 index da933c8404..0000000000 --- a/examples/api/test/services/text_input/text_input_control.0_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 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 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_api_samples/services/text_input/text_input_control.0.dart' - as example; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Enter text using the VKB', (WidgetTester tester) async { - await tester.pumpWidget(const example.MyApp()); - await tester.pumpAndSettle(); - - await tester.tap(find.descendant( - of: find.byType(example.MyVirtualKeyboard), - matching: find.widgetWithText(ElevatedButton, 'A'), - )); - await tester.pumpAndSettle(); - expect(find.widgetWithText(TextField, 'A'), findsOneWidget); - - await tester.tap(find.descendant( - of: find.byType(example.MyVirtualKeyboard), - matching: find.widgetWithText(ElevatedButton, 'B'), - )); - await tester.pumpAndSettle(); - expect(find.widgetWithText(TextField, 'AB'), findsOneWidget); - - await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); - await tester.pumpAndSettle(); - - await tester.tap(find.descendant( - of: find.byType(example.MyVirtualKeyboard), - matching: find.widgetWithText(ElevatedButton, 'C'), - )); - await tester.pumpAndSettle(); - expect(find.widgetWithText(TextField, 'ACB'), findsOneWidget); - }); -} diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart index b2ccef4320..6f2a2614c4 100644 --- a/packages/flutter/lib/src/services/text_input.dart +++ b/packages/flutter/lib/src/services/text_input.dart @@ -1150,18 +1150,6 @@ mixin TextInputClient { /// [TextInputClient] should cleanup its connection and finalize editing. void connectionClosed(); - /// The framework calls this method to notify that the text input control has - /// been changed. - /// - /// The [TextInputClient] may switch to the new text input control by hiding - /// the old and showing the new input control. - /// - /// See also: - /// - /// * [TextInputControl.hide], a method to hide the old input control. - /// * [TextInputControl.show], a method to show the new input control. - void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {} - /// Requests that the client show the editing toolbar, for example when the /// platform changes the selection through a non-flutter method such as /// scribble. @@ -1374,7 +1362,13 @@ class TextInputConnection { if (editableBoxSize != _cachedSize || transform != _cachedTransform) { _cachedSize = editableBoxSize; _cachedTransform = transform; - TextInput._instance._setEditableSizeAndTransform(editableBoxSize, transform); + TextInput._instance._setEditableSizeAndTransform( + { + 'width': editableBoxSize.width, + 'height': editableBoxSize.height, + 'transform': transform.storage, + }, + ); } } @@ -1393,7 +1387,14 @@ class TextInputConnection { } _cachedRect = rect; final Rect validRect = rect.isFinite ? rect : Offset.zero & const Size(-1, -1); - TextInput._instance._setComposingTextRect(validRect); + TextInput._instance._setComposingTextRect( + { + 'width': validRect.width, + 'height': validRect.height, + 'x': validRect.left, + 'y': validRect.top, + }, + ); } /// Sends the coordinates of caret rect. This is used on macOS for positioning @@ -1405,7 +1406,14 @@ class TextInputConnection { } _cachedCaretRect = rect; final Rect validRect = rect.isFinite ? rect : Offset.zero & const Size(-1, -1); - TextInput._instance._setCaretRect(validRect); + TextInput._instance._setCaretRect( + { + 'width': validRect.width, + 'height': validRect.height, + 'x': validRect.left, + 'y': validRect.top, + }, + ); } /// Send the bounding boxes of the current selected glyphs in the client to @@ -1415,7 +1423,9 @@ class TextInputConnection { void setSelectionRects(List selectionRects) { if (!listEquals(_cachedSelectionRects, selectionRects)) { _cachedSelectionRects = selectionRects; - TextInput._instance._setSelectionRects(selectionRects); + TextInput._instance._setSelectionRects(selectionRects.map((SelectionRect rect) { + return [rect.bounds.left, rect.bounds.top, rect.bounds.width, rect.bounds.height, rect.position]; + }).toList()); } } @@ -1434,11 +1444,13 @@ class TextInputConnection { assert(attached); TextInput._instance._setStyle( - fontFamily: fontFamily, - fontSize: fontSize, - fontWeight: fontWeight, - textDirection: textDirection, - textAlign: textAlign, + { + 'fontFamily': fontFamily, + 'fontSize': fontSize, + 'fontWeightIndex': fontWeight?.index, + 'textAlignIndex': textAlign.index, + 'textDirectionIndex': textDirection.index, + }, ); } @@ -1591,63 +1603,6 @@ class TextInput { static final TextInput _instance = TextInput._(); - static void _addInputControl(TextInputControl control) { - if (control != _PlatformTextInputControl.instance) { - _instance._inputControls.add(control); - } - } - - static void _removeInputControl(TextInputControl control) { - if (control != _PlatformTextInputControl.instance) { - _instance._inputControls.remove(control); - } - } - - /// Sets the current text input control. - /// - /// The current text input control receives text input state changes and visual - /// text input control requests, such as showing and hiding the input control, - /// from the framework. - /// - /// Setting the current text input control as `null` removes the visual text - /// input control. - /// - /// See also: - /// - /// * [TextInputControl], an interface for implementing text input controls. - /// * [TextInput.restorePlatformInputControl], a method to restore the default - /// platform text input control. - static void setInputControl(TextInputControl? newControl) { - final TextInputControl? oldControl = _instance._currentControl; - if (newControl == oldControl) { - return; - } - if (newControl != null) { - _addInputControl(newControl); - } - if (oldControl != null) { - _removeInputControl(oldControl); - } - _instance._currentControl = newControl; - final TextInputClient? client = _instance._currentConnection?._client; - client?.didChangeInputControl(oldControl, newControl); - } - - /// Restores the default platform text input control. - /// - /// See also: - /// - /// * [TextInput.setInputControl], a method to set a custom input - /// control, or to remove the visual input control. - static void restorePlatformInputControl() { - setInputControl(_PlatformTextInputControl.instance); - } - - TextInputControl? _currentControl = _PlatformTextInputControl.instance; - final Set _inputControls = { - _PlatformTextInputControl.instance, - }; - static const List _androidSupportedInputActions = [ TextInputAction.none, TextInputAction.unspecified, @@ -1706,9 +1661,15 @@ class TextInput { assert(connection._client != null); assert(configuration != null); assert(_debugEnsureInputActionWorksOnPlatform(configuration.inputAction)); + _channel.invokeMethod( + 'TextInput.setClient', + [ + connection._id, + configuration.toJson(), + ], + ); _currentConnection = connection; _currentConfiguration = configuration; - _setClient(connection._client, configuration); } static bool _debugEnsureInputActionWorksOnPlatform(TextInputAction inputAction) { @@ -1850,8 +1811,7 @@ class TextInput { switch (method) { case 'TextInputClient.updateEditingState': - final TextEditingValue value = TextEditingValue.fromJSON(args[1] as Map); - TextInput._instance._updateEditingValue(value, exclude: _PlatformTextInputControl.instance); + _currentConnection!._client.updateEditingValue(TextEditingValue.fromJSON(args[1] as Map)); break; case 'TextInputClient.updateEditingStateWithDeltas': assert(_currentConnection!._client is DeltaTextInputClient, 'You must be using a DeltaTextInputClient if TextInputConfiguration.enableDeltaModel is set to true'); @@ -1922,119 +1882,74 @@ class TextInput { scheduleMicrotask(() { _hidePending = false; if (_currentConnection == null) { - _hide(); + _channel.invokeMethod('TextInput.hide'); } }); } - void _setClient(TextInputClient client, TextInputConfiguration configuration) { - for (final TextInputControl control in _inputControls) { - control.attach(client, configuration); - } - } - void _clearClient() { - final TextInputClient client = _currentConnection!._client; - for (final TextInputControl control in _inputControls) { - control.detach(client); - } + _channel.invokeMethod('TextInput.clearClient'); _currentConnection = null; _scheduleHide(); } void _updateConfig(TextInputConfiguration configuration) { assert(configuration != null); - for (final TextInputControl control in _inputControls) { - control.updateConfig(configuration); - } + _channel.invokeMethod( + 'TextInput.updateConfig', + configuration.toJson(), + ); } void _setEditingState(TextEditingValue value) { assert(value != null); - for (final TextInputControl control in _inputControls) { - control.setEditingState(value); - } + _channel.invokeMethod( + 'TextInput.setEditingState', + value.toJSON(), + ); } void _show() { - for (final TextInputControl control in _inputControls) { - control.show(); - } - } - - void _hide() { - for (final TextInputControl control in _inputControls) { - control.hide(); - } - } - - void _setEditableSizeAndTransform(Size editableBoxSize, Matrix4 transform) { - for (final TextInputControl control in _inputControls) { - control.setEditableSizeAndTransform(editableBoxSize, transform); - } - } - - void _setComposingTextRect(Rect rect) { - for (final TextInputControl control in _inputControls) { - control.setComposingRect(rect); - } - } - - void _setCaretRect(Rect rect) { - for (final TextInputControl control in _inputControls) { - control.setCaretRect(rect); - } - } - - void _setSelectionRects(List selectionRects) { - for (final TextInputControl control in _inputControls) { - control.setSelectionRects(selectionRects); - } - } - - void _setStyle({ - required String? fontFamily, - required double? fontSize, - required FontWeight? fontWeight, - required TextDirection textDirection, - required TextAlign textAlign, - }) { - for (final TextInputControl control in _inputControls) { - control.setStyle( - fontFamily: fontFamily, - fontSize: fontSize, - fontWeight: fontWeight, - textDirection: textDirection, - textAlign: textAlign, - ); - } + _channel.invokeMethod('TextInput.show'); } void _requestAutofill() { - for (final TextInputControl control in _inputControls) { - control.requestAutofill(); - } + _channel.invokeMethod('TextInput.requestAutofill'); } - void _updateEditingValue(TextEditingValue value, {TextInputControl? exclude}) { - if (_currentConnection == null) { - return; - } - - for (final TextInputControl control in _instance._inputControls) { - if (control != exclude) { - control.setEditingState(value); - } - } - _instance._currentConnection!._client.updateEditingValue(value); + void _setEditableSizeAndTransform(Map args) { + _channel.invokeMethod( + 'TextInput.setEditableSizeAndTransform', + args, + ); } - /// Updates the editing value of the attached input client. - /// - /// This method should be called by the text input control implementation to - /// send editing value updates to the attached input client. - static void updateEditingValue(TextEditingValue value) { - _instance._updateEditingValue(value, exclude: _instance._currentControl); + void _setComposingTextRect(Map args) { + _channel.invokeMethod( + 'TextInput.setMarkedTextRect', + args, + ); + } + + void _setCaretRect(Map args) { + _channel.invokeMethod( + 'TextInput.setCaretRect', + args, + ); + } + + void _setSelectionRects(List> args) { + _channel.invokeMethod( + 'TextInput.setSelectionRects', + args, + ); + } + + void _setStyle(Map args) { + _channel.invokeMethod( + 'TextInput.setStyle', + args, + ); } /// Finishes the current autofill context, and potentially saves the user @@ -2087,9 +2002,10 @@ class TextInput { /// topmost [AutofillGroup] is getting disposed. static void finishAutofillContext({ bool shouldSave = true }) { assert(shouldSave != null); - for (final TextInputControl control in TextInput._instance._inputControls) { - control.finishAutofillContext(shouldSave: shouldSave); - } + TextInput._instance._channel.invokeMethod( + 'TextInput.finishAutofillContext', + shouldSave, + ); } /// Registers a [ScribbleClient] with [elementIdentifier] that can be focused @@ -2106,257 +2022,3 @@ class TextInput { TextInput._instance._scribbleClients.remove(elementIdentifier); } } - -/// An interface for implementing text input controls that receive text editing -/// state changes and visual input control requests. -/// -/// Editing state changes and input control requests are sent by the framework -/// when the editing state of the attached text input client changes, or it -/// requests the input control to be shown or hidden, for example. -/// -/// The input control can be installed with [TextInput.setInputControl], and the -/// default platform text input control can be restored with -/// [TextInput.restorePlatformInputControl]. -/// -/// The [TextInputControl] class must be extended. [TextInputControl] -/// implementations should call [TextInput.updateEditingValue] to send user -/// input to the attached input client. -/// -/// {@tool dartpad} -/// This example illustrates a basic [TextInputControl] implementation. -/// -/// ** See code in examples/api/lib/services/text_input/text_input_control.0.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [TextInput.setInputControl], a method to install a custom text input control. -/// * [TextInput.restorePlatformInputControl], a method to restore the default -/// platform text input control. -/// * [TextInput.updateEditingValue], a method to send user input to -/// the framework. -mixin TextInputControl { - /// Requests the text input control to attach to the given input client. - /// - /// This method is called when a text input client is attached. The input - /// control should update its configuration to match the client's configuration. - void attach(TextInputClient client, TextInputConfiguration configuration) {} - - /// Requests the text input control to detach from the given input client. - /// - /// This method is called when a text input client is detached. The input - /// control should release any resources allocated for the client. - void detach(TextInputClient client) {} - - /// Requests that the text input control is shown. - /// - /// This method is called when the input control should become visible. - void show() {} - - /// Requests that the text input control is hidden. - /// - /// This method is called when the input control should hide. - void hide() {} - - /// Informs the text input control about input configuration changes. - /// - /// This method is called when the configuration of the attached input client - /// has changed. - void updateConfig(TextInputConfiguration configuration) {} - - /// Informs the text input control about editing state changes. - /// - /// This method is called when the editing state of the attached input client - /// has changed. - void setEditingState(TextEditingValue value) {} - - /// Informs the text input control about client position changes. - /// - /// This method is called on when the input control should position itself in - /// relation to the attached input client. - void setEditableSizeAndTransform(Size editableBoxSize, Matrix4 transform) {} - - /// Informs the text input control about composing area changes. - /// - /// This method is called when the attached input client's composing area - /// changes. - void setComposingRect(Rect rect) {} - - /// Informs the text input control about caret area changes. - /// - /// This method is called when the attached input client's caret area - /// changes. - void setCaretRect(Rect rect) {} - - /// Informs the text input control about selection area changes. - /// - /// This method is called when the attached input client's selection area - /// changes. - void setSelectionRects(List selectionRects) {} - - /// Informs the text input control about text style changes. - /// - /// This method is called on the when the attached input client's text style - /// changes. - void setStyle({ - required String? fontFamily, - required double? fontSize, - required FontWeight? fontWeight, - required TextDirection textDirection, - required TextAlign textAlign, - }) {} - - /// Requests autofill from the text input control. - /// - /// This method is called when the autofill UI should appear. - void requestAutofill() {} - - /// Requests that the autofill context is finalized. - /// - /// See also: - /// - /// * [TextInput.finishAutofillContext] - void finishAutofillContext({bool shouldSave = true}) {} -} - -/// Provides access to the platform text input control. -class _PlatformTextInputControl with TextInputControl { - _PlatformTextInputControl._(); - - /// The shared instance of [_PlatformTextInputControl]. - static final _PlatformTextInputControl instance = _PlatformTextInputControl._(); - - MethodChannel get _channel => TextInput._instance._channel; - - Map _configurationToJson(TextInputConfiguration configuration) { - final Map json = configuration.toJson(); - if (TextInput._instance._currentControl != _PlatformTextInputControl.instance) { - json['inputType'] = TextInputType.none.toJson(); - } - return json; - } - - @override - void attach(TextInputClient client, TextInputConfiguration configuration) { - _channel.invokeMethod( - 'TextInput.setClient', - [ - TextInput._instance._currentConnection!._id, - _configurationToJson(configuration), - ], - ); - } - - @override - void detach(TextInputClient client) { - _channel.invokeMethod('TextInput.clearClient'); - } - - @override - void updateConfig(TextInputConfiguration configuration) { - _channel.invokeMethod( - 'TextInput.updateConfig', - _configurationToJson(configuration), - ); - } - - @override - void setEditingState(TextEditingValue value) { - _channel.invokeMethod( - 'TextInput.setEditingState', - value.toJSON(), - ); - } - - @override - void show() { - _channel.invokeMethod('TextInput.show'); - } - - @override - void hide() { - _channel.invokeMethod('TextInput.hide'); - } - - @override - void setEditableSizeAndTransform(Size editableBoxSize, Matrix4 transform) { - _channel.invokeMethod( - 'TextInput.setEditableSizeAndTransform', - { - 'width': editableBoxSize.width, - 'height': editableBoxSize.height, - 'transform': transform.storage, - }, - ); - } - - @override - void setComposingRect(Rect rect) { - _channel.invokeMethod( - 'TextInput.setMarkedTextRect', - { - 'width': rect.width, - 'height': rect.height, - 'x': rect.left, - 'y': rect.top, - }, - ); - } - - @override - void setCaretRect(Rect rect) { - _channel.invokeMethod( - 'TextInput.setCaretRect', - { - 'width': rect.width, - 'height': rect.height, - 'x': rect.left, - 'y': rect.top, - }, - ); - } - - @override - void setSelectionRects(List selectionRects) { - _channel.invokeMethod( - 'TextInput.setSelectionRects', - selectionRects.map((SelectionRect rect) { - return [rect.bounds.left, rect.bounds.top, rect.bounds.width, rect.bounds.height, rect.position]; - }).toList(), - ); - } - - - @override - void setStyle({ - required String? fontFamily, - required double? fontSize, - required FontWeight? fontWeight, - required TextDirection textDirection, - required TextAlign textAlign, - }) { - _channel.invokeMethod( - 'TextInput.setStyle', - { - 'fontFamily': fontFamily, - 'fontSize': fontSize, - 'fontWeightIndex': fontWeight?.index, - 'textAlignIndex': textAlign.index, - 'textDirectionIndex': textDirection.index, - }, - ); - } - - @override - void requestAutofill() { - _channel.invokeMethod('TextInput.requestAutofill'); - } - - @override - void finishAutofillContext({bool shouldSave = true}) { - _channel.invokeMethod( - 'TextInput.finishAutofillContext', - shouldSave, - ); - } -} diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index bad52f3373..5b3434ce1d 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2675,14 +2675,6 @@ class EditableTextState extends State with AutomaticKeepAliveClien } - @override - void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) { - if (_hasFocus && _hasInputConnection) { - oldControl?.hide(); - newControl?.show(); - } - } - @override void connectionClosed() { if (_hasInputConnection) { diff --git a/packages/flutter/test/services/autofill_test.dart b/packages/flutter/test/services/autofill_test.dart index dba33c479f..4653fa8cde 100644 --- a/packages/flutter/test/services/autofill_test.dart +++ b/packages/flutter/test/services/autofill_test.dart @@ -139,11 +139,6 @@ class FakeAutofillClient implements TextInputClient, AutofillClient { latestMethodCall = 'showAutocorrectionPromptRect'; } - @override - void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) { - latestMethodCall = 'didChangeInputControl'; - } - @override void autofill(TextEditingValue newEditingValue) => updateEditingValue(newEditingValue); diff --git a/packages/flutter/test/services/delta_text_input_test.dart b/packages/flutter/test/services/delta_text_input_test.dart index 4e98c5cd6b..0c755140f2 100644 --- a/packages/flutter/test/services/delta_text_input_test.dart +++ b/packages/flutter/test/services/delta_text_input_test.dart @@ -292,9 +292,4 @@ class FakeDeltaTextInputClient implements DeltaTextInputClient { } TextInputConfiguration get configuration => const TextInputConfiguration(enableDeltaModel: true); - - @override - void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) { - latestMethodCall = 'didChangeInputControl'; - } } diff --git a/packages/flutter/test/services/text_input_test.dart b/packages/flutter/test/services/text_input_test.dart index a1be567feb..81e64420bc 100644 --- a/packages/flutter/test/services/text_input_test.dart +++ b/packages/flutter/test/services/text_input_test.dart @@ -780,178 +780,6 @@ void main() { isTrue, ); }); - - group('TextInputControl', () { - late FakeTextChannel fakeTextChannel; - - setUp(() { - fakeTextChannel = FakeTextChannel((MethodCall call) async {}); - TextInput.setChannel(fakeTextChannel); - }); - - tearDown(() { - TextInput.restorePlatformInputControl(); - TextInputConnection.debugResetId(); - TextInput.setChannel(SystemChannels.textInput); - }); - - test('gets attached and detached', () { - final FakeTextInputControl control = FakeTextInputControl(); - TextInput.setInputControl(control); - - final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); - final TextInputConnection connection = TextInput.attach(client, const TextInputConfiguration()); - - final List expectedMethodCalls = ['attach']; - expect(control.methodCalls, expectedMethodCalls); - - connection.close(); - expectedMethodCalls.add('detach'); - expect(control.methodCalls, expectedMethodCalls); - }); - - test('receives text input state changes', () { - final FakeTextInputControl control = FakeTextInputControl(); - TextInput.setInputControl(control); - - final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); - final TextInputConnection connection = TextInput.attach(client, const TextInputConfiguration()); - control.methodCalls.clear(); - - final List expectedMethodCalls = []; - - connection.updateConfig(const TextInputConfiguration()); - expectedMethodCalls.add('updateConfig'); - expect(control.methodCalls, expectedMethodCalls); - - connection.setEditingState(TextEditingValue.empty); - expectedMethodCalls.add('setEditingState'); - expect(control.methodCalls, expectedMethodCalls); - - connection.close(); - expectedMethodCalls.add('detach'); - expect(control.methodCalls, expectedMethodCalls); - }); - - test('does not interfere with platform text input', () { - final FakeTextInputControl control = FakeTextInputControl(); - TextInput.setInputControl(control); - - final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); - TextInput.attach(client, const TextInputConfiguration()); - - fakeTextChannel.outgoingCalls.clear(); - - fakeTextChannel.incoming!(MethodCall('TextInputClient.updateEditingState', [1, TextEditingValue.empty.toJSON()])); - - expect(client.latestMethodCall, 'updateEditingValue'); - expect(control.methodCalls, ['attach', 'setEditingState']); - expect(fakeTextChannel.outgoingCalls, isEmpty); - }); - - test('both input controls receive requests', () async { - final FakeTextInputControl control = FakeTextInputControl(); - TextInput.setInputControl(control); - - const TextInputConfiguration textConfig = TextInputConfiguration(); - const TextInputConfiguration numberConfig = TextInputConfiguration(inputType: TextInputType.number); - const TextInputConfiguration noneConfig = TextInputConfiguration(inputType: TextInputType.none); - - final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); - final TextInputConnection connection = TextInput.attach(client, textConfig); - - final List expectedMethodCalls = ['attach']; - expect(control.methodCalls, expectedMethodCalls); - expect(control.inputType, TextInputType.text); - fakeTextChannel.validateOutgoingMethodCalls([ - // When there's a custom text input control installed, the platform text - // input control receives TextInputType.none - MethodCall('TextInput.setClient', [1, noneConfig.toJson()]), - ]); - - connection.show(); - expectedMethodCalls.add('show'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 2); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.show'); - - connection.updateConfig(numberConfig); - expectedMethodCalls.add('updateConfig'); - expect(control.methodCalls, expectedMethodCalls); - expect(control.inputType, TextInputType.number); - expect(fakeTextChannel.outgoingCalls.length, 3); - fakeTextChannel.validateOutgoingMethodCalls([ - // When there's a custom text input control installed, the platform text - // input control receives TextInputType.none - MethodCall('TextInput.setClient', [1, noneConfig.toJson()]), - const MethodCall('TextInput.show'), - MethodCall('TextInput.updateConfig', noneConfig.toJson()), - ]); - - connection.setComposingRect(Rect.zero); - expectedMethodCalls.add('setComposingRect'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 4); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setMarkedTextRect'); - - connection.setCaretRect(Rect.zero); - expectedMethodCalls.add('setCaretRect'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 5); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setCaretRect'); - - connection.setEditableSizeAndTransform(Size.zero, Matrix4.identity()); - expectedMethodCalls.add('setEditableSizeAndTransform'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 6); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setEditableSizeAndTransform'); - - connection.setSelectionRects(const [SelectionRect(position: 0, bounds: Rect.zero)]); - expectedMethodCalls.add('setSelectionRects'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 7); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setSelectionRects'); - - connection.setStyle( - fontFamily: null, - fontSize: null, - fontWeight: null, - textDirection: TextDirection.ltr, - textAlign: TextAlign.left, - ); - expectedMethodCalls.add('setStyle'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 8); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setStyle'); - - connection.close(); - expectedMethodCalls.add('detach'); - expect(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 9); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.clearClient'); - - expectedMethodCalls.add('hide'); - final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); - await binding.runAsync(() async {}); - await expectLater(control.methodCalls, expectedMethodCalls); - expect(fakeTextChannel.outgoingCalls.length, 10); - expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.hide'); - }); - - test('notifies changes to the attached client', () async { - final FakeTextInputControl control = FakeTextInputControl(); - TextInput.setInputControl(control); - - final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); - final TextInputConnection connection = TextInput.attach(client, const TextInputConfiguration()); - - TextInput.setInputControl(null); - expect(client.latestMethodCall, 'didChangeInputControl'); - - connection.show(); - expect(client.latestMethodCall, 'didChangeInputControl'); - }); - }); } class FakeTextInputClient with TextInputClient { @@ -1005,11 +833,6 @@ class FakeTextInputClient with TextInputClient { TextInputConfiguration get configuration => const TextInputConfiguration(); - @override - void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) { - latestMethodCall = 'didChangeInputControl'; - } - @override void insertTextPlaceholder(Size size) { latestMethodCall = 'insertTextPlaceholder'; @@ -1026,81 +849,3 @@ class FakeTextInputClient with TextInputClient { performedSelectors.add(selectorName); } } - -class FakeTextInputControl with TextInputControl { - final List methodCalls = []; - late TextInputType inputType; - - @override - void attach(TextInputClient client, TextInputConfiguration configuration) { - methodCalls.add('attach'); - inputType = configuration.inputType; - } - - @override - void detach(TextInputClient client) { - methodCalls.add('detach'); - } - - @override - void setEditingState(TextEditingValue value) { - methodCalls.add('setEditingState'); - } - - @override - void updateConfig(TextInputConfiguration configuration) { - methodCalls.add('updateConfig'); - inputType = configuration.inputType; - } - - @override - void show() { - methodCalls.add('show'); - } - - @override - void hide() { - methodCalls.add('hide'); - } - - @override - void setComposingRect(Rect rect) { - methodCalls.add('setComposingRect'); - } - - @override - void setCaretRect(Rect rect) { - methodCalls.add('setCaretRect'); - } - - @override - void setEditableSizeAndTransform(Size editableBoxSize, Matrix4 transform) { - methodCalls.add('setEditableSizeAndTransform'); - } - - @override - void setSelectionRects(List selectionRects) { - methodCalls.add('setSelectionRects'); - } - - @override - void setStyle({ - required String? fontFamily, - required double? fontSize, - required FontWeight? fontWeight, - required TextDirection textDirection, - required TextAlign textAlign, - }) { - methodCalls.add('setStyle'); - } - - @override - void finishAutofillContext({bool shouldSave = true}) { - methodCalls.add('finishAutofillContext'); - } - - @override - void requestAutofill() { - methodCalls.add('requestAutofill'); - } -}