FocusManager, stored in the
WidgetsBinding, tracks the currently focused node and the most recent node to request focus. It handles updating focus to the new primary (if any) and maintaining the consistency of the focus tree by sending appropriate notifications. The manager also bubbles raw keyboard events up from the focused node and tracks the current highlight mode.
FocusTraversalPolicydictates how focus moves within a focus scope. Traversal can happen in a
TraversalDirection.up) or to the next, previous, or first node (i.e., the node to receive focus when nothing else is focused). All traversal is limited to the node’s closest enclosing scope. The default traversal policy is the
ReadingOrderTraversalPolicybut can be overridden using the
FocusNode.skipTraversalcan be used to allow a node to be focusable without being eligible for traversal.
FocusAttachmentis a helper used to attach, detach, and reparent a focus node as it moves around the focus tree, typically in response to changes to the underlying widget tree. As such, this ensures that the node’s focus parent is associated with a context (
BuildContext) that is an ancestor of its own context.
Theattachment instance delegates to the underlying focus node using inherited widgets to set defaults -- i.e., when reparenting,
FocusScope.ofare used to find the node’s new parent.
FocusNoderepresents a discrete focusable element within the UI. Focus nodes can request focus, discard focus, respond to focus changes, and receive keyboard events when focused. Focus nodes are grouped into collections using scopes which are themselves a type of focus node. Conceptually, focus nodes are entities in the UI that can receive focus whereas focus scopes allow focus to shift amongst a group of descendant nodes. As program state, focus nodes must be associated with a
FocusScopeNodeis a focus node subclass that organizes focus nodes into a traversable group. Conceptually, a scope corresponds to the subtree (including nested scopes) rooted at the focus scope node. Descendent nodes add themselves to the nearest enclosing scope when receiving focus (
FocusNode._removeChildto ensure scopes forget nodes that have moved). Focus scopes maintain a history stack with the top corresponding to the most recently focused node. If a child is removed, the last focused scope becomes the focused child; this process will not update the primary focus.
Focus.ofestablishes an inherited relationship causing the dependant widget to be rebuilt when focus changes. Autofocus allows the node to request focus within the enclosing scope if no other node is already focused.
FocusScopeis the same as above, but manages a focus scope node.
FocusHighlightStrategydetermine how focusable widgets respond to focus with respect to highlighting. By default the highlight mode is updated automatically by tracking whether the last interaction was touch-based. The highlight mode is either
FocusHighlightMode.traditional, indicating that all controls are eligible, and
FocusHighlightMode.touch, indicating that only those controls that summon the soft keyboard are eligible.
FocusNode.unfocus, which would remove it from the enclosing scope’s stack of recently focused nodes, also ensuring that a pending update doesn’t refocus the node.
FocusScopeNode.focusedChild) that would receive focus if it were.
FocusNodeis anchored to the correct parent when the focus tree changes (e.g., because the widget tree rebuilds). This ensures that the associated build contexts are consistent with the actual widget tree.
Stateinstance. When state is initialized (
State.initState), the focus node is attached to the current
FocusNode.attach), returning a
State.didChangeDependencies), the attachment must be updated via
FocusAttachment.reparent. If a widget is configured to use a new focus node (
State.didUpdateWidget), the previous attachment must be detached (
FocusAttachment.detach, which calls
FocusNode.unfocusas needed) before attaching the new node. Finally, the focus node must be disposed when the host state is itself disposed (
FocusNode.dispose, which calls
FocusScope.ofto locate the nearest parent node. If the node being reparented previous had focus, focus is restored through the new path via
FocusManagertracks the root focus scope as well as the current (primary) and requesting (next) focus nodes. It also maintains a list of dirty nodes that require update notifications.
FocusNode.requestFocus), the manager is notified that the node is dirty (
FocusNode._markAsDirty) and that a focus update is needed (
FocusManager._markNeedsUpdate), passing the requesting node. This sets the next focus node and schedules a microtask to actually update focus. This can delay focus updates by up to one frame.
FocusManager._applyFocusChange), marking all nodes from the root to the incoming and outgoing nodes, inclusive, as being dirty. Then, all dirty nodes are notified that focus has changed (
FocusNode._notify) and marked clean.
FocusNode.requestFocus), every enclosing scope is updated to focus toward the requesting node directly or via an intermediate scope (
FocusNode._setAsFocusedChild). The node is then marked dirty (
FocusNode._markAsDirty) which updates the manager’s next node and requests an update.
FocusScopeNode.requestFocus), the scope tree is traversed to find the first descendent node that isn’t a scope. Once found, that node is focused. Otherwise, the deepest scope is focused.
FocusManager._applyFocusChange, promoting the next node to the primary and sending notifications to all dirty nodes.
FocusTraversalPolicy.findFirstFocus) when navigating.
FrameManager._willDisposeFocusNode) which calls
FrameManager._willUnfocusNode, as above. Finally, it is detached.
FocusNode.requestFocus) or assigned automatically due to another node losing focus.
FocusNode.consumeKeyboardToken), which returns its value and sets it to false. This ensures that the keyboard is shown exactly once in response to explicit user interaction.