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

Commit cb82c6c

Browse files
authored
Implement PlatformDispatcher.requestViewFocusChange on web. (#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: #49841 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 67e6328 commit cb82c6c

File tree

3 files changed

+116
-1
lines changed

3 files changed

+116
-1
lines changed

lib/web_ui/lib/src/engine/platform_dispatcher.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
257257
required ui.ViewFocusState state,
258258
required ui.ViewFocusDirection direction,
259259
}) {
260-
// TODO(tugorez): implement this method. At the moment will be a no op call.
260+
_viewFocusBinding.changeViewFocus(viewId, state);
261261
}
262262

263263
/// A set of views which have rendered in the current `onBeginFrame` or

lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ final class ViewFocusBinding {
3434
_onViewCreatedListener?.cancel();
3535
}
3636

37+
void changeViewFocus(int viewId, ui.ViewFocusState state) {
38+
final DomElement? viewElement = _viewManager[viewId]?.dom.rootElement;
39+
40+
if (state == ui.ViewFocusState.focused) {
41+
// Only move the focus to the flutter view if nothing inside it is focused already.
42+
if (viewId != _viewId(domDocument.activeElement)) {
43+
viewElement?.focus();
44+
}
45+
} else {
46+
viewElement?.blur();
47+
}
48+
}
49+
3750
late final DomEventListener _handleFocusin = createDomEventListener((DomEvent event) {
3851
event as DomFocusEvent;
3952
_handleFocusChange(event.target as DomElement?);

lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,108 @@ void testMain() {
164164
expect(dispatchedViewFocusEvents[2].state, ui.ViewFocusState.unfocused);
165165
expect(dispatchedViewFocusEvents[2].direction, ui.ViewFocusDirection.undefined);
166166
});
167+
168+
test('requestViewFocusChange focuses the view', () {
169+
final EngineFlutterView view = createAndRegisterView(dispatcher);
170+
171+
dispatcher.requestViewFocusChange(
172+
viewId: view.viewId,
173+
state: ui.ViewFocusState.focused,
174+
direction: ui.ViewFocusDirection.forward,
175+
);
176+
177+
expect(domDocument.activeElement, view.dom.rootElement);
178+
179+
expect(dispatchedViewFocusEvents, hasLength(1));
180+
181+
expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
182+
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
183+
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
184+
});
185+
186+
test('requestViewFocusChange blurs the view', () {
187+
final EngineFlutterView view = createAndRegisterView(dispatcher);
188+
189+
dispatcher.requestViewFocusChange(
190+
viewId: view.viewId,
191+
state: ui.ViewFocusState.focused,
192+
direction: ui.ViewFocusDirection.forward,
193+
);
194+
195+
dispatcher.requestViewFocusChange(
196+
viewId: view.viewId,
197+
state: ui.ViewFocusState.unfocused,
198+
direction: ui.ViewFocusDirection.undefined,
199+
);
200+
201+
expect(domDocument.activeElement, isNot(view.dom.rootElement));
202+
203+
expect(dispatchedViewFocusEvents, hasLength(2));
204+
205+
expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
206+
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
207+
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
208+
209+
expect(dispatchedViewFocusEvents[1].viewId, view.viewId);
210+
expect(dispatchedViewFocusEvents[1].state, ui.ViewFocusState.unfocused);
211+
expect(dispatchedViewFocusEvents[1].direction, ui.ViewFocusDirection.undefined);
212+
});
213+
214+
test('requestViewFocusChange does nothing if the view does not exist', () {
215+
final EngineFlutterView view = createAndRegisterView(dispatcher);
216+
217+
dispatcher.requestViewFocusChange(
218+
viewId: 5094555,
219+
state: ui.ViewFocusState.focused,
220+
direction: ui.ViewFocusDirection.forward,
221+
);
222+
223+
expect(domDocument.activeElement, isNot(view.dom.rootElement));
224+
expect(dispatchedViewFocusEvents, isEmpty);
225+
});
226+
227+
test('requestViewFocusChange does nothing if the view is already focused', () {
228+
final EngineFlutterView view = createAndRegisterView(dispatcher);
229+
230+
dispatcher.requestViewFocusChange(
231+
viewId: view.viewId,
232+
state: ui.ViewFocusState.focused,
233+
direction: ui.ViewFocusDirection.forward,
234+
);
235+
dispatcher.requestViewFocusChange(
236+
viewId: view.viewId,
237+
state: ui.ViewFocusState.focused,
238+
direction: ui.ViewFocusDirection.forward,
239+
);
240+
241+
expect(dispatchedViewFocusEvents, hasLength(1));
242+
243+
expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
244+
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
245+
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
246+
});
247+
248+
test('requestViewFocusChange does not move the focus to the view', () {
249+
final DomElement input = createDomElement('input');
250+
final EngineFlutterView view = createAndRegisterView(dispatcher);
251+
252+
view.dom.rootElement.append(input);
253+
input.focus();
254+
255+
dispatcher.requestViewFocusChange(
256+
viewId: view.viewId,
257+
state: ui.ViewFocusState.focused,
258+
direction: ui.ViewFocusDirection.forward,
259+
);
260+
261+
expect(domDocument.activeElement, input);
262+
263+
expect(dispatchedViewFocusEvents, hasLength(1));
264+
265+
expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
266+
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
267+
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
268+
});
167269
});
168270
}
169271

0 commit comments

Comments
 (0)