1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6import 'dart:ui';
7
8import 'package:flutter/foundation.dart';
9import 'package:flutter/gestures.dart';
10import 'package:flutter/painting.dart';
11import 'package:flutter/scheduler.dart';
12import 'package:flutter/services.dart';
13
14import 'binding.dart';
15import 'focus_scope.dart';
16import 'focus_traversal.dart';
17import 'framework.dart';
18
19/// Setting to true will cause extensive logging to occur when focus changes occur.
20///
21/// Can be used to debug focus issues: each time the focus changes, the focus
22/// tree will be printed and requests for focus and other focus operations will
23/// be logged.
24bool debugFocusChanges = false;
25
26// When using _focusDebug, always call it like so:
27//
28// assert(_focusDebug(() => 'Blah $foo'));
29//
30// It needs to be inside the assert in order to be removed in release mode, and
31// it needs to use a closure to generate the string in order to avoid string
32// interpolation when debugFocusChanges is false.
33//
34// It will throw a StateError if you try to call it when the app is in release
35// mode.
36bool _focusDebug(
37 String Function() messageFunc, [
38 Iterable<Object> Function()? detailsFunc,
39]) {
40 if (kReleaseMode) {
41 throw StateError(
42 '_focusDebug was called in Release mode. It should always be wrapped in '
43 'an assert. Always call _focusDebug like so:\n'
44 r" assert(_focusDebug(() => 'Blah $foo'));"
45 );
46 }
47 if (!debugFocusChanges) {
48 return true;
49 }
50 debugPrint('FOCUS: ${messageFunc()}');
51 final Iterable<Object> details = detailsFunc?.call() ?? const <Object>[];
52 if (details.isNotEmpty) {
53 for (final Object detail in details) {
54 debugPrint(' $detail');
55 }
56 }
57 // Return true so that it can be used inside of an assert.
58 return true;
59}
60
61/// An enum that describes how to handle a key event handled by a
62/// [FocusOnKeyCallback] or [FocusOnKeyEventCallback].
63enum KeyEventResult {
64 /// The key event has been handled, and the event should not be propagated to
65 /// other key event handlers.
66 handled,
67 /// The key event has not been handled, and the event should continue to be
68 /// propagated to other key event handlers, even non-Flutter ones.
69 ignored,
70 /// The key event has not been handled, but the key event should not be
71 /// propagated to other key event handlers.
72 ///
73 /// It will be returned to the platform embedding to be propagated to text
74 /// fields and non-Flutter key event handlers on the platform.
75 skipRemainingHandlers,
76}
77
78/// Combine the results returned by multiple [FocusOnKeyCallback]s or
79/// [FocusOnKeyEventCallback]s.
80///
81/// If any callback returns [KeyEventResult.handled], the node considers the
82/// message handled; otherwise, if any callback returns
83/// [KeyEventResult.skipRemainingHandlers], the node skips the remaining
84/// handlers without preventing the platform to handle; otherwise the node is
85/// ignored.
86KeyEventResult combineKeyEventResults(Iterable<KeyEventResult> results) {
87 bool hasSkipRemainingHandlers = false;
88 for (final KeyEventResult result in results) {
89 switch (result) {
90 case KeyEventResult.handled:
91 return KeyEventResult.handled;
92 case KeyEventResult.skipRemainingHandlers:
93 hasSkipRemainingHandlers = true;
94 case KeyEventResult.ignored:
95 break;
96 }
97 }
98 return hasSkipRemainingHandlers ?
99 KeyEventResult.skipRemainingHandlers :
100 KeyEventResult.ignored;
101}
102
103/// Signature of a callback used by [Focus.onKey] and [FocusScope.onKey]
104/// to receive key events.
105///
106/// This kind of callback is deprecated and will be removed at a future date.
107/// Use [FocusOnKeyEventCallback] and associated APIs instead.
108///
109/// The [node] is the node that received the event.
110///
111/// Returns a [KeyEventResult] that describes how, and whether, the key event
112/// was handled.
113@Deprecated(
114 'Use FocusOnKeyEventCallback instead. '
115 'This feature was deprecated after v3.18.0-2.0.pre.',
116)
117typedef FocusOnKeyCallback = KeyEventResult Function(FocusNode node, RawKeyEvent event);
118
119/// Signature of a callback used by [Focus.onKeyEvent] and [FocusScope.onKeyEvent]
120/// to receive key events.
121///
122/// The [node] is the node that received the event.
123///
124/// Returns a [KeyEventResult] that describes how, and whether, the key event
125/// was handled.
126typedef FocusOnKeyEventCallback = KeyEventResult Function(FocusNode node, KeyEvent event);
127
128/// Signature of a callback used by [FocusManager.addEarlyKeyEventHandler] and
129/// [FocusManager.addLateKeyEventHandler].
130///
131/// The `event` parameter is a [KeyEvent] that is being sent to the callback to
132/// be handled.
133///
134/// The [KeyEventResult] return value indicates whether or not the event will
135/// continue to be propagated. If the value returned is [KeyEventResult.handled]
136/// or [KeyEventResult.skipRemainingHandlers], then the event will not continue
137/// to be propagated.
138typedef OnKeyEventCallback = KeyEventResult Function(KeyEvent event);
139
140// Represents a pending autofocus request.
141@immutable
142class _Autofocus {
143 const _Autofocus({ required this.scope, required this.autofocusNode });
144
145 final FocusScopeNode scope;
146 final FocusNode autofocusNode;
147
148 // Applies the autofocus request, if the node is still attached to the
149 // original scope and the scope has no focused child.
150 //
151 // The widget tree is responsible for calling reparent/detach on attached
152 // nodes to keep their parent/manager information up-to-date, so here we can
153 // safely check if the scope/node involved in each autofocus request is
154 // still attached, and discard the ones which are no longer attached to the
155 // original manager.
156 void applyIfValid(FocusManager manager) {
157 final bool shouldApply = (scope.parent != null || identical(scope, manager.rootScope))
158 && identical(scope._manager, manager)
159 && scope.focusedChild == null
160 && autofocusNode.ancestors.contains(scope);
161 if (shouldApply) {
162 assert(_focusDebug(() => 'Applying autofocus: $autofocusNode'));
163 autofocusNode._doRequestFocus(findFirstFocus: true);
164 } else {
165 assert(_focusDebug(() => 'Autofocus request discarded for node: $autofocusNode.'));
166 }
167 }
168}
169
170/// An attachment point for a [FocusNode].
171///
172/// Using a [FocusAttachment] is rarely needed, unless building something
173/// akin to the [Focus] or [FocusScope] widgets from scratch.
174///
175/// Once created, a [FocusNode] must be attached to the widget tree by its
176/// _host_ [StatefulWidget] via a [FocusAttachment] object. [FocusAttachment]s
177/// are owned by the [StatefulWidget] that hosts a [FocusNode] or
178/// [FocusScopeNode]. There can be multiple [FocusAttachment]s for each
179/// [FocusNode], but the node will only ever be attached to one of them at a
180/// time.
181///
182/// This attachment is created by calling [FocusNode.attach], usually from the
183/// host widget's [State.initState] method. If the widget is updated to have a
184/// different focus node, then the new node needs to be attached in
185/// [State.didUpdateWidget], after calling [detach] on the previous
186/// [FocusAttachment]. Once detached, the attachment is defunct and will no
187/// longer make changes to the [FocusNode] through [reparent].
188///
189/// Without these attachment points, it would be possible for a focus node to
190/// simultaneously be attached to more than one part of the widget tree during
191/// the build stage.
192class FocusAttachment {
193 /// A private constructor, because [FocusAttachment]s are only to be created
194 /// by [FocusNode.attach].
195 FocusAttachment._(this._node);
196
197 // The focus node that this attachment manages an attachment for. The node may
198 // not yet have a parent, or may have been detached from this attachment, so
199 // don't count on this node being in a usable state.
200 final FocusNode _node;
201
202 /// Returns true if the associated node is attached to this attachment.
203 ///
204 /// It is possible to be attached to the widget tree, but not be placed in
205 /// the focus tree (i.e. to not have a parent yet in the focus tree).
206 bool get isAttached => _node._attachment == this;
207
208 /// Detaches the [FocusNode] this attachment point is associated with from the
209 /// focus tree, and disconnects it from this attachment point.
210 ///
211 /// Calling [FocusNode.dispose] will also automatically detach the node.
212 void detach() {
213 assert(_focusDebug(() => 'Detaching node:', () => <Object>[_node, 'With enclosing scope ${_node.enclosingScope}']));
214 if (isAttached) {
215 if (_node.hasPrimaryFocus || (_node._manager != null && _node._manager!._markedForFocus == _node)) {
216 _node.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
217 }
218 // This node is no longer in the tree, so shouldn't send notifications anymore.
219 _node._manager?._markDetached(_node);
220 _node._parent?._removeChild(_node);
221 _node._attachment = null;
222 assert(!_node.hasPrimaryFocus);
223 assert(_node._manager?._markedForFocus != _node);
224 }
225 assert(!isAttached);
226 }
227
228 /// Ensures that the [FocusNode] attached at this attachment point has the
229 /// proper parent node, changing it if necessary.
230 ///
231 /// If given, ensures that the given [parent] node is the parent of the node
232 /// that is attached at this attachment point, changing it if necessary.
233 /// However, it is usually not necessary to supply an explicit parent, since
234 /// [reparent] will use [Focus.of] to determine the correct parent node for
235 /// the context given in [FocusNode.attach].
236 ///
237 /// If [isAttached] is false, then calling this method does nothing.
238 ///
239 /// Should be called whenever the associated widget is rebuilt in order to
240 /// maintain the focus hierarchy.
241 ///
242 /// A [StatefulWidget] that hosts a [FocusNode] should call this method on the
243 /// node it hosts during its [State.build] or [State.didChangeDependencies]
244 /// methods in case the widget is moved from one location in the tree to
245 /// another location that has a different [FocusScope] or context.
246 ///
247 /// The optional [parent] argument must be supplied when not using [Focus] and
248 /// [FocusScope] widgets to build the focus tree, or if there is a need to
249 /// supply the parent explicitly (which are both uncommon).
250 void reparent({FocusNode? parent}) {
251 if (isAttached) {
252 assert(_node.context != null);
253 parent ??= Focus.maybeOf(_node.context!, scopeOk: true);
254 parent ??= _node.context!.owner!.focusManager.rootScope;
255 parent._reparent(_node);
256 }
257 }
258}
259
260/// Describe what should happen after [FocusNode.unfocus] is called.
261///
262/// See also:
263///
264/// * [FocusNode.unfocus], which takes this as its `disposition` parameter.
265enum UnfocusDisposition {
266 /// Focus the nearest focusable enclosing scope of this node, but do not
267 /// descend to locate the leaf [FocusScopeNode.focusedChild] the way
268 /// [previouslyFocusedChild] does.
269 ///
270 /// Focusing the scope in this way clears the [FocusScopeNode.focusedChild]
271 /// history for the enclosing scope when it receives focus. Because of this,
272 /// calling a traversal method like [FocusNode.nextFocus] after unfocusing
273 /// will cause the [FocusTraversalPolicy] to pick the node it thinks should be
274 /// first in the scope.
275 ///
276 /// This is the default disposition for [FocusNode.unfocus].
277 scope,
278
279 /// Focus the previously focused child of the nearest focusable enclosing
280 /// scope of this node.
281 ///
282 /// If there is no previously focused child, then this is equivalent to
283 /// using the [scope] disposition.
284 ///
285 /// Unfocusing with this disposition will cause [FocusNode.unfocus] to walk up
286 /// the tree to the nearest focusable enclosing scope, then start to walk down
287 /// the tree, looking for a focused child at its
288 /// [FocusScopeNode.focusedChild].
289 ///
290 /// If the [FocusScopeNode.focusedChild] is a scope, then look for its
291 /// [FocusScopeNode.focusedChild], and so on, finding the leaf
292 /// [FocusScopeNode.focusedChild] that is not a scope, or, failing that, a
293 /// leaf scope that has no focused child.
294 previouslyFocusedChild,
295}
296
297/// An object that can be used by a stateful widget to obtain the keyboard focus
298/// and to handle keyboard events.
299///
300/// _Please see the [Focus] and [FocusScope] widgets, which are utility widgets
301/// that manage their own [FocusNode]s and [FocusScopeNode]s, respectively. If
302/// they aren't appropriate, [FocusNode]s can be managed directly, but doing this
303/// is rare._
304///
305/// [FocusNode]s are persistent objects that form a _focus tree_ that is a
306/// representation of the widgets in the hierarchy that are interested in focus.
307/// A focus node might need to be created if it is passed in from an ancestor of
308/// a [Focus] widget to control the focus of the children from the ancestor, or
309/// a widget might need to host one if the widget subsystem is not being used,
310/// or if the [Focus] and [FocusScope] widgets provide insufficient control.
311///
312/// [FocusNode]s are organized into _scopes_ (see [FocusScopeNode]), which form
313/// sub-trees of nodes that restrict traversal to a group of nodes. Within a
314/// scope, the most recent nodes to have focus are remembered, and if a node is
315/// focused and then unfocused, the previous node receives focus again.
316///
317/// The focus node hierarchy can be traversed using the [parent], [children],
318/// [ancestors] and [descendants] accessors.
319///
320/// [FocusNode]s are [ChangeNotifier]s, so a listener can be registered to
321/// receive a notification when the focus changes. Listeners will also be
322/// notified when [skipTraversal], [canRequestFocus], [descendantsAreFocusable],
323/// and [descendantsAreTraversable] properties are updated. If the [Focus] and
324/// [FocusScope] widgets are being used to manage the nodes, consider
325/// establishing an [InheritedWidget] dependency on them by calling [Focus.of]
326/// or [FocusScope.of] instead. [FocusNode.hasFocus] can also be used to
327/// establish a similar dependency, especially if all that is needed is to
328/// determine whether or not the widget is focused at build time.
329///
330/// To see the focus tree in the debug console, call [debugDumpFocusTree]. To
331/// get the focus tree as a string, call [debugDescribeFocusTree].
332///
333/// {@template flutter.widgets.FocusNode.lifecycle}
334/// ## Lifecycle
335///
336/// There are several actors involved in the lifecycle of a
337/// [FocusNode]/[FocusScopeNode]. They are created and disposed by their
338/// _owner_, attached, detached, and re-parented using a [FocusAttachment] by
339/// their _host_ (which must be owned by the [State] of a [StatefulWidget]), and
340/// they are managed by the [FocusManager]. Different parts of the [FocusNode]
341/// API are intended for these different actors.
342///
343/// [FocusNode]s (and hence [FocusScopeNode]s) are persistent objects that form
344/// part of a _focus tree_ that is a sparse representation of the widgets in the
345/// hierarchy that are interested in receiving keyboard events. They must be
346/// managed like other persistent state, which is typically done by a
347/// [StatefulWidget] that owns the node. A stateful widget that owns a focus
348/// scope node must call [dispose] from its [State.dispose] method.
349///
350/// Once created, a [FocusNode] must be attached to the widget tree via a
351/// [FocusAttachment] object. This attachment is created by calling [attach],
352/// usually from the [State.initState] method. If the hosting widget is updated
353/// to have a different focus node, then the updated node needs to be attached
354/// in [State.didUpdateWidget], after calling [FocusAttachment.detach] on the
355/// previous [FocusAttachment].
356///
357/// Because [FocusNode]s form a sparse representation of the widget tree, they
358/// must be updated whenever the widget tree is rebuilt. This is done by calling
359/// [FocusAttachment.reparent], usually from the [State.build] or
360/// [State.didChangeDependencies] methods of the widget that represents the
361/// focused region, so that the [BuildContext] assigned to the [FocusScopeNode]
362/// can be tracked (the context is used to obtain the [RenderObject], from which
363/// the geometry of focused regions can be determined).
364///
365/// Creating a [FocusNode] each time [State.build] is invoked will cause the
366/// focus to be lost each time the widget is built, which is usually not desired
367/// behavior (call [unfocus] if losing focus is desired).
368///
369/// If, as is common, the hosting [StatefulWidget] is also the owner of the
370/// focus node, then it will also call [dispose] from its [State.dispose] (in
371/// which case the [FocusAttachment.detach] may be skipped, since dispose will
372/// automatically detach). If another object owns the focus node, then it must
373/// call [dispose] when the node is done being used.
374/// {@endtemplate}
375///
376/// {@template flutter.widgets.FocusNode.keyEvents}
377/// ## Key Event Propagation
378///
379/// The [FocusManager] receives key events from [HardwareKeyboard] and will pass
380/// them to the focused nodes. It starts with the node with the primary focus,
381/// and will call the [onKeyEvent] callback for that node. If the callback
382/// returns [KeyEventResult.ignored], indicating that it did not handle the
383/// event, the [FocusManager] will move to the parent of that node and call its
384/// [onKeyEvent]. If that [onKeyEvent] returns [KeyEventResult.handled], then it
385/// will stop propagating the event. If it reaches the root [FocusScopeNode],
386/// [FocusManager.rootScope], the event is discarded.
387/// {@endtemplate}
388///
389/// ## Focus Traversal
390///
391/// The term _traversal_, sometimes called _tab traversal_, refers to moving the
392/// focus from one widget to the next in a particular order (also sometimes
393/// referred to as the _tab order_, since the TAB key is often bound to the
394/// action to move to the next widget).
395///
396/// To give focus to the logical _next_ or _previous_ widget in the UI, call the
397/// [nextFocus] or [previousFocus] methods. To give the focus to a widget in a
398/// particular direction, call the [focusInDirection] method.
399///
400/// The policy for what the _next_ or _previous_ widget is, or the widget in a
401/// particular direction, is determined by the [FocusTraversalPolicy] in force.
402///
403/// The ambient policy is determined by looking up the widget hierarchy for a
404/// [FocusTraversalGroup] widget, and obtaining the focus traversal policy from
405/// it. Different focus nodes can inherit difference policies, so part of the
406/// app can go in a predefined order (using [OrderedTraversalPolicy]), and part
407/// can go in reading order (using [ReadingOrderTraversalPolicy]), depending
408/// upon the use case.
409///
410/// Predefined policies include [WidgetOrderTraversalPolicy],
411/// [ReadingOrderTraversalPolicy], [OrderedTraversalPolicy], and
412/// [DirectionalFocusTraversalPolicyMixin], but custom policies can be built
413/// based upon these policies. See [FocusTraversalPolicy] for more information.
414///
415/// {@tool dartpad}
416/// This example shows how a FocusNode should be managed if not using the
417/// [Focus] or [FocusScope] widgets. See the [Focus] widget for a similar
418/// example using [Focus] and [FocusScope] widgets.
419///
420/// ** See code in examples/api/lib/widgets/focus_manager/focus_node.0.dart **
421/// {@end-tool}
422///
423/// See also:
424///
425/// * [Focus], a widget that manages a [FocusNode] and provides access to focus
426/// information and actions to its descendant widgets.
427/// * [FocusTraversalGroup], a widget used to group together and configure the
428/// focus traversal policy for a widget subtree.
429/// * [FocusManager], a singleton that manages the primary focus and distributes
430/// key events to focused nodes.
431/// * [FocusTraversalPolicy], a class used to determine how to move the focus to
432/// other nodes.
433class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
434 /// Creates a focus node.
435 ///
436 /// The [debugLabel] is ignored on release builds.
437 ///
438 /// To receive key events that focuses on this node, pass a listener to
439 /// `onKeyEvent`.
440 FocusNode({
441 String? debugLabel,
442 @Deprecated(
443 'Use onKeyEvent instead. '
444 'This feature was deprecated after v3.18.0-2.0.pre.',
445 )
446 this.onKey,
447 this.onKeyEvent,
448 bool skipTraversal = false,
449 bool canRequestFocus = true,
450 bool descendantsAreFocusable = true,
451 bool descendantsAreTraversable = true,
452 }) : _skipTraversal = skipTraversal,
453 _canRequestFocus = canRequestFocus,
454 _descendantsAreFocusable = descendantsAreFocusable,
455 _descendantsAreTraversable = descendantsAreTraversable {
456 // Set it via the setter so that it does nothing on release builds.
457 this.debugLabel = debugLabel;
458
459 if (kFlutterMemoryAllocationsEnabled) {
460 ChangeNotifier.maybeDispatchObjectCreation(this);
461 }
462 }
463
464 /// If true, tells the focus traversal policy to skip over this node for
465 /// purposes of the traversal algorithm.
466 ///
467 /// This may be used to place nodes in the focus tree that may be focused, but
468 /// not traversed, allowing them to receive key events as part of the focus
469 /// chain, but not be traversed to via focus traversal.
470 ///
471 /// This is different from [canRequestFocus] because it only implies that the
472 /// node can't be reached via traversal, not that it can't be focused. It may
473 /// still be focused explicitly.
474 bool get skipTraversal {
475 if (_skipTraversal) {
476 return true;
477 }
478 for (final FocusNode ancestor in ancestors) {
479 if (!ancestor.descendantsAreTraversable) {
480 return true;
481 }
482 }
483 return false;
484 }
485 bool _skipTraversal;
486 set skipTraversal(bool value) {
487 if (value != _skipTraversal) {
488 _skipTraversal = value;
489 _manager?._markPropertiesChanged(this);
490 }
491 }
492
493 /// If true, this focus node may request the primary focus.
494 ///
495 /// Defaults to true. Set to false if you want this node to do nothing when
496 /// [requestFocus] is called on it.
497 ///
498 /// If set to false on a [FocusScopeNode], will cause all of the children of
499 /// the scope node to not be focusable.
500 ///
501 /// If set to false on a [FocusNode], it will not affect the focusability of
502 /// children of the node.
503 ///
504 /// The [hasFocus] member can still return true if this node is the ancestor
505 /// of a node with primary focus.
506 ///
507 /// This is different than [skipTraversal] because [skipTraversal] still
508 /// allows the node to be focused, just not traversed to via the
509 /// [FocusTraversalPolicy].
510 ///
511 /// Setting [canRequestFocus] to false implies that the node will also be
512 /// skipped for traversal purposes.
513 ///
514 /// See also:
515 ///
516 /// * [FocusTraversalGroup], a widget used to group together and configure the
517 /// focus traversal policy for a widget subtree.
518 /// * [FocusTraversalPolicy], a class that can be extended to describe a
519 /// traversal policy.
520 bool get canRequestFocus {
521 if (!_canRequestFocus) {
522 return false;
523 }
524 final FocusScopeNode? scope = enclosingScope;
525 if (scope != null && !scope.canRequestFocus) {
526 return false;
527 }
528 for (final FocusNode ancestor in ancestors) {
529 if (!ancestor.descendantsAreFocusable) {
530 return false;
531 }
532 }
533 return true;
534 }
535
536 bool _canRequestFocus;
537 @mustCallSuper
538 set canRequestFocus(bool value) {
539 if (value != _canRequestFocus) {
540 // Have to set this first before unfocusing, since it checks this to cull
541 // unfocusable, previously-focused children.
542 _canRequestFocus = value;
543 if (hasFocus && !value) {
544 unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
545 }
546 _manager?._markPropertiesChanged(this);
547 }
548 }
549
550 /// If false, will disable focus for all of this node's descendants.
551 ///
552 /// Defaults to true. Does not affect focusability of this node: for that,
553 /// use [canRequestFocus].
554 ///
555 /// If any descendants are focused when this is set to false, they will be
556 /// unfocused. When [descendantsAreFocusable] is set to true again, they will
557 /// not be refocused, although they will be able to accept focus again.
558 ///
559 /// Does not affect the value of [canRequestFocus] on the descendants.
560 ///
561 /// If a descendant node loses focus when this value is changed, the focus
562 /// will move to the scope enclosing this node.
563 ///
564 /// See also:
565 ///
566 /// * [ExcludeFocus], a widget that uses this property to conditionally
567 /// exclude focus for a subtree.
568 /// * [descendantsAreTraversable], which makes this widget's descendants
569 /// untraversable.
570 /// * [ExcludeFocusTraversal], a widget that conditionally excludes focus
571 /// traversal for a subtree.
572 /// * [Focus], a widget that exposes this setting as a parameter.
573 /// * [FocusTraversalGroup], a widget used to group together and configure
574 /// the focus traversal policy for a widget subtree that also has a
575 /// `descendantsAreFocusable` parameter that prevents its children from
576 /// being focused.
577 bool get descendantsAreFocusable => _descendantsAreFocusable;
578 bool _descendantsAreFocusable;
579 @mustCallSuper
580 set descendantsAreFocusable(bool value) {
581 if (value == _descendantsAreFocusable) {
582 return;
583 }
584 // Set _descendantsAreFocusable before unfocusing, so the scope won't try
585 // and focus any of the children here again if it is false.
586 _descendantsAreFocusable = value;
587 if (!value && hasFocus) {
588 unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
589 }
590 _manager?._markPropertiesChanged(this);
591 }
592
593 /// If false, tells the focus traversal policy to skip over for all of this
594 /// node's descendants for purposes of the traversal algorithm.
595 ///
596 /// Defaults to true. Does not affect the focus traversal of this node: for
597 /// that, use [skipTraversal].
598 ///
599 /// Does not affect the value of [FocusNode.skipTraversal] on the
600 /// descendants. Does not affect focusability of the descendants.
601 ///
602 /// See also:
603 ///
604 /// * [ExcludeFocusTraversal], a widget that uses this property to conditionally
605 /// exclude focus traversal for a subtree.
606 /// * [descendantsAreFocusable], which makes this widget's descendants
607 /// unfocusable.
608 /// * [ExcludeFocus], a widget that conditionally excludes focus for a subtree.
609 /// * [FocusTraversalGroup], a widget used to group together and configure
610 /// the focus traversal policy for a widget subtree that also has an
611 /// `descendantsAreFocusable` parameter that prevents its children from
612 /// being focused.
613 bool get descendantsAreTraversable => _descendantsAreTraversable;
614 bool _descendantsAreTraversable;
615 @mustCallSuper
616 set descendantsAreTraversable(bool value) {
617 if (value != _descendantsAreTraversable) {
618 _descendantsAreTraversable = value;
619 _manager?._markPropertiesChanged(this);
620 }
621 }
622
623 /// The context that was supplied to [attach].
624 ///
625 /// This is typically the context for the widget that is being focused, as it
626 /// is used to determine the bounds of the widget.
627 BuildContext? get context => _context;
628 BuildContext? _context;
629
630 /// Called if this focus node receives a key event while focused (i.e. when
631 /// [hasFocus] returns true).
632 ///
633 /// This property is deprecated and will be removed at a future date. Use
634 /// [onKeyEvent] instead.
635 ///
636 /// This is a legacy API based on [RawKeyEvent] and will be deprecated in the
637 /// future. Prefer [onKeyEvent] instead.
638 ///
639 /// {@macro flutter.widgets.FocusNode.keyEvents}
640 @Deprecated(
641 'Use onKeyEvent instead. '
642 'This feature was deprecated after v3.18.0-2.0.pre.',
643 )
644 FocusOnKeyCallback? onKey;
645
646 /// Called if this focus node receives a key event while focused (i.e. when
647 /// [hasFocus] returns true).
648 ///
649 /// {@macro flutter.widgets.FocusNode.keyEvents}
650 FocusOnKeyEventCallback? onKeyEvent;
651
652 FocusManager? _manager;
653 List<FocusNode>? _ancestors;
654 List<FocusNode>? _descendants;
655 bool _hasKeyboardToken = false;
656
657 /// Returns the parent node for this object.
658 ///
659 /// All nodes except for the root [FocusScopeNode] ([FocusManager.rootScope])
660 /// will be given a parent when they are added to the focus tree, which is
661 /// done using [FocusAttachment.reparent].
662 FocusNode? get parent => _parent;
663 FocusNode? _parent;
664
665 /// An iterator over the children of this node.
666 Iterable<FocusNode> get children => _children;
667 final List<FocusNode> _children = <FocusNode>[];
668
669 /// An iterator over the children that are allowed to be traversed by the
670 /// [FocusTraversalPolicy].
671 ///
672 /// Returns the list of focusable, traversable children of this node,
673 /// regardless of those settings on this focus node. Will return an empty
674 /// iterable if [descendantsAreFocusable] is false.
675 ///
676 /// See also
677 ///
678 /// * [traversalDescendants], which traverses all of the node's descendants,
679 /// not just the immediate children.
680 Iterable<FocusNode> get traversalChildren {
681 if (!descendantsAreFocusable) {
682 return const Iterable<FocusNode>.empty();
683 }
684 return children.where(
685 (FocusNode node) => !node.skipTraversal && node.canRequestFocus,
686 );
687 }
688
689 /// A debug label that is used for diagnostic output.
690 ///
691 /// Will always return null in release builds.
692 String? get debugLabel => _debugLabel;
693 String? _debugLabel;
694 set debugLabel(String? value) {
695 assert(() {
696 // Only set the value in debug builds.
697 _debugLabel = value;
698 return true;
699 }());
700 }
701
702 FocusAttachment? _attachment;
703
704 /// An [Iterable] over the hierarchy of children below this one, in
705 /// depth-first order.
706 Iterable<FocusNode> get descendants {
707 if (_descendants == null) {
708 final List<FocusNode> result = <FocusNode>[];
709 for (final FocusNode child in _children) {
710 result.addAll(child.descendants);
711 result.add(child);
712 }
713 _descendants = result;
714 }
715 return _descendants!;
716 }
717
718 /// Returns all descendants which do not have the [skipTraversal] and do have
719 /// the [canRequestFocus] flag set.
720 Iterable<FocusNode> get traversalDescendants {
721 if (!descendantsAreFocusable) {
722 return const Iterable<FocusNode>.empty();
723 }
724 return descendants.where((FocusNode node) => !node.skipTraversal && node.canRequestFocus);
725 }
726
727 /// An [Iterable] over the ancestors of this node.
728 ///
729 /// Iterates the ancestors of this node starting at the parent and iterating
730 /// over successively more remote ancestors of this node, ending at the root
731 /// [FocusScopeNode] ([FocusManager.rootScope]).
732 Iterable<FocusNode> get ancestors {
733 if (_ancestors == null) {
734 final List<FocusNode> result = <FocusNode>[];
735 FocusNode? parent = _parent;
736 while (parent != null) {
737 result.add(parent);
738 parent = parent._parent;
739 }
740 _ancestors = result;
741 }
742 return _ancestors!;
743 }
744
745 /// Whether this node has input focus.
746 ///
747 /// A [FocusNode] has focus when it is an ancestor of a node that returns true
748 /// from [hasPrimaryFocus], or it has the primary focus itself.
749 ///
750 /// The [hasFocus] accessor is different from [hasPrimaryFocus] in that
751 /// [hasFocus] is true if the node is anywhere in the focus chain, but for
752 /// [hasPrimaryFocus] the node must to be at the end of the chain to return
753 /// true.
754 ///
755 /// A node that returns true for [hasFocus] will receive key events if none of
756 /// its focused descendants returned true from their [onKey] handler.
757 ///
758 /// This object is a [ChangeNotifier], and notifies its [Listenable] listeners
759 /// (registered via [addListener]) whenever this value changes.
760 ///
761 /// See also:
762 ///
763 /// * [Focus.isAt], which is a static method that will return the focus
764 /// state of the nearest ancestor [Focus] widget's focus node.
765 bool get hasFocus => hasPrimaryFocus || (_manager?.primaryFocus?.ancestors.contains(this) ?? false);
766
767 /// Returns true if this node currently has the application-wide input focus.
768 ///
769 /// A [FocusNode] has the primary focus when the node is focused in its
770 /// nearest ancestor [FocusScopeNode] and [hasFocus] is true for all its
771 /// ancestor nodes, but none of its descendants.
772 ///
773 /// This is different from [hasFocus] in that [hasFocus] is true if the node
774 /// is anywhere in the focus chain, but here the node has to be at the end of
775 /// the chain to return true.
776 ///
777 /// A node that returns true for [hasPrimaryFocus] will be the first node to
778 /// receive key events through its [onKey] handler.
779 ///
780 /// This object notifies its listeners whenever this value changes.
781 bool get hasPrimaryFocus => _manager?.primaryFocus == this;
782
783 /// Returns the [FocusHighlightMode] that is currently in effect for this node.
784 FocusHighlightMode get highlightMode => FocusManager.instance.highlightMode;
785
786 /// Returns the nearest enclosing scope node above this node, including
787 /// this node, if it's a scope.
788 ///
789 /// Returns null if no scope is found.
790 ///
791 /// Use [enclosingScope] to look for scopes above this node.
792 FocusScopeNode? get nearestScope => enclosingScope;
793
794 /// Returns the nearest enclosing scope node above this node, or null if the
795 /// node has not yet be added to the focus tree.
796 ///
797 /// If this node is itself a scope, this will only return ancestors of this
798 /// scope.
799 ///
800 /// Use [nearestScope] to start at this node instead of above it.
801 FocusScopeNode? get enclosingScope {
802 for (final FocusNode node in ancestors) {
803 if (node is FocusScopeNode) {
804 return node;
805 }
806 }
807 return null;
808 }
809
810 /// Returns the size of the attached widget's [RenderObject], in logical
811 /// units.
812 ///
813 /// Size is the size of the transformed widget in global coordinates.
814 Size get size => rect.size;
815
816 /// Returns the global offset to the upper left corner of the attached
817 /// widget's [RenderObject], in logical units.
818 ///
819 /// Offset is the offset of the transformed widget in global coordinates.
820 Offset get offset {
821 assert(
822 context != null,
823 "Tried to get the offset of a focus node that didn't have its context set yet.\n"
824 'The context needs to be set before trying to evaluate traversal policies. '
825 'Setting the context is typically done with the attach method.',
826 );
827 final RenderObject object = context!.findRenderObject()!;
828 return MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
829 }
830
831 /// Returns the global rectangle of the attached widget's [RenderObject], in
832 /// logical units.
833 ///
834 /// Rect is the rectangle of the transformed widget in global coordinates.
835 Rect get rect {
836 assert(
837 context != null,
838 "Tried to get the bounds of a focus node that didn't have its context set yet.\n"
839 'The context needs to be set before trying to evaluate traversal policies. '
840 'Setting the context is typically done with the attach method.',
841 );
842 final RenderObject object = context!.findRenderObject()!;
843 final Offset topLeft = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
844 final Offset bottomRight = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.bottomRight);
845 return Rect.fromLTRB(topLeft.dx, topLeft.dy, bottomRight.dx, bottomRight.dy);
846 }
847
848 /// Removes the focus on this node by moving the primary focus to another node.
849 ///
850 /// This method removes focus from a node that has the primary focus, cancels
851 /// any outstanding requests to focus it, while setting the primary focus to
852 /// another node according to the `disposition`.
853 ///
854 /// It is safe to call regardless of whether this node has ever requested
855 /// focus or not. If this node doesn't have focus or primary focus, nothing
856 /// happens.
857 ///
858 /// The `disposition` argument determines which node will receive primary
859 /// focus after this one loses it.
860 ///
861 /// If `disposition` is set to [UnfocusDisposition.scope] (the default), then
862 /// the previously focused node history of the enclosing scope will be
863 /// cleared, and the primary focus will be moved to the nearest enclosing
864 /// scope ancestor that is enabled for focus, ignoring the
865 /// [FocusScopeNode.focusedChild] for that scope.
866 ///
867 /// If `disposition` is set to [UnfocusDisposition.previouslyFocusedChild],
868 /// then this node will be removed from the previously focused list in the
869 /// [enclosingScope], and the focus will be moved to the previously focused
870 /// node of the [enclosingScope], which (if it is a scope itself), will find
871 /// its focused child, etc., until a leaf focus node is found. If there is no
872 /// previously focused child, then the scope itself will receive focus, as if
873 /// [UnfocusDisposition.scope] were specified.
874 ///
875 /// If you want this node to lose focus and the focus to move to the next or
876 /// previous node in the enclosing [FocusTraversalGroup], call [nextFocus] or
877 /// [previousFocus] instead of calling [unfocus].
878 ///
879 /// {@tool dartpad}
880 /// This example shows the difference between the different [UnfocusDisposition]
881 /// values for [unfocus].
882 ///
883 /// Try setting focus on the four text fields by selecting them, and then
884 /// select "UNFOCUS" to see what happens when the current
885 /// [FocusManager.primaryFocus] is unfocused.
886 ///
887 /// Try pressing the TAB key after unfocusing to see what the next widget
888 /// chosen is.
889 ///
890 /// ** See code in examples/api/lib/widgets/focus_manager/focus_node.unfocus.0.dart **
891 /// {@end-tool}
892 void unfocus({
893 UnfocusDisposition disposition = UnfocusDisposition.scope,
894 }) {
895 if (!hasFocus && (_manager == null || _manager!._markedForFocus != this)) {
896 return;
897 }
898 FocusScopeNode? scope = enclosingScope;
899 if (scope == null) {
900 // If the scope is null, then this is either the root node, or a node that
901 // is not yet in the tree, neither of which do anything when unfocused.
902 return;
903 }
904 switch (disposition) {
905 case UnfocusDisposition.scope:
906 // If it can't request focus, then don't modify its focused children.
907 if (scope.canRequestFocus) {
908 // Clearing the focused children here prevents re-focusing the node
909 // that we just unfocused if we immediately hit "next" after
910 // unfocusing, and also prevents choosing to refocus the next-to-last
911 // focused child if unfocus is called more than once.
912 scope._focusedChildren.clear();
913 }
914
915 while (!scope!.canRequestFocus) {
916 scope = scope.enclosingScope ?? _manager?.rootScope;
917 }
918 scope._doRequestFocus(findFirstFocus: false);
919 case UnfocusDisposition.previouslyFocusedChild:
920 // Select the most recent focused child from the nearest focusable scope
921 // and focus that. If there isn't one, focus the scope itself.
922 if (scope.canRequestFocus) {
923 scope._focusedChildren.remove(this);
924 }
925 while (!scope!.canRequestFocus) {
926 scope.enclosingScope?._focusedChildren.remove(scope);
927 scope = scope.enclosingScope ?? _manager?.rootScope;
928 }
929 scope._doRequestFocus(findFirstFocus: true);
930 }
931 assert(_focusDebug(() => 'Unfocused node:', () => <Object>['primary focus was $this', 'next focus will be ${_manager?._markedForFocus}']));
932 }
933
934 /// Removes the keyboard token from this focus node if it has one.
935 ///
936 /// This mechanism helps distinguish between an input control gaining focus by
937 /// default and gaining focus as a result of an explicit user action.
938 ///
939 /// When a focus node requests the focus (either via
940 /// [FocusScopeNode.requestFocus] or [FocusScopeNode.autofocus]), the focus
941 /// node receives a keyboard token if it does not already have one. Later,
942 /// when the focus node becomes focused, the widget that manages the
943 /// [TextInputConnection] should show the keyboard (i.e. call
944 /// [TextInputConnection.show]) only if it successfully consumes the keyboard
945 /// token from the focus node.
946 ///
947 /// Returns true if this method successfully consumes the keyboard token.
948 bool consumeKeyboardToken() {
949 if (!_hasKeyboardToken) {
950 return false;
951 }
952 _hasKeyboardToken = false;
953 return true;
954 }
955
956 // Marks the node as being the next to be focused, meaning that it will become
957 // the primary focus and notify listeners of a focus change the next time
958 // focus is resolved by the manager. If something else calls _markNextFocus
959 // before then, then that node will become the next focus instead of the
960 // previous one.
961 void _markNextFocus(FocusNode newFocus) {
962 if (_manager != null) {
963 // If we have a manager, then let it handle the focus change.
964 _manager!._markNextFocus(this);
965 return;
966 }
967 // If we don't have a manager, then change the focus locally.
968 newFocus._setAsFocusedChildForScope();
969 newFocus._notify();
970 if (newFocus != this) {
971 _notify();
972 }
973 }
974
975 // Removes the given FocusNode and its children as a child of this node.
976 @mustCallSuper
977 void _removeChild(FocusNode node, {bool removeScopeFocus = true}) {
978 assert(_children.contains(node), "Tried to remove a node that wasn't a child.");
979 assert(node._parent == this);
980 assert(node._manager == _manager);
981
982 if (removeScopeFocus) {
983 final FocusScopeNode? nodeScope = node.enclosingScope;
984 if (nodeScope != null) {
985 nodeScope._focusedChildren.remove(node);
986 node.descendants.where((FocusNode descendant) {
987 return descendant.enclosingScope == nodeScope;
988 }).forEach(nodeScope._focusedChildren.remove);
989 }
990 }
991
992 node._parent = null;
993 _children.remove(node);
994 for (final FocusNode ancestor in ancestors) {
995 ancestor._descendants = null;
996 }
997 _descendants = null;
998 assert(_manager == null || !_manager!.rootScope.descendants.contains(node));
999 }
1000
1001 void _updateManager(FocusManager? manager) {
1002 _manager = manager;
1003 for (final FocusNode descendant in descendants) {
1004 descendant._manager = manager;
1005 descendant._ancestors = null;
1006 }
1007 }
1008
1009 // Used by FocusAttachment.reparent to perform the actual parenting operation.
1010 @mustCallSuper
1011 void _reparent(FocusNode child) {
1012 assert(child != this, 'Tried to make a child into a parent of itself.');
1013 if (child._parent == this) {
1014 assert(_children.contains(child), "Found a node that says it's a child, but doesn't appear in the child list.");
1015 // The child is already a child of this parent.
1016 return;
1017 }
1018 assert(_manager == null || child != _manager!.rootScope, "Reparenting the root node isn't allowed.");
1019 assert(!ancestors.contains(child), 'The supplied child is already an ancestor of this node. Loops are not allowed.');
1020 final FocusScopeNode? oldScope = child.enclosingScope;
1021 final bool hadFocus = child.hasFocus;
1022 child._parent?._removeChild(child, removeScopeFocus: oldScope != nearestScope);
1023 _children.add(child);
1024 child._parent = this;
1025 child._ancestors = null;
1026 child._updateManager(_manager);
1027 for (final FocusNode ancestor in child.ancestors) {
1028 ancestor._descendants = null;
1029 }
1030 if (hadFocus) {
1031 // Update the focus chain for the current focus without changing it.
1032 _manager?.primaryFocus?._setAsFocusedChildForScope();
1033 }
1034 if (oldScope != null && child.context != null && child.enclosingScope != oldScope) {
1035 FocusTraversalGroup.maybeOf(child.context!)?.changedScope(node: child, oldScope: oldScope);
1036 }
1037 if (child._requestFocusWhenReparented) {
1038 child._doRequestFocus(findFirstFocus: true);
1039 child._requestFocusWhenReparented = false;
1040 }
1041 }
1042
1043 /// Called by the _host_ [StatefulWidget] to attach a [FocusNode] to the
1044 /// widget tree.
1045 ///
1046 /// In order to attach a [FocusNode] to the widget tree, call [attach],
1047 /// typically from the [StatefulWidget]'s [State.initState] method.
1048 ///
1049 /// If the focus node in the host widget is swapped out, the new node will
1050 /// need to be attached. [FocusAttachment.detach] should be called on the old
1051 /// node, and then [attach] called on the new node. This typically happens in
1052 /// the [State.didUpdateWidget] method.
1053 ///
1054 /// To receive key events that focuses on this node, pass a listener to
1055 /// `onKeyEvent`.
1056 @mustCallSuper
1057 FocusAttachment attach(
1058 BuildContext? context, {
1059 FocusOnKeyEventCallback? onKeyEvent,
1060 @Deprecated(
1061 'Use onKeyEvent instead. '
1062 'This feature was deprecated after v3.18.0-2.0.pre.',
1063 )
1064 FocusOnKeyCallback? onKey,
1065 }) {
1066 _context = context;
1067 this.onKey = onKey ?? this.onKey;
1068 this.onKeyEvent = onKeyEvent ?? this.onKeyEvent;
1069 _attachment = FocusAttachment._(this);
1070 return _attachment!;
1071 }
1072
1073 @override
1074 void dispose() {
1075 // Detaching will also unfocus and clean up the manager's data structures.
1076 _attachment?.detach();
1077 super.dispose();
1078 }
1079
1080 @mustCallSuper
1081 void _notify() {
1082 if (_parent == null) {
1083 // no longer part of the tree, so don't notify.
1084 return;
1085 }
1086 if (hasPrimaryFocus) {
1087 _setAsFocusedChildForScope();
1088 }
1089 notifyListeners();
1090 }
1091
1092 /// Requests the primary focus for this node, or for a supplied [node], which
1093 /// will also give focus to its [ancestors].
1094 ///
1095 /// If called without a node, request focus for this node. If the node hasn't
1096 /// been added to the focus tree yet, then defer the focus request until it
1097 /// is, allowing newly created widgets to request focus as soon as they are
1098 /// added.
1099 ///
1100 /// If the given [node] is not yet a part of the focus tree, then this method
1101 /// will add the [node] as a child of this node before requesting focus.
1102 ///
1103 /// If the given [node] is a [FocusScopeNode] and that focus scope node has a
1104 /// non-null [FocusScopeNode.focusedChild], then request the focus for the
1105 /// focused child. This process is recursive and continues until it encounters
1106 /// either a focus scope node with a null focused child or an ordinary
1107 /// (non-scope) [FocusNode] is found.
1108 ///
1109 /// The node is notified that it has received the primary focus in a
1110 /// microtask, so notification may lag the request by up to one frame.
1111 void requestFocus([FocusNode? node]) {
1112 if (node != null) {
1113 if (node._parent == null) {
1114 _reparent(node);
1115 }
1116 assert(node.ancestors.contains(this), 'Focus was requested for a node that is not a descendant of the scope from which it was requested.');
1117 node._doRequestFocus(findFirstFocus: true);
1118 return;
1119 }
1120 _doRequestFocus(findFirstFocus: true);
1121 }
1122
1123 // This is overridden in FocusScopeNode.
1124 void _doRequestFocus({required bool findFirstFocus}) {
1125 if (!canRequestFocus) {
1126 assert(_focusDebug(() => 'Node NOT requesting focus because canRequestFocus is false: $this'));
1127 return;
1128 }
1129 // If the node isn't part of the tree, then we just defer the focus request
1130 // until the next time it is reparented, so that it's possible to focus
1131 // newly added widgets.
1132 if (_parent == null) {
1133 _requestFocusWhenReparented = true;
1134 return;
1135 }
1136 _setAsFocusedChildForScope();
1137 if (hasPrimaryFocus && (_manager!._markedForFocus == null || _manager!._markedForFocus == this)) {
1138 return;
1139 }
1140 _hasKeyboardToken = true;
1141 assert(_focusDebug(() => 'Node requesting focus: $this'));
1142 _markNextFocus(this);
1143 }
1144
1145 // If set to true, the node will request focus on this node the next time
1146 // this node is reparented in the focus tree.
1147 //
1148 // Once requestFocus has been called at the next reparenting, this value
1149 // will be reset to false.
1150 //
1151 // This will only force a call to requestFocus for the node once the next time
1152 // the node is reparented. After that, _requestFocusWhenReparented would need
1153 // to be set to true again to have it be focused again on the next
1154 // reparenting.
1155 //
1156 // This is used when requestFocus is called and there is no parent yet.
1157 bool _requestFocusWhenReparented = false;
1158
1159 /// Sets this node as the [FocusScopeNode.focusedChild] of the enclosing
1160 /// scope.
1161 ///
1162 /// Sets this node as the focused child for the enclosing scope, and that
1163 /// scope as the focused child for the scope above it, etc., until it reaches
1164 /// the root node. It doesn't change the primary focus, it just changes what
1165 /// node would be focused if the enclosing scope receives focus, and keeps
1166 /// track of previously focused children in that scope, so that if the focused
1167 /// child in that scope is removed, the previous focus returns.
1168 void _setAsFocusedChildForScope() {
1169 FocusNode scopeFocus = this;
1170 for (final FocusScopeNode ancestor in ancestors.whereType<FocusScopeNode>()) {
1171 assert(scopeFocus != ancestor, 'Somehow made a loop by setting focusedChild to its scope.');
1172 assert(_focusDebug(() => 'Setting $scopeFocus as focused child for scope:', () => <Object>[ancestor]));
1173 // Remove it anywhere in the focused child history.
1174 ancestor._focusedChildren.remove(scopeFocus);
1175 // Add it to the end of the list, which is also the top of the queue: The
1176 // end of the list represents the currently focused child.
1177 ancestor._focusedChildren.add(scopeFocus);
1178 scopeFocus = ancestor;
1179 }
1180 }
1181
1182 /// Request to move the focus to the next focus node, by calling the
1183 /// [FocusTraversalPolicy.next] method.
1184 ///
1185 /// Returns true if it successfully found a node and requested focus.
1186 bool nextFocus() => FocusTraversalGroup.of(context!).next(this);
1187
1188 /// Request to move the focus to the previous focus node, by calling the
1189 /// [FocusTraversalPolicy.previous] method.
1190 ///
1191 /// Returns true if it successfully found a node and requested focus.
1192 bool previousFocus() => FocusTraversalGroup.of(context!).previous(this);
1193
1194 /// Request to move the focus to the nearest focus node in the given
1195 /// direction, by calling the [FocusTraversalPolicy.inDirection] method.
1196 ///
1197 /// Returns true if it successfully found a node and requested focus.
1198 bool focusInDirection(TraversalDirection direction) => FocusTraversalGroup.of(context!).inDirection(this, direction);
1199
1200 @override
1201 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1202 super.debugFillProperties(properties);
1203 properties.add(DiagnosticsProperty<BuildContext>('context', context, defaultValue: null));
1204 properties.add(FlagProperty('descendantsAreFocusable', value: descendantsAreFocusable, ifFalse: 'DESCENDANTS UNFOCUSABLE', defaultValue: true));
1205 properties.add(FlagProperty('descendantsAreTraversable', value: descendantsAreTraversable, ifFalse: 'DESCENDANTS UNTRAVERSABLE', defaultValue: true));
1206 properties.add(FlagProperty('canRequestFocus', value: canRequestFocus, ifFalse: 'NOT FOCUSABLE', defaultValue: true));
1207 properties.add(FlagProperty('hasFocus', value: hasFocus && !hasPrimaryFocus, ifTrue: 'IN FOCUS PATH', defaultValue: false));
1208 properties.add(FlagProperty('hasPrimaryFocus', value: hasPrimaryFocus, ifTrue: 'PRIMARY FOCUS', defaultValue: false));
1209 }
1210
1211 @override
1212 List<DiagnosticsNode> debugDescribeChildren() {
1213 int count = 1;
1214 return _children.map<DiagnosticsNode>((FocusNode child) {
1215 return child.toDiagnosticsNode(name: 'Child ${count++}');
1216 }).toList();
1217 }
1218
1219 @override
1220 String toStringShort() {
1221 final bool hasDebugLabel = debugLabel != null && debugLabel!.isNotEmpty;
1222 final String extraData = '${hasDebugLabel ? debugLabel : ''}'
1223 '${hasFocus && hasDebugLabel ? ' ' : ''}'
1224 '${hasFocus && !hasPrimaryFocus ? '[IN FOCUS PATH]' : ''}'
1225 '${hasPrimaryFocus ? '[PRIMARY FOCUS]' : ''}';
1226 return '${describeIdentity(this)}${extraData.isNotEmpty ? '($extraData)' : ''}';
1227 }
1228}
1229
1230/// A subclass of [FocusNode] that acts as a scope for its descendants,
1231/// maintaining information about which descendant is currently or was last
1232/// focused.
1233///
1234/// _Please see the [FocusScope] and [Focus] widgets, which are utility widgets
1235/// that manage their own [FocusScopeNode]s and [FocusNode]s, respectively. If
1236/// they aren't appropriate, [FocusScopeNode]s can be managed directly._
1237///
1238/// [FocusScopeNode] organizes [FocusNode]s into _scopes_. Scopes form sub-trees
1239/// of nodes that can be traversed as a group. Within a scope, the most recent
1240/// nodes to have focus are remembered, and if a node is focused and then
1241/// removed, the original node receives focus again.
1242///
1243/// From a [FocusScopeNode], calling [setFirstFocus], sets the given focus scope
1244/// as the [focusedChild] of this node, adopting if it isn't already part of the
1245/// focus tree.
1246///
1247/// {@macro flutter.widgets.FocusNode.lifecycle}
1248/// {@macro flutter.widgets.FocusNode.keyEvents}
1249///
1250/// See also:
1251///
1252/// * [Focus], a widget that manages a [FocusNode] and provides access to focus
1253/// information and actions to its descendant widgets.
1254/// * [FocusManager], a singleton that manages the primary focus and
1255/// distributes key events to focused nodes.
1256class FocusScopeNode extends FocusNode {
1257 /// Creates a [FocusScopeNode].
1258 ///
1259 /// All parameters are optional.
1260 FocusScopeNode({
1261 super.debugLabel,
1262 super.onKeyEvent,
1263 @Deprecated(
1264 'Use onKeyEvent instead. '
1265 'This feature was deprecated after v3.18.0-2.0.pre.',
1266 )
1267 super.onKey,
1268 super.skipTraversal,
1269 super.canRequestFocus,
1270 this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
1271 }) : super(
1272 descendantsAreFocusable: true,
1273 );
1274
1275 @override
1276 FocusScopeNode get nearestScope => this;
1277
1278 /// Controls the transfer of focus beyond the first and the last items of a
1279 /// [FocusScopeNode].
1280 ///
1281 /// Changing this field value has no immediate effect on the UI. Instead, next time
1282 /// focus traversal takes place [FocusTraversalPolicy] will read this value
1283 /// and apply the new behavior.
1284 TraversalEdgeBehavior traversalEdgeBehavior;
1285
1286 /// Returns true if this scope is the focused child of its parent scope.
1287 bool get isFirstFocus => enclosingScope!.focusedChild == this;
1288
1289 /// Returns the child of this node that should receive focus if this scope
1290 /// node receives focus.
1291 ///
1292 /// If [hasFocus] is true, then this points to the child of this node that is
1293 /// currently focused.
1294 ///
1295 /// Returns null if there is no currently focused child.
1296 FocusNode? get focusedChild {
1297 assert(_focusedChildren.isEmpty || _focusedChildren.last.enclosingScope == this, 'Focused child does not have the same idea of its enclosing scope as the scope does.');
1298 return _focusedChildren.isNotEmpty ? _focusedChildren.last : null;
1299 }
1300
1301 // A stack of the children that have been set as the focusedChild, most recent
1302 // last (which is the top of the stack).
1303 final List<FocusNode> _focusedChildren = <FocusNode>[];
1304
1305 /// An iterator over the children that are allowed to be traversed by the
1306 /// [FocusTraversalPolicy].
1307 ///
1308 /// Will return an empty iterable if this scope node is not focusable, or if
1309 /// [descendantsAreFocusable] is false.
1310 ///
1311 /// See also:
1312 ///
1313 /// * [traversalDescendants], which traverses all of the node's descendants,
1314 /// not just the immediate children.
1315 @override
1316 Iterable<FocusNode> get traversalChildren {
1317 if (!canRequestFocus) {
1318 return const Iterable<FocusNode>.empty();
1319 }
1320 return super.traversalChildren;
1321 }
1322
1323 /// Returns all descendants which do not have the [skipTraversal] and do have
1324 /// the [canRequestFocus] flag set.
1325 ///
1326 /// Will return an empty iterable if this scope node is not focusable, or if
1327 /// [descendantsAreFocusable] is false.
1328 @override
1329 Iterable<FocusNode> get traversalDescendants {
1330 if (!canRequestFocus) {
1331 return const Iterable<FocusNode>.empty();
1332 }
1333 return super.traversalDescendants;
1334 }
1335
1336 /// Make the given [scope] the active child scope for this scope.
1337 ///
1338 /// If the given [scope] is not yet a part of the focus tree, then add it to
1339 /// the tree as a child of this scope. If it is already part of the focus
1340 /// tree, the given scope must be a descendant of this scope.
1341 void setFirstFocus(FocusScopeNode scope) {
1342 assert(scope != this, 'Unexpected self-reference in setFirstFocus.');
1343 assert(_focusDebug(() => 'Setting scope as first focus in $this to node:', () => <Object>[scope]));
1344 if (scope._parent == null) {
1345 _reparent(scope);
1346 }
1347 assert(scope.ancestors.contains(this), '$FocusScopeNode $scope must be a child of $this to set it as first focus.');
1348 if (hasFocus) {
1349 scope._doRequestFocus(findFirstFocus: true);
1350 } else {
1351 scope._setAsFocusedChildForScope();
1352 }
1353 }
1354
1355 /// If this scope lacks a focus, request that the given node become the focus.
1356 ///
1357 /// If the given node is not yet part of the focus tree, then add it as a
1358 /// child of this node.
1359 ///
1360 /// Useful for widgets that wish to grab the focus if no other widget already
1361 /// has the focus.
1362 ///
1363 /// The node is notified that it has received the primary focus in a
1364 /// microtask, so notification may lag the request by up to one frame.
1365 void autofocus(FocusNode node) {
1366 // Attach the node to the tree first, so in _applyFocusChange if the node
1367 // is detached we don't add it back to the tree.
1368 if (node._parent == null) {
1369 _reparent(node);
1370 }
1371
1372 assert(_manager != null);
1373 assert(_focusDebug(() => 'Autofocus scheduled for $node: scope $this'));
1374 _manager?._pendingAutofocuses.add(_Autofocus(scope: this, autofocusNode: node));
1375 _manager?._markNeedsUpdate();
1376 }
1377
1378 @override
1379 void _doRequestFocus({required bool findFirstFocus}) {
1380
1381 // It is possible that a previously focused child is no longer focusable.
1382 while (this.focusedChild != null && !this.focusedChild!.canRequestFocus) {
1383 _focusedChildren.removeLast();
1384 }
1385
1386 final FocusNode? focusedChild = this.focusedChild;
1387 // If findFirstFocus is false, then the request is to make this scope the
1388 // focus instead of looking for the ultimate first focus for this scope and
1389 // its descendants.
1390 if (!findFirstFocus || focusedChild == null) {
1391 if (canRequestFocus) {
1392 _setAsFocusedChildForScope();
1393 _markNextFocus(this);
1394 }
1395 return;
1396 }
1397
1398 focusedChild._doRequestFocus(findFirstFocus: true);
1399 }
1400
1401 @override
1402 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1403 super.debugFillProperties(properties);
1404 if (_focusedChildren.isEmpty) {
1405 return;
1406 }
1407 final List<String> childList = _focusedChildren.reversed.map<String>((FocusNode child) {
1408 return child.toStringShort();
1409 }).toList();
1410 properties.add(IterableProperty<String>('focusedChildren', childList, defaultValue: const Iterable<String>.empty()));
1411 properties.add(DiagnosticsProperty<TraversalEdgeBehavior>('traversalEdgeBehavior', traversalEdgeBehavior, defaultValue: TraversalEdgeBehavior.closedLoop));
1412 }
1413}
1414
1415/// An enum to describe which kind of focus highlight behavior to use when
1416/// displaying focus information.
1417enum FocusHighlightMode {
1418 /// Touch interfaces will not show the focus highlight except for controls
1419 /// which bring up the soft keyboard.
1420 ///
1421 /// If a device that uses a traditional mouse and keyboard has a touch screen
1422 /// attached, it can also enter `touch` mode if the user is using the touch
1423 /// screen.
1424 touch,
1425
1426 /// Traditional interfaces (keyboard and mouse) will show the currently
1427 /// focused control via a focus highlight of some sort.
1428 ///
1429 /// If a touch device (like a mobile phone) has a keyboard and/or mouse
1430 /// attached, it also can enter `traditional` mode if the user is using these
1431 /// input devices.
1432 traditional,
1433}
1434
1435/// An enum to describe how the current value of [FocusManager.highlightMode] is
1436/// determined. The strategy is set on [FocusManager.highlightStrategy].
1437enum FocusHighlightStrategy {
1438 /// Automatic switches between the various highlight modes based on the last
1439 /// kind of input that was received. This is the default.
1440 automatic,
1441
1442 /// [FocusManager.highlightMode] always returns [FocusHighlightMode.touch].
1443 alwaysTouch,
1444
1445 /// [FocusManager.highlightMode] always returns [FocusHighlightMode.traditional].
1446 alwaysTraditional,
1447}
1448
1449/// Manages the focus tree.
1450///
1451/// The focus tree is a separate, sparser, tree from the widget tree that
1452/// maintains the hierarchical relationship between focusable widgets in the
1453/// widget tree.
1454///
1455/// The focus manager is responsible for tracking which [FocusNode] has the
1456/// primary input focus (the [primaryFocus]), holding the [FocusScopeNode] that
1457/// is the root of the focus tree (the [rootScope]), and what the current
1458/// [highlightMode] is. It also distributes [KeyEvent]s to the nodes in the
1459/// focus tree.
1460///
1461/// The singleton [FocusManager] instance is held by the [WidgetsBinding] as
1462/// [WidgetsBinding.focusManager], and can be conveniently accessed using the
1463/// [FocusManager.instance] static accessor.
1464///
1465/// To find the [FocusNode] for a given [BuildContext], use [Focus.of]. To find
1466/// the [FocusScopeNode] for a given [BuildContext], use [FocusScope.of].
1467///
1468/// If you would like notification whenever the [primaryFocus] changes, register
1469/// a listener with [addListener]. When you no longer want to receive these
1470/// events, as when your object is about to be disposed, you must unregister
1471/// with [removeListener] to avoid memory leaks. Removing listeners is typically
1472/// done in [State.dispose] on stateful widgets.
1473///
1474/// The [highlightMode] describes how focus highlights should be displayed on
1475/// components in the UI. The [highlightMode] changes are notified separately
1476/// via [addHighlightModeListener] and removed with
1477/// [removeHighlightModeListener]. The highlight mode changes when the user
1478/// switches from a mouse to a touch interface, or vice versa.
1479///
1480/// The widgets that are used to manage focus in the widget tree are:
1481///
1482/// * [Focus], a widget that manages a [FocusNode] in the focus tree so that
1483/// the focus tree reflects changes in the widget hierarchy.
1484/// * [FocusScope], a widget that manages a [FocusScopeNode] in the focus tree,
1485/// creating a new scope for restricting focus to a set of focus nodes.
1486/// * [FocusTraversalGroup], a widget that groups together nodes that should be
1487/// traversed using an order described by a given [FocusTraversalPolicy].
1488///
1489/// See also:
1490///
1491/// * [FocusNode], which is a node in the focus tree that can receive focus.
1492/// * [FocusScopeNode], which is a node in the focus tree used to collect
1493/// subtrees into groups and restrict focus to them.
1494/// * The [primaryFocus] global accessor, for convenient access from anywhere
1495/// to the current focus manager state.
1496class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1497 /// Creates an object that manages the focus tree.
1498 ///
1499 /// This constructor is rarely called directly. To access the [FocusManager],
1500 /// consider using the [FocusManager.instance] accessor instead (which gets it
1501 /// from the [WidgetsBinding] singleton).
1502 ///
1503 /// This newly constructed focus manager does not have the necessary event
1504 /// handlers registered to allow it to manage focus. To register those event
1505 /// handlers, callers must call [registerGlobalHandlers]. See the
1506 /// documentation in that method for caveats to watch out for.
1507 FocusManager() {
1508 if (kFlutterMemoryAllocationsEnabled) {
1509 ChangeNotifier.maybeDispatchObjectCreation(this);
1510 }
1511 rootScope._manager = this;
1512 }
1513
1514 /// Registers global input event handlers that are needed to manage focus.
1515 ///
1516 /// This calls the [HardwareKeyboard.addHandler] on the shared instance of
1517 /// [HardwareKeyboard] and adds a route to the global entry in the gesture
1518 /// routing table. As such, only one [FocusManager] instance should register
1519 /// its global handlers.
1520 ///
1521 /// When this focus manager is no longer needed, calling [dispose] on it will
1522 /// unregister these handlers.
1523 void registerGlobalHandlers() => _highlightManager.registerGlobalHandlers();
1524
1525 @override
1526 void dispose() {
1527 _highlightManager.dispose();
1528 rootScope.dispose();
1529 super.dispose();
1530 }
1531
1532 /// Provides convenient access to the current [FocusManager] singleton from
1533 /// the [WidgetsBinding] instance.
1534 static FocusManager get instance => WidgetsBinding.instance.focusManager;
1535
1536 final _HighlightModeManager _highlightManager = _HighlightModeManager();
1537
1538 /// Sets the strategy by which [highlightMode] is determined.
1539 ///
1540 /// If set to [FocusHighlightStrategy.automatic], then the highlight mode will
1541 /// change depending upon the interaction mode used last. For instance, if the
1542 /// last interaction was a touch interaction, then [highlightMode] will return
1543 /// [FocusHighlightMode.touch], and focus highlights will only appear on
1544 /// widgets that bring up a soft keyboard. If the last interaction was a
1545 /// non-touch interaction (hardware keyboard press, mouse click, etc.), then
1546 /// [highlightMode] will return [FocusHighlightMode.traditional], and focus
1547 /// highlights will appear on all widgets.
1548 ///
1549 /// If set to [FocusHighlightStrategy.alwaysTouch] or
1550 /// [FocusHighlightStrategy.alwaysTraditional], then [highlightMode] will
1551 /// always return [FocusHighlightMode.touch] or
1552 /// [FocusHighlightMode.traditional], respectively, regardless of the last UI
1553 /// interaction type.
1554 ///
1555 /// The initial value of [highlightMode] depends upon the value of
1556 /// [defaultTargetPlatform] and [MouseTracker.mouseIsConnected] of
1557 /// [RendererBinding.mouseTracker], making a guess about which interaction is
1558 /// most appropriate for the initial interaction mode.
1559 ///
1560 /// Defaults to [FocusHighlightStrategy.automatic].
1561 FocusHighlightStrategy get highlightStrategy => _highlightManager.strategy;
1562 set highlightStrategy(FocusHighlightStrategy value) {
1563 if (_highlightManager.strategy == value) {
1564 return;
1565 }
1566 _highlightManager.strategy = value;
1567 }
1568
1569 /// Indicates the current interaction mode for focus highlights.
1570 ///
1571 /// The value returned depends upon the [highlightStrategy] used, and possibly
1572 /// (depending on the value of [highlightStrategy]) the most recent
1573 /// interaction mode that they user used.
1574 ///
1575 /// If [highlightMode] returns [FocusHighlightMode.touch], then widgets should
1576 /// not draw their focus highlight unless they perform text entry.
1577 ///
1578 /// If [highlightMode] returns [FocusHighlightMode.traditional], then widgets should
1579 /// draw their focus highlight whenever they are focused.
1580 // Don't want to set _highlightMode here, since it's possible for the target
1581 // platform to change (especially in tests).
1582 FocusHighlightMode get highlightMode => _highlightManager.highlightMode;
1583
1584 /// Register a closure to be called when the [FocusManager] notifies its listeners
1585 /// that the value of [highlightMode] has changed.
1586 void addHighlightModeListener(ValueChanged<FocusHighlightMode> listener) => _highlightManager.addListener(listener);
1587
1588 /// Remove a previously registered closure from the list of closures that the
1589 /// [FocusManager] notifies.
1590 void removeHighlightModeListener(ValueChanged<FocusHighlightMode> listener) => _highlightManager.removeListener(listener);
1591
1592 /// {@template flutter.widgets.focus_manager.FocusManager.addEarlyKeyEventHandler}
1593 /// Adds a key event handler to a set of handlers that are called before any
1594 /// key event handlers in the focus tree are called.
1595 ///
1596 /// All of the handlers in the set will be called for every key event the
1597 /// [FocusManager] receives. If any one of the handlers returns
1598 /// [KeyEventResult.handled] or [KeyEventResult.skipRemainingHandlers], then
1599 /// none of the handlers in the focus tree will be called.
1600 ///
1601 /// If handlers are added while the existing callbacks are being invoked, they
1602 /// will not be called until the next key event occurs.
1603 ///
1604 /// See also:
1605 ///
1606 /// * [removeEarlyKeyEventHandler], which removes handlers added by this
1607 /// function.
1608 /// * [addLateKeyEventHandler], which is a similar mechanism for adding
1609 /// handlers that are invoked after the focus tree has had a chance to
1610 /// handle an event.
1611 /// {@endtemplate}
1612 void addEarlyKeyEventHandler(OnKeyEventCallback handler) {
1613 _highlightManager.addEarlyKeyEventHandler(handler);
1614 }
1615
1616 /// {@template flutter.widgets.focus_manager.FocusManager.removeEarlyKeyEventHandler}
1617 /// Removes a key handler added by calling [addEarlyKeyEventHandler].
1618 ///
1619 /// If handlers are removed while the existing callbacks are being invoked,
1620 /// they will continue to be called until the next key event is received.
1621 ///
1622 /// See also:
1623 ///
1624 /// * [addEarlyKeyEventHandler], which adds the handlers removed by this
1625 /// function.
1626 /// {@endtemplate}
1627 void removeEarlyKeyEventHandler(OnKeyEventCallback handler) {
1628 _highlightManager.removeEarlyKeyEventHandler(handler);
1629 }
1630
1631 /// {@template flutter.widgets.focus_manager.FocusManager.addLateKeyEventHandler}
1632 /// Adds a key event handler to a set of handlers that are called if none of
1633 /// the key event handlers in the focus tree handle the event.
1634 ///
1635 /// If the event reaches the root of the focus tree without being handled,
1636 /// then all of the handlers in the set will be called. If any of them returns
1637 /// [KeyEventResult.handled] or [KeyEventResult.skipRemainingHandlers], then
1638 /// event propagation to the platform will be stopped.
1639 ///
1640 /// If handlers are added while the existing callbacks are being invoked, they
1641 /// will not be called until the next key event is not handled by the focus
1642 /// tree.
1643 ///
1644 /// See also:
1645 ///
1646 /// * [removeLateKeyEventHandler], which removes handlers added by this
1647 /// function.
1648 /// * [addEarlyKeyEventHandler], which is a similar mechanism for adding
1649 /// handlers that are invoked before the focus tree has had a chance to
1650 /// handle an event.
1651 /// {@endtemplate}
1652 void addLateKeyEventHandler(OnKeyEventCallback handler) {
1653 _highlightManager.addLateKeyEventHandler(handler);
1654 }
1655
1656 /// {@template flutter.widgets.focus_manager.FocusManager.removeLateKeyEventHandler}
1657 /// Removes a key handler added by calling [addLateKeyEventHandler].
1658 ///
1659 /// If handlers are removed while the existing callbacks are being invoked,
1660 /// they will continue to be called until the next key event is received.
1661 ///
1662 /// See also:
1663 ///
1664 /// * [addLateKeyEventHandler], which adds the handlers removed by this
1665 /// function.
1666 /// {@endtemplate}
1667 void removeLateKeyEventHandler(OnKeyEventCallback handler) {
1668 _highlightManager.removeLateKeyEventHandler(handler);
1669 }
1670
1671 /// The root [FocusScopeNode] in the focus tree.
1672 ///
1673 /// This field is rarely used directly. To find the nearest [FocusScopeNode]
1674 /// for a given [FocusNode], call [FocusNode.nearestScope].
1675 final FocusScopeNode rootScope = FocusScopeNode(debugLabel: 'Root Focus Scope');
1676
1677 /// The node that currently has the primary focus.
1678 FocusNode? get primaryFocus => _primaryFocus;
1679 FocusNode? _primaryFocus;
1680
1681 // The set of nodes that need to notify their listeners of changes at the next
1682 // update.
1683 final Set<FocusNode> _dirtyNodes = <FocusNode>{};
1684
1685 // The node that has requested to have the primary focus, but hasn't been
1686 // given it yet.
1687 FocusNode? _markedForFocus;
1688
1689 void _markDetached(FocusNode node) {
1690 // The node has been removed from the tree, so it no longer needs to be
1691 // notified of changes.
1692 assert(_focusDebug(() => 'Node was detached: $node'));
1693 if (_primaryFocus == node) {
1694 _primaryFocus = null;
1695 }
1696 _dirtyNodes.remove(node);
1697 }
1698
1699 void _markPropertiesChanged(FocusNode node) {
1700 _markNeedsUpdate();
1701 assert(_focusDebug(() => 'Properties changed for node $node.'));
1702 _dirtyNodes.add(node);
1703 }
1704
1705 void _markNextFocus(FocusNode node) {
1706 if (_primaryFocus == node) {
1707 // The caller asked for the current focus to be the next focus, so just
1708 // pretend that didn't happen.
1709 _markedForFocus = null;
1710 } else {
1711 _markedForFocus = node;
1712 _markNeedsUpdate();
1713 }
1714 }
1715
1716 // The list of autofocus requests made since the last _applyFocusChange call.
1717 final List<_Autofocus> _pendingAutofocuses = <_Autofocus>[];
1718
1719 // True indicates that there is an update pending.
1720 bool _haveScheduledUpdate = false;
1721
1722 // Request that an update be scheduled, optionally requesting focus for the
1723 // given newFocus node.
1724 void _markNeedsUpdate() {
1725 assert(_focusDebug(() => 'Scheduling update, current focus is $_primaryFocus, next focus will be $_markedForFocus'));
1726 if (_haveScheduledUpdate) {
1727 return;
1728 }
1729 _haveScheduledUpdate = true;
1730 scheduleMicrotask(applyFocusChangesIfNeeded);
1731 }
1732
1733 /// Applies any pending focus changes and notifies listeners that the focus
1734 /// has changed.
1735 ///
1736 /// Must not be called during the build phase. This method is meant to be
1737 /// called in a post-frame callback or microtask when the pending focus
1738 /// changes need to be resolved before something else occurs.
1739 ///
1740 /// It can't be called during the build phase because not all listeners are
1741 /// safe to be called with an update during a build.
1742 ///
1743 /// Typically, this is called automatically by the [FocusManager], but
1744 /// sometimes it is necessary to ensure that no focus changes are pending
1745 /// before executing an action. For example, the [MenuAnchor] class uses this
1746 /// to make sure that the previous focus has been restored before executing a
1747 /// menu callback when a menu item is selected.
1748 ///
1749 /// It is safe to call this if no focus changes are pending.
1750 void applyFocusChangesIfNeeded() {
1751 assert(
1752 SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks,
1753 'applyFocusChangesIfNeeded() should not be called during the build phase.'
1754 );
1755
1756 _haveScheduledUpdate = false;
1757 final FocusNode? previousFocus = _primaryFocus;
1758
1759 for (final _Autofocus autofocus in _pendingAutofocuses) {
1760 autofocus.applyIfValid(this);
1761 }
1762 _pendingAutofocuses.clear();
1763
1764 if (_primaryFocus == null && _markedForFocus == null) {
1765 // If we don't have any current focus, and nobody has asked to focus yet,
1766 // then revert to the root scope.
1767 _markedForFocus = rootScope;
1768 }
1769 assert(_focusDebug(() => 'Refreshing focus state. Next focus will be $_markedForFocus'));
1770 // A node has requested to be the next focus, and isn't already the primary
1771 // focus.
1772 if (_markedForFocus != null && _markedForFocus != _primaryFocus) {
1773 final Set<FocusNode> previousPath = previousFocus?.ancestors.toSet() ?? <FocusNode>{};
1774 final Set<FocusNode> nextPath = _markedForFocus!.ancestors.toSet();
1775 // Notify nodes that are newly focused.
1776 _dirtyNodes.addAll(nextPath.difference(previousPath));
1777 // Notify nodes that are no longer focused
1778 _dirtyNodes.addAll(previousPath.difference(nextPath));
1779
1780 _primaryFocus = _markedForFocus;
1781 _markedForFocus = null;
1782 }
1783 assert(_markedForFocus == null);
1784 if (previousFocus != _primaryFocus) {
1785 assert(_focusDebug(() => 'Updating focus from $previousFocus to $_primaryFocus'));
1786 if (previousFocus != null) {
1787 _dirtyNodes.add(previousFocus);
1788 }
1789 if (_primaryFocus != null) {
1790 _dirtyNodes.add(_primaryFocus!);
1791 }
1792 }
1793 for (final FocusNode node in _dirtyNodes) {
1794 node._notify();
1795 }
1796 assert(_focusDebug(() => 'Notified ${_dirtyNodes.length} dirty nodes:', () => _dirtyNodes));
1797 _dirtyNodes.clear();
1798 if (previousFocus != _primaryFocus) {
1799 notifyListeners();
1800 }
1801 assert(() {
1802 if (debugFocusChanges) {
1803 debugDumpFocusTree();
1804 }
1805 return true;
1806 }());
1807 }
1808
1809 @override
1810 List<DiagnosticsNode> debugDescribeChildren() {
1811 return <DiagnosticsNode>[
1812 rootScope.toDiagnosticsNode(name: 'rootScope'),
1813 ];
1814 }
1815
1816 @override
1817 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1818 properties.add(FlagProperty('haveScheduledUpdate', value: _haveScheduledUpdate, ifTrue: 'UPDATE SCHEDULED'));
1819 properties.add(DiagnosticsProperty<FocusNode>('primaryFocus', primaryFocus, defaultValue: null));
1820 properties.add(DiagnosticsProperty<FocusNode>('nextFocus', _markedForFocus, defaultValue: null));
1821 final Element? element = primaryFocus?.context as Element?;
1822 if (element != null) {
1823 properties.add(DiagnosticsProperty<String>('primaryFocusCreator', element.debugGetCreatorChain(20)));
1824 }
1825 }
1826}
1827
1828// A class to detect and manage the highlight mode transitions. An instance of
1829// this is owned by the FocusManager.
1830//
1831// This doesn't extend ChangeNotifier because the callback passes the updated
1832// value, and ChangeNotifier requires using VoidCallback.
1833class _HighlightModeManager {
1834 // If set, indicates if the last interaction detected was touch or not. If
1835 // null, no interactions have occurred yet.
1836 bool? _lastInteractionWasTouch;
1837
1838 FocusHighlightMode get highlightMode => _highlightMode ?? _defaultModeForPlatform;
1839 FocusHighlightMode? _highlightMode;
1840
1841 FocusHighlightStrategy get strategy => _strategy;
1842 FocusHighlightStrategy _strategy = FocusHighlightStrategy.automatic;
1843 set strategy(FocusHighlightStrategy value) {
1844 if (_strategy == value) {
1845 return;
1846 }
1847 _strategy = value;
1848 updateMode();
1849 }
1850
1851 /// {@macro flutter.widgets.focus_manager.FocusManager.addEarlyKeyEventHandler}
1852 void addEarlyKeyEventHandler(OnKeyEventCallback callback) => _earlyKeyEventHandlers.add(callback);
1853
1854 /// {@macro flutter.widgets.focus_manager.FocusManager.removeEarlyKeyEventHandler}
1855 void removeEarlyKeyEventHandler(OnKeyEventCallback callback) => _earlyKeyEventHandlers.remove(callback);
1856
1857 // The list of callbacks for early key handling.
1858 final HashedObserverList<OnKeyEventCallback> _earlyKeyEventHandlers = HashedObserverList<OnKeyEventCallback>();
1859
1860 /// {@macro flutter.widgets.focus_manager.FocusManager.addLateKeyEventHandler}
1861 void addLateKeyEventHandler(OnKeyEventCallback callback) => _lateKeyEventHandlers.add(callback);
1862
1863 /// {@macro flutter.widgets.focus_manager.FocusManager.removeLateKeyEventHandler}
1864 void removeLateKeyEventHandler(OnKeyEventCallback callback) => _lateKeyEventHandlers.remove(callback);
1865
1866 // The list of callbacks for late key handling.
1867 final HashedObserverList<OnKeyEventCallback> _lateKeyEventHandlers = HashedObserverList<OnKeyEventCallback>();
1868
1869 /// Register a closure to be called when the [FocusManager] notifies its
1870 /// listeners that the value of [highlightMode] has changed.
1871 void addListener(ValueChanged<FocusHighlightMode> listener) => _listeners.add(listener);
1872
1873 /// Remove a previously registered closure from the list of closures that the
1874 /// [FocusManager] notifies.
1875 void removeListener(ValueChanged<FocusHighlightMode> listener) => _listeners.remove(listener);
1876
1877 // The list of listeners for [highlightMode] state changes.
1878 HashedObserverList<ValueChanged<FocusHighlightMode>> _listeners = HashedObserverList<ValueChanged<FocusHighlightMode>>();
1879
1880 void registerGlobalHandlers() {
1881 assert(ServicesBinding.instance.keyEventManager.keyMessageHandler == null);
1882 // TODO(gspencergoog): Remove this when the RawKeyEvent system is
1883 // deprecated, and replace it with registering a handler on the
1884 // HardwareKeyboard.
1885 ServicesBinding.instance.keyEventManager.keyMessageHandler = handleKeyMessage;
1886 GestureBinding.instance.pointerRouter.addGlobalRoute(handlePointerEvent);
1887 }
1888
1889 @mustCallSuper
1890 void dispose() {
1891 if (ServicesBinding.instance.keyEventManager.keyMessageHandler == handleKeyMessage) {
1892 GestureBinding.instance.pointerRouter.removeGlobalRoute(handlePointerEvent);
1893 ServicesBinding.instance.keyEventManager.keyMessageHandler = null;
1894 }
1895 _listeners = HashedObserverList<ValueChanged<FocusHighlightMode>>();
1896 }
1897
1898 @pragma('vm:notify-debugger-on-exception')
1899 void notifyListeners() {
1900 if (_listeners.isEmpty) {
1901 return;
1902 }
1903 final List<ValueChanged<FocusHighlightMode>> localListeners = List<ValueChanged<FocusHighlightMode>>.of(_listeners);
1904 for (final ValueChanged<FocusHighlightMode> listener in localListeners) {
1905 try {
1906 if (_listeners.contains(listener)) {
1907 listener(highlightMode);
1908 }
1909 } catch (exception, stack) {
1910 InformationCollector? collector;
1911 assert(() {
1912 collector = () => <DiagnosticsNode>[
1913 DiagnosticsProperty<_HighlightModeManager>(
1914 'The $runtimeType sending notification was',
1915 this,
1916 style: DiagnosticsTreeStyle.errorProperty,
1917 ),
1918 ];
1919 return true;
1920 }());
1921 FlutterError.reportError(FlutterErrorDetails(
1922 exception: exception,
1923 stack: stack,
1924 library: 'widgets library',
1925 context: ErrorDescription('while dispatching notifications for $runtimeType'),
1926 informationCollector: collector,
1927 ));
1928 }
1929 }
1930 }
1931
1932 void handlePointerEvent(PointerEvent event) {
1933 final FocusHighlightMode expectedMode;
1934 switch (event.kind) {
1935 case PointerDeviceKind.touch:
1936 case PointerDeviceKind.stylus:
1937 case PointerDeviceKind.invertedStylus:
1938 _lastInteractionWasTouch = true;
1939 expectedMode = FocusHighlightMode.touch;
1940 case PointerDeviceKind.mouse:
1941 case PointerDeviceKind.trackpad:
1942 case PointerDeviceKind.unknown:
1943 _lastInteractionWasTouch = false;
1944 expectedMode = FocusHighlightMode.traditional;
1945 }
1946 if (expectedMode != highlightMode) {
1947 updateMode();
1948 }
1949 }
1950
1951 bool handleKeyMessage(KeyMessage message) {
1952 // Update highlightMode first, since things responding to the keys might
1953 // look at the highlight mode, and it should be accurate.
1954 _lastInteractionWasTouch = false;
1955 updateMode();
1956
1957 assert(_focusDebug(() => 'Received key event $message'));
1958 if (FocusManager.instance.primaryFocus == null) {
1959 assert(_focusDebug(() => 'No primary focus for key event, ignored: $message'));
1960 return false;
1961 }
1962
1963 bool handled = false;
1964 // Check to see if any of the early handlers handle the key. If so, then
1965 // return early.
1966 if (_earlyKeyEventHandlers.isNotEmpty) {
1967 final List<KeyEventResult> results = <KeyEventResult>[];
1968 // Copy the list before iteration to prevent problems if the list gets
1969 // modified during iteration.
1970 final List<OnKeyEventCallback> iterationList = _earlyKeyEventHandlers.toList();
1971 for (final OnKeyEventCallback callback in iterationList) {
1972 for (final KeyEvent event in message.events) {
1973 results.add(callback(event));
1974 }
1975 }
1976 final KeyEventResult result = combineKeyEventResults(results);
1977 switch (result) {
1978 case KeyEventResult.ignored:
1979 break;
1980 case KeyEventResult.handled:
1981 assert(_focusDebug(() => 'Key event $message handled by early key event callback.'));
1982 handled = true;
1983 case KeyEventResult.skipRemainingHandlers:
1984 assert(_focusDebug(() => 'Key event $message propagation stopped by early key event callback.'));
1985 handled = false;
1986 }
1987 }
1988 if (handled) {
1989 return true;
1990 }
1991
1992 // Walk the current focus from the leaf to the root, calling each node's
1993 // onKeyEvent on the way up, and if one responds that they handled it or
1994 // want to stop propagation, stop.
1995 for (final FocusNode node in <FocusNode>[
1996 FocusManager.instance.primaryFocus!,
1997 ...FocusManager.instance.primaryFocus!.ancestors,
1998 ]) {
1999 final List<KeyEventResult> results = <KeyEventResult>[];
2000 if (node.onKeyEvent != null) {
2001 for (final KeyEvent event in message.events) {
2002 results.add(node.onKeyEvent!(node, event));
2003 }
2004 }
2005 if (node.onKey != null && message.rawEvent != null) {
2006 results.add(node.onKey!(node, message.rawEvent!));
2007 }
2008 final KeyEventResult result = combineKeyEventResults(results);
2009 switch (result) {
2010 case KeyEventResult.ignored:
2011 continue;
2012 case KeyEventResult.handled:
2013 assert(_focusDebug(() => 'Node $node handled key event $message.'));
2014 handled = true;
2015 case KeyEventResult.skipRemainingHandlers:
2016 assert(_focusDebug(() => 'Node $node stopped key event propagation: $message.'));
2017 handled = false;
2018 }
2019 // Only KeyEventResult.ignored will continue the for loop. All other
2020 // options will stop the event propagation.
2021 assert(result != KeyEventResult.ignored);
2022 break;
2023 }
2024
2025 // Check to see if any late key event handlers want to handle the event.
2026 if (!handled && _lateKeyEventHandlers.isNotEmpty) {
2027 final List<KeyEventResult> results = <KeyEventResult>[];
2028 // Copy the list before iteration to prevent problems if the list gets
2029 // modified during iteration.
2030 final List<OnKeyEventCallback> iterationList = _lateKeyEventHandlers.toList();
2031 for (final OnKeyEventCallback callback in iterationList) {
2032 for (final KeyEvent event in message.events) {
2033 results.add(callback(event));
2034 }
2035 }
2036 final KeyEventResult result = combineKeyEventResults(results);
2037 switch (result) {
2038 case KeyEventResult.ignored:
2039 break;
2040 case KeyEventResult.handled:
2041 assert(_focusDebug(() => 'Key event $message handled by late key event callback.'));
2042 handled = true;
2043 case KeyEventResult.skipRemainingHandlers:
2044 assert(_focusDebug(() => 'Key event $message propagation stopped by late key event callback.'));
2045 handled = false;
2046 }
2047 }
2048 if (!handled) {
2049 assert(_focusDebug(() => 'Key event not handled by focus system: $message.'));
2050 }
2051 return handled;
2052 }
2053
2054 // Update function to be called whenever the state relating to highlightMode
2055 // changes.
2056 void updateMode() {
2057 final FocusHighlightMode newMode;
2058 switch (strategy) {
2059 case FocusHighlightStrategy.automatic:
2060 if (_lastInteractionWasTouch == null) {
2061 // If we don't have any information about the last interaction yet,
2062 // then just rely on the default value for the platform, which will be
2063 // determined based on the target platform if _highlightMode is not
2064 // set.
2065 return;
2066 }
2067 if (_lastInteractionWasTouch!) {
2068 newMode = FocusHighlightMode.touch;
2069 } else {
2070 newMode = FocusHighlightMode.traditional;
2071 }
2072 case FocusHighlightStrategy.alwaysTouch:
2073 newMode = FocusHighlightMode.touch;
2074 case FocusHighlightStrategy.alwaysTraditional:
2075 newMode = FocusHighlightMode.traditional;
2076 }
2077 // We can't just compare newMode with _highlightMode here, since
2078 // _highlightMode could be null, so we want to compare with the return value
2079 // for the getter, since that's what clients will be looking at.
2080 final FocusHighlightMode oldMode = highlightMode;
2081 _highlightMode = newMode;
2082 if (highlightMode != oldMode) {
2083 notifyListeners();
2084 }
2085 }
2086
2087 static FocusHighlightMode get _defaultModeForPlatform {
2088 // Assume that if we're on one of the mobile platforms, and there's no mouse
2089 // connected, that the initial interaction will be touch-based, and that
2090 // it's traditional mouse and keyboard on all other platforms.
2091 //
2092 // This only affects the initial value: the ongoing value is updated to a
2093 // known correct value as soon as any pointer/keyboard events are received.
2094 switch (defaultTargetPlatform) {
2095 case TargetPlatform.android:
2096 case TargetPlatform.fuchsia:
2097 case TargetPlatform.iOS:
2098 if (WidgetsBinding.instance.mouseTracker.mouseIsConnected) {
2099 return FocusHighlightMode.traditional;
2100 }
2101 return FocusHighlightMode.touch;
2102 case TargetPlatform.linux:
2103 case TargetPlatform.macOS:
2104 case TargetPlatform.windows:
2105 return FocusHighlightMode.traditional;
2106 }
2107 }
2108}
2109
2110/// Provides convenient access to the current [FocusManager.primaryFocus] from
2111/// the [WidgetsBinding] instance.
2112FocusNode? get primaryFocus => WidgetsBinding.instance.focusManager.primaryFocus;
2113
2114/// Returns a text representation of the current focus tree, along with the
2115/// current attributes on each node.
2116///
2117/// Will return an empty string in release builds.
2118String debugDescribeFocusTree() {
2119 String? result;
2120 assert(() {
2121 result = FocusManager.instance.toStringDeep();
2122 return true;
2123 }());
2124 return result ?? '';
2125}
2126
2127/// Prints a text representation of the current focus tree, along with the
2128/// current attributes on each node.
2129///
2130/// Will do nothing in release builds.
2131void debugDumpFocusTree() {
2132 assert(() {
2133 debugPrint(debugDescribeFocusTree());
2134 return true;
2135 }());
2136}
2137