Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ extension DomHTMLDocumentExtension on DomHTMLDocument {
@JS('visibilityState')
external JSString get _visibilityState;
String get visibilityState => _visibilityState.toDart;

@JS('hasFocus')
external JSBoolean _hasFocus();
bool hasFocus() => _hasFocus().toDart;
}

@JS('document')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ final class ViewFocusBinding {
});

late final DomEventListener _handleFocusout = createDomEventListener((DomEvent event) {
// During focusout processing, activeElement typically points to <body />.
// However, if an element is focused during a blur event, activeElement points to that focused element.
// We leverage this behavior to ignore focusout events where the document has focus but activeElement is not <body />.
//
// Refer to https://github.com/flutter/engine/pull/54965 for more info.
if (domDocument.hasFocus() && domDocument.activeElement != domDocument.body) {
Comment thread
tugorez marked this conversation as resolved.
Outdated
return;
}

event as DomFocusEvent;
_handleFocusChange(event.relatedTarget as DomElement?);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,30 @@ void testMain() {
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
});

test('works even if focus is changed in the middle of a blur call', () {
final DomElement input1 = createDomElement('input');
final DomElement input2 = createDomElement('input');
final EngineFlutterView view = createAndRegisterView(dispatcher);
final DomEventListener focusInput1Listener = createDomEventListener((DomEvent event) {
input1.focusWithoutScroll();
});

view.dom.rootElement.append(input1);
view.dom.rootElement.append(input2);

input1.addEventListener('blur', focusInput1Listener);
input1.focusWithoutScroll();
// The event handler above should move the focus back to input1.
input2.focusWithoutScroll();
input1.removeEventListener('blur', focusInput1Listener);

expect(dispatchedViewFocusEvents, hasLength(1));

expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
});
});
}

Expand Down