diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 5b9c0437ffb89..b18a03778e751 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -782,9 +782,10 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy { // On Safari Desktop, when a form is focused, it opens an autofill menu // immediately. // Flutter framework sends `setEditableSizeAndTransform` for informing - // the engine about the location of the text field. This call will - // arrive after `show` call. Therefore form is placed, when - // `setEditableSizeAndTransform` method is called and focus called on the + // the engine about the location of the text field. This call may arrive + // after the first `show` call, depending on the text input widget's + // implementation. Therefore form is placed, when + // `setEditableSizeAndTransform` method is called and focus called on the // form only after placing it to the correct position and only once after // that. Calling focus multiple times causes flickering. focusedFormElement!.focus(); @@ -800,6 +801,9 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy { @override void initializeElementPlacement() { + if (geometry != null) { + placeElement(); + } activeDomElement.focus(); } } diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 3a37ab76743b4..a5dc20790c989 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -551,6 +551,40 @@ void testMain() { expect(spy.messages, isEmpty); }); + test('setClient, setEditingState, setSizeAndTransform, show - input element is put into the DOM', () { + editingStrategy = SafariDesktopTextEditingStrategy(textEditing!); + textEditing!.debugTextEditingStrategyOverride = editingStrategy; + final MethodCall setClient = MethodCall( + 'TextInput.setClient', [123, flutterSinglelineConfig]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + const MethodCall setEditingState = + MethodCall('TextInput.setEditingState', { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + }); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + + // Editing shouldn't have started yet. + expect(document.activeElement, document.body); + + // The "setSizeAndTransform" message has to be here before we call + // checkInputEditingState, since on some platforms (e.g. Desktop Safari) + // we don't put the input element into the DOM until we get its correct + // dimensions from the framework. + final MethodCall setSizeAndTransform = + configureSetSizeAndTransformMethodCall(150, 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + expect(defaultTextEditingRoot.activeElement, + textEditing!.strategy.domElement); + }); + test('setClient, setEditingState, show, updateConfig, clearClient', () { final MethodCall setClient = MethodCall('TextInput.setClient', [ 123,