Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/pointer_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
_convertEventsToPointerData(data: pointerData, event: event, details: down);
_callback(event, pointerData);

if (event.target == _viewTarget) {
if (event.target == _viewTarget && !EngineSemantics.instance.semanticsEnabled) {
// Ensure smooth focus transitions between text fields within the Flutter view.
// Without preventing the default and this delay, the engine may not have fully
// rendered the next input element, leading to the focus incorrectly returning to
Expand Down
10 changes: 0 additions & 10 deletions lib/web_ui/lib/src/engine/semantics/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,6 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy {
}
subscriptions.clear();
lastEditingState = null;

// If the text element still has focus, remove focus from the editable
// element to cause the on-screen keyboard, if any, to hide (e.g. on iOS,
// Android).
// Otherwise, the keyboard stays on screen even when the user navigates to
// a different screen (e.g. by hitting the "back" button).
// Keep this consistent with how DefaultTextEditingStrategy does it. As of
// right now, the only difference is that semantic text fields do not
// participate in form autofill.
DefaultTextEditingStrategy.scheduleFocusFlutterView(activeDomElement, activeDomElementView);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextEditingStrategy.disable is used by TextInput.clearClient, TextInput.hide, and in one case by TextInput.setClient. The focus move (which previously was just domElement.blur()) seems to have the intention to close the virtual keyboard when the framework demands it. If we do not do anything here, does it mean that there are situations when the framework would detach and move onto the next widget, but the browser will stay focused on the text field and virtual keyboard showing? I remember we had bugs where the virtual keyboard would show up, but typing into it would not enter any text, because the <input> is not connected to the framework.

Copy link
Contributor Author

@tugorez tugorez Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great point Yegor!

If I understand correctly, the deactivated call suggests either app transitioned "flutter" focus to another widget, shifting the "browser" focus, or the user interacted outside the app, causing it to lose focus. In both cases the focus will be moved away from the text input causing the keyboard to be hidding. Is there a use case I might be missing?

domElement = null;
activeTextField = null;
_queuedStyle = null;
Expand Down
19 changes: 11 additions & 8 deletions lib/web_ui/test/engine/semantics/text_field_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:typed_data';

import 'package:test/bootstrap/browser.dart';
Expand Down Expand Up @@ -159,7 +158,7 @@ void testMain() {
// TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
}, skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox);

test('Syncs semantic state from framework', () async {
test('Syncs semantic state from framework', () {
expect(
owner().semanticsHost.ownerDocument?.activeElement, domDocument.body);

Expand Down Expand Up @@ -223,10 +222,12 @@ void testMain() {
owner().semanticsHost.ownerDocument?.activeElement,
textField.editableElement,
);
await Future<void>.delayed(Duration.zero);

// When semantics are enabled, deactivation shouldn't move focus or blur the
// editable element. Instead the engine/browser should move it accordingly.
expect(
owner().semanticsHost.ownerDocument?.activeElement,
EnginePlatformDispatcher.instance.implicitView!.dom.rootElement,
textField.editableElement,
);

// There was no user interaction with the <input> element,
Expand Down Expand Up @@ -336,8 +337,7 @@ void testMain() {
strategy.disable();
});

test('Does not dispose and recreate dom elements in persistent mode',
() async {
test('Does not dispose and recreate dom elements in persistent mode', () {
strategy.enable(
singlelineConfig,
onChange: (_, __) {},
Expand All @@ -362,12 +362,15 @@ void testMain() {
// It doesn't remove the DOM element.
final textField = textFieldSemantics.semanticRole! as SemanticTextField;
expect(owner().semanticsHost.contains(textField.editableElement), isTrue);

// Editing element is not enabled.
expect(strategy.isEnabled, isFalse);
await Future<void>.delayed(Duration.zero);

// When semantics are enabled, deactivation shouldn't move focus or blur the
// editable element. Instead the engine/browser should move it accordingly.
expect(
owner().semanticsHost.ownerDocument?.activeElement,
EnginePlatformDispatcher.instance.implicitView!.dom.rootElement,
textField.editableElement,
);
});

Expand Down