Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ecebe3c

Browse files
committed
Add a safelyBlurElement util.
1 parent 0462d19 commit ecebe3c

File tree

4 files changed

+39
-36
lines changed

4 files changed

+39
-36
lines changed

lib/web_ui/lib/src/engine/semantics/semantics.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1928,7 +1928,13 @@ class SemanticsObject {
19281928
void dispose() {
19291929
assert(!_isDisposed);
19301930
_isDisposed = true;
1931-
element.remove();
1931+
1932+
EnginePlatformDispatcher.instance.viewManager.safelyBlurElement(
1933+
element,
1934+
delayed: false,
1935+
removeElement: true,
1936+
);
1937+
19321938
_parent = null;
19331939
semanticRole?.dispose();
19341940
semanticRole = null;

lib/web_ui/lib/src/engine/semantics/text_field.dart

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,7 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy {
114114
}
115115
subscriptions.clear();
116116
lastEditingState = null;
117-
118-
// If the text element still has focus, remove focus from the editable
119-
// element to cause the on-screen keyboard, if any, to hide (e.g. on iOS,
120-
// Android).
121-
// Otherwise, the keyboard stays on screen even when the user navigates to
122-
// a different screen (e.g. by hitting the "back" button).
123-
// Keep this consistent with how DefaultTextEditingStrategy does it. As of
124-
// right now, the only difference is that semantic text fields do not
125-
// participate in form autofill.
126-
DefaultTextEditingStrategy.scheduleFocusFlutterView(activeDomElement, activeDomElementView);
117+
EnginePlatformDispatcher.instance.viewManager.safelyBlurElement(activeDomElement);
127118
domElement = null;
128119
activeTextField = null;
129120
_queuedStyle = null;

lib/web_ui/lib/src/engine/text_editing/text_editing.dart

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,9 +1411,9 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
14111411
inputConfiguration.autofillGroup?.formElement != null) {
14121412
_styleAutofillElements(activeDomElement, isOffScreen: true);
14131413
inputConfiguration.autofillGroup?.storeForm();
1414-
scheduleFocusFlutterView(activeDomElement, activeDomElementView);
1414+
EnginePlatformDispatcher.instance.viewManager.safelyBlurElement(activeDomElement);
14151415
} else {
1416-
scheduleFocusFlutterView(activeDomElement, activeDomElementView, removeElement: true);
1416+
EnginePlatformDispatcher.instance.viewManager.safelyBlurElement(activeDomElement, removeElement: true);
14171417
}
14181418
domElement = null;
14191419
}
@@ -1573,29 +1573,6 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
15731573
void moveFocusToActiveDomElement() {
15741574
activeDomElement.focusWithoutScroll();
15751575
}
1576-
1577-
/// Move the focus to the given [EngineFlutterView] in the next timer event.
1578-
///
1579-
/// The timer gives the engine the opportunity to focus on another element.
1580-
/// Shifting focus immediately can cause the keyboard to jump.
1581-
static void scheduleFocusFlutterView(
1582-
DomElement element,
1583-
EngineFlutterView? view, {
1584-
bool removeElement = false,
1585-
}) {
1586-
Timer(Duration.zero, () {
1587-
// If by the time the timer fired the focused element is no longer the
1588-
// editing element whose editing session was disabled, there's no need to
1589-
// move the focus, as it is likely that another widget already took the
1590-
// focus.
1591-
if (element == domDocument.activeElement) {
1592-
view?.dom.rootElement.focusWithoutScroll();
1593-
}
1594-
if (removeElement) {
1595-
element.remove();
1596-
}
1597-
});
1598-
}
15991576
}
16001577

16011578
/// IOS/Safari behaviour for text editing.

lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,35 @@ class FlutterViewManager {
113113
return viewId == null ? null : _viewData[viewId];
114114
}
115115

116+
/// Safely manages focus when blurring and optionally removing a DOM element.
117+
///
118+
/// This function ensures the blur operation doesn't disrupt the framework's view focus management.
119+
///
120+
/// * [removeElement] controls whether the element is removed from the DOM after being blurred.
121+
/// * [delayed] controls whether the engine will be given the opportunity to focus on another element first.
122+
void safelyBlurElement(DomElement element, {bool removeElement = false, bool delayed = true}) {
123+
final EngineFlutterView? view = findViewForElement(element);
124+
125+
void blur() {
126+
// If by the time the timer fired the focused element is no longer the
127+
// editing element whose editing session was disabled, there's no need to
128+
// move the focus, as it is likely that another widget already took the
129+
// focus.
130+
if (element == domDocument.activeElement) {
131+
view?.dom.rootElement.focusWithoutScroll();
132+
}
133+
if (removeElement) {
134+
element.remove();
135+
}
136+
}
137+
138+
if (delayed) {
139+
Timer(Duration.zero, blur);
140+
} else {
141+
blur();
142+
}
143+
}
144+
116145
void dispose() {
117146
// We need to call `toList()` in order to avoid concurrent modification
118147
// inside the loop.

0 commit comments

Comments
 (0)