Unverified Commit c033b08d authored by Satsrag's avatar Satsrag Committed by GitHub

fix: cannot input new line using custom input control (#140356)

For https://github.com/flutter/flutter/issues/125875 and https://github.com/flutter/engine/pull/45522

According to [this discussion](https://github.com/flutter/flutter/pull/139446#discussion_r1424776370), Just added `forceMultiline` to JSON on `_configurationToJson`. @LongCatIsLooong @Renzo-Olivares
parent 01c057cd
...@@ -2226,7 +2226,15 @@ class _PlatformTextInputControl with TextInputControl { ...@@ -2226,7 +2226,15 @@ class _PlatformTextInputControl with TextInputControl {
Map<String, dynamic> _configurationToJson(TextInputConfiguration configuration) { Map<String, dynamic> _configurationToJson(TextInputConfiguration configuration) {
final Map<String, dynamic> json = configuration.toJson(); final Map<String, dynamic> json = configuration.toJson();
if (TextInput._instance._currentControl != _PlatformTextInputControl.instance) { if (TextInput._instance._currentControl != _PlatformTextInputControl.instance) {
json['inputType'] = TextInputType.none.toJson(); final Map<String, dynamic> none = TextInputType.none.toJson();
// See: https://github.com/flutter/flutter/issues/125875
// On Web engine, use isMultiline to create <input> or <textarea> element
// When there's a custom [TextInputControl] installed.
// It's only needed When there's a custom [TextInputControl] installed.
if (kIsWeb) {
none['isMultiline'] = configuration.inputType == TextInputType.multiline;
}
json['inputType'] = none;
} }
return json; return json;
} }
......
...@@ -879,8 +879,25 @@ void main() { ...@@ -879,8 +879,25 @@ void main() {
const TextInputConfiguration textConfig = TextInputConfiguration(); const TextInputConfiguration textConfig = TextInputConfiguration();
const TextInputConfiguration numberConfig = TextInputConfiguration(inputType: TextInputType.number); const TextInputConfiguration numberConfig = TextInputConfiguration(inputType: TextInputType.number);
const TextInputConfiguration multilineConfig = TextInputConfiguration(inputType: TextInputType.multiline);
const TextInputConfiguration noneConfig = TextInputConfiguration(inputType: TextInputType.none); const TextInputConfiguration noneConfig = TextInputConfiguration(inputType: TextInputType.none);
// Test for https://github.com/flutter/flutter/issues/125875.
// When there's a custom text input control installed on Web, the platform text
// input control receives TextInputType.none and isMultiline flag.
// isMultiline flag is set to true when the input type is multiline.
// isMultiline flag is set to false when the input type is not multiline.
final Map<String, dynamic> noneIsMultilineFalseJson = noneConfig.toJson();
final Map<String, dynamic> noneInputType = noneIsMultilineFalseJson['inputType'] as Map<String, dynamic>;
if (kIsWeb) {
noneInputType['isMultiline'] = false;
}
final Map<String, dynamic> noneIsMultilineTrueJson = noneConfig.toJson();
final Map<String, dynamic> noneInputType1 = noneIsMultilineTrueJson['inputType'] as Map<String, dynamic>;
if (kIsWeb) {
noneInputType1['isMultiline'] = true;
}
final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty); final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
final TextInputConnection connection = TextInput.attach(client, textConfig); final TextInputConnection connection = TextInput.attach(client, textConfig);
...@@ -889,8 +906,8 @@ void main() { ...@@ -889,8 +906,8 @@ void main() {
expect(control.inputType, TextInputType.text); expect(control.inputType, TextInputType.text);
fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[ fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[
// When there's a custom text input control installed, the platform text // When there's a custom text input control installed, the platform text
// input control receives TextInputType.none // input control receives TextInputType.none with isMultiline flag
MethodCall('TextInput.setClient', <dynamic>[1, noneConfig.toJson()]), MethodCall('TextInput.setClient', <dynamic>[1, noneIsMultilineFalseJson]),
]); ]);
connection.show(); connection.show();
...@@ -906,34 +923,49 @@ void main() { ...@@ -906,34 +923,49 @@ void main() {
expect(fakeTextChannel.outgoingCalls.length, 3); expect(fakeTextChannel.outgoingCalls.length, 3);
fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[ fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[
// When there's a custom text input control installed, the platform text // When there's a custom text input control installed, the platform text
// input control receives TextInputType.none // input control receives TextInputType.none with isMultiline flag
MethodCall('TextInput.setClient', <dynamic>[1, noneConfig.toJson()]), MethodCall('TextInput.setClient', <dynamic>[1, noneIsMultilineFalseJson]),
const MethodCall('TextInput.show'),
MethodCall('TextInput.updateConfig', noneIsMultilineFalseJson),
]);
connection.updateConfig(multilineConfig);
expectedMethodCalls.add('updateConfig');
expect(control.methodCalls, expectedMethodCalls);
expect(control.inputType, TextInputType.multiline);
expect(fakeTextChannel.outgoingCalls.length, 4);
fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[
// When there's a custom text input control installed, the platform text
// input control receives TextInputType.none with isMultiline flag
MethodCall('TextInput.setClient', <dynamic>[1, noneIsMultilineFalseJson]),
const MethodCall('TextInput.show'), const MethodCall('TextInput.show'),
MethodCall('TextInput.updateConfig', noneConfig.toJson()), MethodCall('TextInput.updateConfig', noneIsMultilineFalseJson),
MethodCall('TextInput.updateConfig', noneIsMultilineTrueJson),
]); ]);
connection.setComposingRect(Rect.zero); connection.setComposingRect(Rect.zero);
expectedMethodCalls.add('setComposingRect'); expectedMethodCalls.add('setComposingRect');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 4); expect(fakeTextChannel.outgoingCalls.length, 5);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setMarkedTextRect'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setMarkedTextRect');
connection.setCaretRect(Rect.zero); connection.setCaretRect(Rect.zero);
expectedMethodCalls.add('setCaretRect'); expectedMethodCalls.add('setCaretRect');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 5); expect(fakeTextChannel.outgoingCalls.length, 6);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setCaretRect'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setCaretRect');
connection.setEditableSizeAndTransform(Size.zero, Matrix4.identity()); connection.setEditableSizeAndTransform(Size.zero, Matrix4.identity());
expectedMethodCalls.add('setEditableSizeAndTransform'); expectedMethodCalls.add('setEditableSizeAndTransform');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 6); expect(fakeTextChannel.outgoingCalls.length, 7);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setEditableSizeAndTransform'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setEditableSizeAndTransform');
connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 1, bounds: Rect.fromLTWH(2, 3, 4, 5), direction: TextDirection.rtl)]); connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 1, bounds: Rect.fromLTWH(2, 3, 4, 5), direction: TextDirection.rtl)]);
expectedMethodCalls.add('setSelectionRects'); expectedMethodCalls.add('setSelectionRects');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 7); expect(fakeTextChannel.outgoingCalls.length, 8);
expect(fakeTextChannel.outgoingCalls.last.arguments, const TypeMatcher<List<List<num>>>()); expect(fakeTextChannel.outgoingCalls.last.arguments, const TypeMatcher<List<List<num>>>());
final List<List<num>> sentList = fakeTextChannel.outgoingCalls.last.arguments as List<List<num>>; final List<List<num>> sentList = fakeTextChannel.outgoingCalls.last.arguments as List<List<num>>;
expect(sentList.length, 1); expect(sentList.length, 1);
...@@ -955,23 +987,52 @@ void main() { ...@@ -955,23 +987,52 @@ void main() {
); );
expectedMethodCalls.add('setStyle'); expectedMethodCalls.add('setStyle');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 8); expect(fakeTextChannel.outgoingCalls.length, 9);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setStyle'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setStyle');
connection.close(); connection.close();
expectedMethodCalls.add('detach'); expectedMethodCalls.add('detach');
expect(control.methodCalls, expectedMethodCalls); expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 9); expect(fakeTextChannel.outgoingCalls.length, 10);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.clearClient'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.clearClient');
expectedMethodCalls.add('hide'); expectedMethodCalls.add('hide');
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
await binding.runAsync(() async {}); await binding.runAsync(() async {});
await expectLater(control.methodCalls, expectedMethodCalls); await expectLater(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 10); expect(fakeTextChannel.outgoingCalls.length, 11);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.hide'); expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.hide');
}); });
test('the platform input control receives isMultiline true on attach', () async {
final FakeTextInputControl control = FakeTextInputControl();
TextInput.setInputControl(control);
const TextInputConfiguration multilineConfig = TextInputConfiguration(inputType: TextInputType.multiline);
const TextInputConfiguration noneConfig = TextInputConfiguration(inputType: TextInputType.none);
// Test for https://github.com/flutter/flutter/issues/125875.
// When there's a custom text input control installed, the platform text
// input control receives TextInputType.none and isMultiline flag.
// isMultiline flag is set to true when the input type is multiline.
// isMultiline flag is set to false when the input type is not multiline.
final Map<String, dynamic> noneIsMultilineTrueJson = noneConfig.toJson();
final Map<String, dynamic> noneInputType = noneIsMultilineTrueJson['inputType'] as Map<String, dynamic>;
noneInputType['isMultiline'] = true;
final FakeTextInputClient client = FakeTextInputClient(TextEditingValue.empty);
TextInput.attach(client, multilineConfig);
final List<String> expectedMethodCalls = <String>['attach'];
expect(control.methodCalls, expectedMethodCalls);
expect(control.inputType, TextInputType.multiline);
fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[
// When there's a custom text input control installed, the platform text
// input control receives TextInputType.none with isMultiline flag
MethodCall('TextInput.setClient', <dynamic>[1, noneIsMultilineTrueJson]),
]);
}, skip: !kIsWeb); // https://github.com/flutter/flutter/issues/125875
test('notifies changes to the attached client', () async { test('notifies changes to the attached client', () async {
final FakeTextInputControl control = FakeTextInputControl(); final FakeTextInputControl control = FakeTextInputControl();
TextInput.setInputControl(control); TextInput.setInputControl(control);
......
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