@@ -27,18 +27,20 @@ final class ViewFocusBinding {
2727 StreamSubscription <int >? _onViewCreatedListener;
2828
2929 void init () {
30+ // We need a global listener here to know if the user was pressing "shift"
31+ // when the Flutter view receives focus, to move the Flutter focus to the
32+ // *last* focusable element.
3033 domDocument.body? .addEventListener (_keyDown, _handleKeyDown);
3134 domDocument.body? .addEventListener (_keyUp, _handleKeyUp);
32- domDocument.body? .addEventListener (_focusin, _handleFocusin);
33- domDocument.body? .addEventListener (_focusout, _handleFocusout);
35+
36+ // If so, update `_handleViewCreated` and add a `_handleViewDisposed` to attach
37+ // and remove the focus/blur listener.
3438 _onViewCreatedListener = _viewManager.onViewCreated.listen (_handleViewCreated);
3539 }
3640
3741 void dispose () {
3842 domDocument.body? .removeEventListener (_keyDown, _handleKeyDown);
3943 domDocument.body? .removeEventListener (_keyUp, _handleKeyUp);
40- domDocument.body? .removeEventListener (_focusin, _handleFocusin);
41- domDocument.body? .removeEventListener (_focusout, _handleFocusout);
4244 _onViewCreatedListener? .cancel ();
4345 }
4446
@@ -48,13 +50,14 @@ final class ViewFocusBinding {
4850 }
4951 final DomElement ? viewElement = _viewManager[viewId]? .dom.rootElement;
5052
51- if (state == ui.ViewFocusState .focused) {
52- // Only move the focus to the flutter view if nothing inside it is focused already.
53- if (viewId != _viewId (domDocument.activeElement)) {
54- viewElement? .focusWithoutScroll ();
55- }
56- } else {
57- viewElement? .blur ();
53+ switch (state) {
54+ case ui.ViewFocusState .focused:
55+ // Only move the focus to the flutter view if nothing inside it is focused already.
56+ if (viewId != _viewId (domDocument.activeElement)) {
57+ viewElement? .focusWithoutScroll ();
58+ }
59+ case ui.ViewFocusState .unfocused:
60+ viewElement? .blur ();
5861 }
5962 }
6063
@@ -115,8 +118,8 @@ final class ViewFocusBinding {
115118 direction: _viewFocusDirection,
116119 );
117120 }
118- _maybeMarkViewAsFocusable (_lastViewId, reachableByKeyboard : true );
119- _maybeMarkViewAsFocusable (viewId, reachableByKeyboard : false );
121+ _updateViewKeyboardReachability (_lastViewId, reachable : true );
122+ _updateViewKeyboardReachability (viewId, reachable : false );
120123 _lastViewId = viewId;
121124 _onViewFocusChange (event);
122125 }
@@ -127,29 +130,32 @@ final class ViewFocusBinding {
127130 }
128131
129132 void _handleViewCreated (int viewId) {
130- _maybeMarkViewAsFocusable (viewId, reachableByKeyboard: true );
133+ final DomElement ? rootElement = _viewManager[viewId]? .dom.rootElement;
134+
135+ rootElement? .addEventListener (_focusin, _handleFocusin);
136+ rootElement? .addEventListener (_focusout, _handleFocusout);
137+
138+ _updateViewKeyboardReachability (viewId, reachable: true );
131139 }
132140
133- void _maybeMarkViewAsFocusable (
141+ // Controls whether the Flutter view identified by [viewId] is reachable by
142+ // keyboard.
143+ void _updateViewKeyboardReachability (
134144 int ? viewId, {
135- required bool reachableByKeyboard ,
145+ required bool reachable ,
136146 }) {
137147 if (viewId == null ) {
138148 return ;
139149 }
140150
141151 final DomElement ? rootElement = _viewManager[viewId]? .dom.rootElement;
142- if (EngineSemantics .instance.semanticsEnabled) {
143- rootElement? .removeAttribute ('tabindex' );
144- } else {
145- // A tabindex with value zero means the DOM element can be reached by using
146- // the keyboard (tab, shift + tab). When its value is -1 it is still focusable
147- // but can't be focused by the result of keyboard events This is specially
148- // important when the semantics tree is enabled as it puts DOM nodes inside
149- // the flutter view and having it with a zero tabindex messes the focus
150- // traversal order when pressing tab or shift tab.
151- rootElement? .setAttribute ('tabindex' , reachableByKeyboard ? 0 : - 1 );
152- }
152+ // A tabindex with value zero means the DOM element can be reached using the
153+ // keyboard (tab, shift + tab). When its value is -1 it is still focusable
154+ // but can't be focused as the result of keyboard events. This is specially
155+ // important when the semantics tree is enabled as it puts DOM nodes inside
156+ // the flutter view and having it with a zero tabindex messes the focus
157+ // traversal order when pressing tab or shift tab.
158+ rootElement? .setAttribute ('tabindex' , reachable ? 0 : - 1 );
153159 }
154160
155161 static const String _focusin = 'focusin' ;
0 commit comments