diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart index ba51ee408f3..5174520235a 100644 --- a/packages/flutter/lib/src/services/text_input.dart +++ b/packages/flutter/lib/src/services/text_input.dart @@ -880,6 +880,13 @@ class TextInputConnection { TextInput._instance._requestAutofill(); } + /// Requests that the text input control update itself according to the new + /// [TextInputConfiguration]. + void updateConfig(TextInputConfiguration configuration) { + assert(attached); + TextInput._instance._updateConfig(configuration); + } + /// Requests that the text input control change its internal state to match the given state. void setEditingState(TextEditingValue value) { assert(attached); @@ -1211,6 +1218,14 @@ class TextInput { _scheduleHide(); } + void _updateConfig(TextInputConfiguration configuration) { + assert(configuration != null); + _channel.invokeMethod( + 'TextInput.updateConfig', + configuration.toJson(), + ); + } + void _setEditingState(TextEditingValue value) { assert(value != null); _channel.invokeMethod( diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 24b8ff87862..4d3bfb1b0e0 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1561,15 +1561,22 @@ class EditableTextState extends State with AutomaticKeepAliveClien if (!_shouldCreateInputConnection) { _closeInputConnectionIfNeeded(); } else { - if (oldWidget.readOnly && _hasFocus) + if (oldWidget.readOnly && _hasFocus) { _openInputConnection(); + } + } + + if (kIsWeb && _hasInputConnection) { + if (oldWidget.readOnly != widget.readOnly) { + _textInputConnection!.updateConfig(textInputConfiguration); + } } if (widget.style != oldWidget.style) { final TextStyle style = widget.style; // The _textInputConnection will pick up the new style when it attaches in // _openInputConnection. - if (_textInputConnection != null && _textInputConnection!.attached) { + if (_hasInputConnection) { _textInputConnection!.setStyle( fontFamily: style.fontFamily, fontSize: style.fontSize, diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 9f6ed0e3094..051d2b26221 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -1436,6 +1436,44 @@ void main() { } }); + testWidgets('Sends "updateConfig" when read-only flag is flipped', (WidgetTester tester) async { + bool readOnly = true; + StateSetter setState; + final TextEditingController controller = TextEditingController(text: 'Lorem ipsum dolor sit amet'); + + await tester.pumpWidget( + MaterialApp( + home: StatefulBuilder(builder: (BuildContext context, StateSetter stateSetter) { + setState = stateSetter; + return EditableText( + readOnly: readOnly, + controller: controller, + backgroundCursorColor: Colors.grey, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + ); + }), + ), + ); + + // Interact with the field to establish the input connection. + final Offset topLeft = tester.getTopLeft(find.byType(EditableText)); + await tester.tapAt(topLeft + const Offset(0.0, 5.0)); + await tester.pump(); + + expect(tester.testTextInput.hasAnyClients, kIsWeb ? isTrue : isFalse); + if (kIsWeb) { + expect(tester.testTextInput.setClientArgs['readOnly'], isTrue); + } + + setState(() { readOnly = false; }); + await tester.pump(); + + expect(tester.testTextInput.hasAnyClients, isTrue); + expect(tester.testTextInput.setClientArgs['readOnly'], isFalse); + }); + testWidgets('Fires onChanged when text changes via TextSelectionOverlay', (WidgetTester tester) async { String changedValue; final Widget widget = MaterialApp( diff --git a/packages/flutter_test/lib/src/test_text_input.dart b/packages/flutter_test/lib/src/test_text_input.dart index 01dba631a93..d0aa5b87d34 100644 --- a/packages/flutter_test/lib/src/test_text_input.dart +++ b/packages/flutter_test/lib/src/test_text_input.dart @@ -98,6 +98,9 @@ class TestTextInput { _client = methodCall.arguments[0] as int; setClientArgs = methodCall.arguments[1] as Map; break; + case 'TextInput.updateConfig': + setClientArgs = methodCall.arguments as Map; + break; case 'TextInput.clearClient': _client = 0; _isVisible = false;