Skip to content

Commit

Permalink
Implement PlatformDispatcher.requestViewFocusChange on web. (flutter#…
Browse files Browse the repository at this point in the history
…50535)

Implement PlatformDispatcher.requestViewFocusChange on web. 

Relevant Issues are:

* Design doc: https://flutter.dev/go/focus-management 
* Focus in web multiview: flutter/flutter#137443
* Platform dispatcher changes: flutter#49841

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
tugorez authored Mar 13, 2024
1 parent 67e6328 commit cb82c6c
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
required ui.ViewFocusState state,
required ui.ViewFocusDirection direction,
}) {
// TODO(tugorez): implement this method. At the moment will be a no op call.
_viewFocusBinding.changeViewFocus(viewId, state);
}

/// A set of views which have rendered in the current `onBeginFrame` or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ final class ViewFocusBinding {
_onViewCreatedListener?.cancel();
}

void changeViewFocus(int viewId, ui.ViewFocusState state) {
final DomElement? viewElement = _viewManager[viewId]?.dom.rootElement;

if (state == ui.ViewFocusState.focused) {
// Only move the focus to the flutter view if nothing inside it is focused already.
if (viewId != _viewId(domDocument.activeElement)) {
viewElement?.focus();
}
} else {
viewElement?.blur();
}
}

late final DomEventListener _handleFocusin = createDomEventListener((DomEvent event) {
event as DomFocusEvent;
_handleFocusChange(event.target as DomElement?);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,108 @@ void testMain() {
expect(dispatchedViewFocusEvents[2].state, ui.ViewFocusState.unfocused);
expect(dispatchedViewFocusEvents[2].direction, ui.ViewFocusDirection.undefined);
});

test('requestViewFocusChange focuses the view', () {
final EngineFlutterView view = createAndRegisterView(dispatcher);

dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);

expect(domDocument.activeElement, view.dom.rootElement);

expect(dispatchedViewFocusEvents, hasLength(1));

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

test('requestViewFocusChange blurs the view', () {
final EngineFlutterView view = createAndRegisterView(dispatcher);

dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);

dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.unfocused,
direction: ui.ViewFocusDirection.undefined,
);

expect(domDocument.activeElement, isNot(view.dom.rootElement));

expect(dispatchedViewFocusEvents, hasLength(2));

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

expect(dispatchedViewFocusEvents[1].viewId, view.viewId);
expect(dispatchedViewFocusEvents[1].state, ui.ViewFocusState.unfocused);
expect(dispatchedViewFocusEvents[1].direction, ui.ViewFocusDirection.undefined);
});

test('requestViewFocusChange does nothing if the view does not exist', () {
final EngineFlutterView view = createAndRegisterView(dispatcher);

dispatcher.requestViewFocusChange(
viewId: 5094555,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);

expect(domDocument.activeElement, isNot(view.dom.rootElement));
expect(dispatchedViewFocusEvents, isEmpty);
});

test('requestViewFocusChange does nothing if the view is already focused', () {
final EngineFlutterView view = createAndRegisterView(dispatcher);

dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);
dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);

expect(dispatchedViewFocusEvents, hasLength(1));

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

test('requestViewFocusChange does not move the focus to the view', () {
final DomElement input = createDomElement('input');
final EngineFlutterView view = createAndRegisterView(dispatcher);

view.dom.rootElement.append(input);
input.focus();

dispatcher.requestViewFocusChange(
viewId: view.viewId,
state: ui.ViewFocusState.focused,
direction: ui.ViewFocusDirection.forward,
);

expect(domDocument.activeElement, input);

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

0 comments on commit cb82c6c

Please sign in to comment.