From cd334c4fee017d6120b6f13db302f9b72284b666 Mon Sep 17 00:00:00 2001 From: Steve Metzger Date: Wed, 20 Jan 2021 15:15:39 -0500 Subject: [PATCH 1/2] TextFormField does not get blurred when tapped outside of the widget. --- .../src/engine/text_editing/text_editing.dart | 62 +++++++++++++++++-- lib/web_ui/test/text_editing_test.dart | 33 ++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) 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 b131a08fa153f..660bb9a5f288d 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 @@ -512,6 +512,7 @@ class InputConfiguration { const TextCapitalizationConfig.defaultCapitalization(), this.autofill, this.autofillGroup, + this.forceCloseConnectionOnBlur = false, }); InputConfiguration.fromFrameworkMessage( @@ -523,6 +524,7 @@ class InputConfiguration { inputAction = flutterInputConfiguration['inputAction'] ?? 'TextInputAction.done', obscureText = flutterInputConfiguration['obscureText'] ?? false, + forceCloseConnectionOnBlur = flutterInputConfiguration['forceCloseConnectionOnBlur'] ?? false, readOnly = flutterInputConfiguration['readOnly'] ?? false, autocorrect = flutterInputConfiguration['autocorrect'] ?? true, textCapitalization = TextCapitalizationConfig.fromInputConfiguration( @@ -550,6 +552,22 @@ class InputConfiguration { /// Whether to hide the text being edited. final bool obscureText; + /// Previously, Flutter for Web behaved like a web page. If user clicked on an + /// area other than the input field itself, the input field was blurred, thus + /// the connection is closed. + /// This behavior was changed for Desktop Browsers. + /// https://github.com/flutter/engine/pull/18743 + /// + /// Now only, pressing enter or tab changes the focus of the input fields. + /// + /// The created a regression for applications that relied on the old beharior + /// https://github.com/flutter/flutter/issues/64245 + /// + /// This flag provides a workaround to the regression allowing developers to + /// optionally force the connection to close on blur, as suggested in the comments + /// https://github.com/flutter/flutter/issues/64245#issuecomment-681815149 + final bool forceCloseConnectionOnBlur; + /// Whether to enable autocorrection. /// /// Definition of autocorrect can be found in: @@ -838,10 +856,29 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { _subscriptions.add(html.document.onSelectionChange.listen(_handleChange)); - // Refocus on the domElement after blur, so that user can keep editing the - // text field. + // The behavior for blur in DOM elements changes depending on the reason of + // blur: + // + // (1) By default we refocus on the domElement after blur, so that user can + // keep editing the text field. + // + // (2) BUT if the blur is triggered due to interaction with another + // element on the page AND we explicitly force the connection to close on + // blur, the current text connection is obsolete so connection close request + // is send to Flutter. + // + // See [HybridTextEditing.sendTextConnectionClosedToFlutterIfAny]. + // + // In order to detect between these two cases, after a blur event is + // triggered [domRenderer.windowHasFocus] method which checks the window + // focus is called. _subscriptions.add(domElement.onBlur.listen((_) { - domElement.focus(); + bool windowHasFocus = domRenderer.windowHasFocus ?? false; + if (windowHasFocus && _inputConfiguration.forceCloseConnectionOnBlur) { + owner.sendTextConnectionClosedToFrameworkIfAny(); + } else { + domElement.focus(); + } })); preventDefaultForMouseEvents(); @@ -1259,10 +1296,23 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { // enough for covering "Select All" functionality. _subscriptions.add(domElement.onSelect.listen(_handleChange)); - // Refocus on the domElement after blur, so that user can keep editing the - // text field. + + // For Firefox, we also use the same approach as the parent class. + // + // The different part is, in Firefox, we are not able to get correct value + // when we check the window focus like [domRendered.windowHasFocus]. + // + // However [document.activeElement] always equals to [domElement] if the + // user goes to another tab, minimizes the browser or opens the dev tools. + // Hence [document.activeElement] is checked in this listener. _subscriptions.add(domElement.onBlur.listen((_) { - _postponeFocus(); + html.Element? activeElement = html.document.activeElement; + bool domElementIsActive = activeElement != domElement; + if (domElementIsActive && _inputConfiguration.forceCloseConnectionOnBlur) { + owner.sendTextConnectionClosedToFrameworkIfAny(); + } else { + _postponeFocus(); + } })); preventDefaultForMouseEvents(); diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index efcf24df89585..cd2e90c9bdf20 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -473,6 +473,37 @@ void testMain() { // TODO(nurhan): https://github.com/flutter/flutter/issues/50769 skip: browserEngine == BrowserEngine.edge); + test('Does not re-acquire focus when forced', () { + editingElement = + SemanticsTextEditingStrategy( + SemanticsObject(5, null), HybridTextEditing(), testInputElement); + final InputConfiguration config = InputConfiguration( + inputType: EngineInputType.text, + forceCloseConnectionOnBlur: true, + ); + + expect(document.activeElement, document.body); + + document.body.append(testInputElement); + editingElement.enable( + config, + onChange: trackEditingState, + onAction: trackInputAction, + ); + expect(document.activeElement, testInputElement); + + // The input should lose focus now.. + editingElement.domElement.blur(); + expect(document.activeElement, document.body); + + editingElement.disable(); + }, + // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 + // TODO(nurhan): https://github.com/flutter/flutter/issues/50769 + skip: (browserEngine == BrowserEngine.webkit || + browserEngine == BrowserEngine.edge || + browserEngine == BrowserEngine.firefox)); + test('Does not dispose and recreate dom elements in persistent mode', () { editingElement = SemanticsTextEditingStrategy( SemanticsObject(5, null), HybridTextEditing(), testInputElement); @@ -2274,6 +2305,7 @@ Map createFlutterConfig( String inputType, { bool readOnly = false, bool obscureText = false, + bool forceCloseConnectionOnBlur = false, bool autocorrect = true, String textCapitalization = 'TextCapitalization.none', String inputAction, @@ -2288,6 +2320,7 @@ Map createFlutterConfig( }, 'readOnly': readOnly, 'obscureText': obscureText, + 'forceCloseConnectionOnBlur': forceCloseConnectionOnBlur, 'autocorrect': autocorrect, 'inputAction': inputAction ?? 'TextInputAction.done', 'textCapitalization': textCapitalization, From ee289f31423dcb2b0f36bf1a45df449753511469 Mon Sep 17 00:00:00 2001 From: Steve Metzger Date: Fri, 19 Feb 2021 14:21:13 -0500 Subject: [PATCH 2/2] change forceCloseConnectionOnBlur to forceSubmitOnFocusLost --- .../lib/src/engine/text_editing/text_editing.dart | 10 +++++----- lib/web_ui/test/text_editing_test.dart | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) 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 660bb9a5f288d..1af89de167c07 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 @@ -512,7 +512,7 @@ class InputConfiguration { const TextCapitalizationConfig.defaultCapitalization(), this.autofill, this.autofillGroup, - this.forceCloseConnectionOnBlur = false, + this.forceSubmitOnFocusLost = false, }); InputConfiguration.fromFrameworkMessage( @@ -524,7 +524,7 @@ class InputConfiguration { inputAction = flutterInputConfiguration['inputAction'] ?? 'TextInputAction.done', obscureText = flutterInputConfiguration['obscureText'] ?? false, - forceCloseConnectionOnBlur = flutterInputConfiguration['forceCloseConnectionOnBlur'] ?? false, + forceSubmitOnFocusLost = flutterInputConfiguration['forceSubmitOnFocusLost'] ?? false, readOnly = flutterInputConfiguration['readOnly'] ?? false, autocorrect = flutterInputConfiguration['autocorrect'] ?? true, textCapitalization = TextCapitalizationConfig.fromInputConfiguration( @@ -566,7 +566,7 @@ class InputConfiguration { /// This flag provides a workaround to the regression allowing developers to /// optionally force the connection to close on blur, as suggested in the comments /// https://github.com/flutter/flutter/issues/64245#issuecomment-681815149 - final bool forceCloseConnectionOnBlur; + final bool forceSubmitOnFocusLost; /// Whether to enable autocorrection. /// @@ -874,7 +874,7 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { // focus is called. _subscriptions.add(domElement.onBlur.listen((_) { bool windowHasFocus = domRenderer.windowHasFocus ?? false; - if (windowHasFocus && _inputConfiguration.forceCloseConnectionOnBlur) { + if (windowHasFocus && _inputConfiguration.forceSubmitOnFocusLost) { owner.sendTextConnectionClosedToFrameworkIfAny(); } else { domElement.focus(); @@ -1308,7 +1308,7 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { _subscriptions.add(domElement.onBlur.listen((_) { html.Element? activeElement = html.document.activeElement; bool domElementIsActive = activeElement != domElement; - if (domElementIsActive && _inputConfiguration.forceCloseConnectionOnBlur) { + if (domElementIsActive && _inputConfiguration.forceSubmitOnFocusLost) { owner.sendTextConnectionClosedToFrameworkIfAny(); } else { _postponeFocus(); diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index cd2e90c9bdf20..c0a88e705debd 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -479,7 +479,7 @@ void testMain() { SemanticsObject(5, null), HybridTextEditing(), testInputElement); final InputConfiguration config = InputConfiguration( inputType: EngineInputType.text, - forceCloseConnectionOnBlur: true, + forceSubmitOnFocusLost: true, ); expect(document.activeElement, document.body); @@ -2305,7 +2305,7 @@ Map createFlutterConfig( String inputType, { bool readOnly = false, bool obscureText = false, - bool forceCloseConnectionOnBlur = false, + bool forceSubmitOnFocusLost = false, bool autocorrect = true, String textCapitalization = 'TextCapitalization.none', String inputAction, @@ -2320,7 +2320,7 @@ Map createFlutterConfig( }, 'readOnly': readOnly, 'obscureText': obscureText, - 'forceCloseConnectionOnBlur': forceCloseConnectionOnBlur, + 'forceSubmitOnFocusLost': forceSubmitOnFocusLost, 'autocorrect': autocorrect, 'inputAction': inputAction ?? 'TextInputAction.done', 'textCapitalization': textCapitalization,