• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 The Chromium 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:io';
7import 'dart:ui';
8
9import 'package:flutter/foundation.dart';
10import 'package:flutter/painting.dart';
11import 'package:flutter/rendering.dart';
12import 'package:flutter/services.dart';
13
14import 'binding.dart';
15import 'focus_scope.dart';
16import 'focus_traversal.dart';
17import 'framework.dart';
18
19// Used for debugging focus code. Set to true to see highly verbose debug output
20// when focus changes occur.
21const bool _kDebugFocus = false;
22
23bool _focusDebug(String message, [Iterable<String> details]) {
24  if (_kDebugFocus) {
25    debugPrint('FOCUS: $message');
26    if (details != null && details.isNotEmpty) {
27      for (String detail in details) {
28        debugPrint('    $detail');
29      }
30    }
31  }
32  return true;
33}
34
35/// Signature of a callback used by [Focus.onKey] and [FocusScope.onKey]
36/// to receive key events.
37///
38/// The [node] is the node that received the event.
39typedef FocusOnKeyCallback = bool Function(FocusNode node, RawKeyEvent event);
40
41/// An attachment point for a [FocusNode].
42///
43/// Once created, a [FocusNode] must be attached to the widget tree by its
44/// _host_ [StatefulWidget] via a [FocusAttachment] object. [FocusAttachment]s
45/// are owned by the [StatefulWidget] that hosts a [FocusNode] or
46/// [FocusScopeNode]. There can be multiple [FocusAttachment]s for each
47/// [FocusNode], but the node will only ever be attached to one of them at a
48/// time.
49///
50/// This attachment is created by calling [FocusNode.attach], usually from the
51/// host widget's [State.initState] method. If the widget is updated to have a
52/// different focus node, then the new node needs to be attached in
53/// [State.didUpdateWidget], after calling [detach] on the previous
54/// [FocusAttachment]. Once detached, the attachment is defunct and will no
55/// longer make changes to the [FocusNode] through [reparent].
56///
57/// Without these attachment points, it would be possible for a focus node to
58/// simultaneously be attached to more than one part of the widget tree during
59/// the build stage.
60class FocusAttachment {
61  /// A private constructor, because [FocusAttachment]s are only to be created
62  /// by [FocusNode.attach].
63  FocusAttachment._(this._node) : assert(_node != null);
64
65  // The focus node that this attachment manages an attachment for. The node may
66  // not yet have a parent, or may have been detached from this attachment, so
67  // don't count on this node being in a usable state.
68  final FocusNode _node;
69
70  /// Returns true if the associated node is attached to this attachment.
71  ///
72  /// It is possible to be attached to the widget tree, but not be placed in
73  /// the focus tree (i.e. to not have a parent yet in the focus tree).
74  bool get isAttached => _node._attachment == this;
75
76  /// Detaches the [FocusNode] this attachment point is associated with from the
77  /// focus tree, and disconnects it from this attachment point.
78  ///
79  /// Calling [FocusNode.dispose] will also automatically detach the node.
80  void detach() {
81    assert(_node != null);
82    assert(_focusDebug('Detaching node:', <String>[_node.toString(), 'With enclosing scope ${_node.enclosingScope}']));
83    if (isAttached) {
84      if (_node.hasPrimaryFocus) {
85        _node.unfocus();
86      }
87      _node._parent?._removeChild(_node);
88      _node._attachment = null;
89    }
90    assert(!isAttached);
91  }
92
93  /// Ensures that the [FocusNode] attached at this attachment point has the
94  /// proper parent node, changing it if necessary.
95  ///
96  /// If given, ensures that the given [parent] node is the parent of the node
97  /// that is attached at this attachment point, changing it if necessary.
98  /// However, it is usually not necessary to supply an explicit parent, since
99  /// [reparent] will use [Focus.of] to determine the correct parent node for
100  /// the context given in [FocusNode.attach].
101  ///
102  /// If [isAttached] is false, then calling this method does nothing.
103  ///
104  /// Should be called whenever the associated widget is rebuilt in order to
105  /// maintain the focus hierarchy.
106  ///
107  /// A [StatefulWidget] that hosts a [FocusNode] should call this method on the
108  /// node it hosts during its [State.build] or [State.didChangeDependencies]
109  /// methods in case the widget is moved from one location in the tree to
110  /// another location that has a different [FocusScope] or context.
111  ///
112  /// The optional [parent] argument must be supplied when not using [Focus] and
113  /// [FocusScope] widgets to build the focus tree, or if there is a need to
114  /// supply the parent explicitly (which are both uncommon).
115  void reparent({FocusNode parent}) {
116    assert(_node != null);
117    if (isAttached) {
118      assert(_node.context != null);
119      parent ??= Focus.of(_node.context, nullOk: true);
120      parent ??= FocusScope.of(_node.context);
121      assert(parent != null);
122      parent._reparent(_node);
123    }
124  }
125}
126
127/// An object that can be used by a stateful widget to obtain the keyboard focus
128/// and to handle keyboard events.
129///
130/// _Please see the [Focus] and [FocusScope] widgets, which are utility widgets
131/// that manage their own [FocusNode]s and [FocusScopeNode]s, respectively. If
132/// they aren't appropriate, [FocusNode]s can be managed directly._
133///
134/// [FocusNode]s are persistent objects that form a _focus tree_ that is a
135/// representation of the widgets in the hierarchy that are interested in focus.
136/// A focus node might need to be created if it is passed in from an ancestor of
137/// a [Focus] widget to control the focus of the children from the ancestor, or
138/// a widget might need to host one if the widget subsystem is not being used,
139/// or if the [Focus] and [FocusScope] widgets provide insufficient control.
140///
141/// [FocusNodes] are organized into _scopes_ (see [FocusScopeNode]), which form
142/// sub-trees of nodes that can be traversed as a group. Within a scope, the
143/// most recent nodes to have focus are remembered, and if a node is focused and
144/// then removed, the previous node receives focus again.
145///
146/// The focus node hierarchy can be traversed using the [parent], [children],
147/// [ancestors] and [descendants] accessors.
148///
149/// [FocusNode]s are [ChangeNotifier]s, so a listener can be registered to
150/// receive a notification when the focus changes. If the [Focus] and
151/// [FocusScope] widgets are being used to manage the nodes, consider
152/// establishing an [InheritedWidget] dependency on them by calling [Focus.of]
153/// or [FocusScope.of] instead. [Focus.hasFocus] can also be used to establish a
154/// similar dependency, especially if all that is needed is to determine whether
155/// or not the widget is focused at build time.
156///
157/// To see the focus tree in the debug console, call [debugDumpFocusTree]. To
158/// get the focus tree as a string, call [debugDescribeFocusTree].
159///
160/// {@template flutter.widgets.focus_manager.focus.lifecycle}
161/// ## Lifecycle
162///
163/// There are several actors involved in the lifecycle of a
164/// [FocusNode]/[FocusScopeNode]. They are created and disposed by their
165/// _owner_, attached, detached, and reparented using a [FocusAttachment] by
166/// their _host_ (which must be owned by the [State] of a [StatefulWidget]), and
167/// they are managed by the [FocusManager]. Different parts of the [FocusNode]
168/// API are intended for these different actors.
169///
170/// [FocusNode]s (and hence [FocusScopeNode]s) are persistent objects that form
171/// part of a _focus tree_ that is a sparse representation of the widgets in the
172/// hierarchy that are interested in receiving keyboard events. They must be
173/// managed like other persistent state, which is typically done by a
174/// [StatefulWidget] that owns the node. A stateful widget that owns a focus
175/// scope node must call [dispose] from its [State.dispose] method.
176///
177/// Once created, a [FocusNode] must be attached to the widget tree via a
178/// [FocusAttachment] object. This attachment is created by calling [attach],
179/// usually from the [State.initState] method. If the hosting widget is updated
180/// to have a different focus node, then the updated node needs to be attached
181/// in [State.didUpdateWidget], after calling [detach] on the previous
182/// [FocusAttachment].
183///
184/// Because [FocusNode]s form a sparse representation of the widget tree,
185/// they must be updated whenever the widget tree is rebuilt. This is done by
186/// calling [FocusAttachment.reparent], usually from the [State.build] or
187/// [State.didChangeDependencies] methods of the widget that represents the
188/// focused region, so that the [BuildContext] assigned to the [FocusScopeNode]
189/// can be tracked (the context is used to obtain the [RenderObject], from which
190/// the geometry of focused regions can be determined).
191///
192/// Creating a [FocusNode] each time [State.build] is invoked will cause the
193/// focus to be lost each time the widget is built, which is usually not desired
194/// behavior (call [unfocus] if losing focus is desired).
195///
196/// If, as is common, the hosting [StatefulWidget] is also the owner of the
197/// focus node, then it will also call [dispose] from its [State.dispose] (in
198/// which case the [detach] may be skipped, since dispose will automatically
199/// detach). If another object owns the focus node, then it must call [dispose]
200/// when the node is done being used.
201/// {@endtemplate}
202///
203/// {@template flutter.widgets.focus_manager.focus.keyEvents}
204/// ## Key Event Propagation
205///
206/// The [FocusManager] receives all key events and will pass them to the focused
207/// nodes. It starts with the node with the primary focus, and will call the
208/// [onKey] callback for that node. If the callback returns false, indicating
209/// that it did not handle the event, the [FocusManager] will move to the parent
210/// of that node and call its [onKey]. If that [onKey] returns true, then it
211/// will stop propagating the event. If it reaches the root [FocusScopeNode],
212/// [FocusManager.rootScope], the event is discarded.
213/// {@endtemplate}
214///
215/// ## Focus Traversal
216///
217/// The term _traversal_, sometimes called _tab traversal_, refers to moving the
218/// focus from one widget to the next in a particular order (also sometimes
219/// referred to as the _tab order_, since the TAB key is often bound to the
220/// action to move to the next widget).
221///
222/// To give focus to the logical _next_ or _previous_ widget in the UI, call the
223/// [nextFocus] or [previousFocus] methods. To give the focus to a widget in a
224/// particular direction, call the [focusInDirection] method.
225///
226/// The policy for what the _next_ or _previous_ widget is, or the widget in a
227/// particular direction, is determined by the [FocusTraversalPolicy] in force.
228///
229/// The ambient policy is determined by looking up the widget hierarchy for a
230/// [DefaultFocusTraversal] widget, and obtaining the focus traversal policy
231/// from it. Different focus nodes can inherit difference policies, so part of
232/// the app can go in widget order, and part can go in reading order, depending
233/// upon the use case.
234///
235/// Predefined policies include [WidgetOrderFocusTraversalPolicy],
236/// [ReadingOrderTraversalPolicy], and [DirectionalFocusTraversalPolicyMixin],
237/// but custom policies can be built based upon these policies.
238///
239/// {@tool snippet --template=stateless_widget_scaffold}
240/// This example shows how a FocusNode should be managed if not using the
241/// [Focus] or [FocusScope] widgets. See the [Focus] widget for a similar
242/// example using [Focus] and [FocusScope] widgets.
243///
244/// ```dart imports
245/// import 'package:flutter/services.dart';
246/// ```
247///
248/// ```dart preamble
249/// class ColorfulButton extends StatefulWidget {
250///   ColorfulButton({Key key}) : super(key: key);
251///
252///   @override
253///   _ColorfulButtonState createState() => _ColorfulButtonState();
254/// }
255///
256/// class _ColorfulButtonState extends State<ColorfulButton> {
257///   FocusNode _node;
258///   bool _focused = false;
259///   FocusAttachment _nodeAttachment;
260///   Color _color = Colors.white;
261///
262///   @override
263///   void initState() {
264///     super.initState();
265///     _node = FocusNode(debugLabel: 'Button');
266///     _node.addListener(_handleFocusChange);
267///     _nodeAttachment = _node.attach(context, onKey: _handleKeyPress);
268///   }
269///
270///   void _handleFocusChange() {
271///     if (_node.hasFocus != _focused) {
272///       setState(() {
273///         _focused = _node.hasFocus;
274///       });
275///     }
276///   }
277///
278///   bool _handleKeyPress(FocusNode node, RawKeyEvent event) {
279///     if (event is RawKeyDownEvent) {
280///       print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}');
281///       if (event.logicalKey == LogicalKeyboardKey.keyR) {
282///         print('Changing color to red.');
283///         setState(() {
284///           _color = Colors.red;
285///         });
286///         return true;
287///       } else if (event.logicalKey == LogicalKeyboardKey.keyG) {
288///         print('Changing color to green.');
289///         setState(() {
290///           _color = Colors.green;
291///         });
292///         return true;
293///       } else if (event.logicalKey == LogicalKeyboardKey.keyB) {
294///         print('Changing color to blue.');
295///         setState(() {
296///           _color = Colors.blue;
297///         });
298///         return true;
299///       }
300///     }
301///     return false;
302///   }
303///
304///   @override
305///   void dispose() {
306///     _node.removeListener(_handleFocusChange);
307///     // The attachment will automatically be detached in dispose().
308///     _node.dispose();
309///     super.dispose();
310///   }
311///
312///   @override
313///   Widget build(BuildContext context) {
314///     _nodeAttachment.reparent();
315///     return GestureDetector(
316///       onTap: () {
317///         if (_focused) {
318///             _node.unfocus();
319///         } else {
320///            _node.requestFocus();
321///         }
322///       },
323///       child: Center(
324///         child: Container(
325///           width: 400,
326///           height: 100,
327///           color: _focused ? _color : Colors.white,
328///           alignment: Alignment.center,
329///           child: Text(
330///               _focused ? "I'm in color! Press R,G,B!" : 'Press to focus'),
331///         ),
332///       ),
333///     );
334///   }
335/// }
336/// ```
337///
338/// ```dart
339/// Widget build(BuildContext context) {
340///   final TextTheme textTheme = Theme.of(context).textTheme;
341///   return DefaultTextStyle(
342///     style: textTheme.display1,
343///     child: ColorfulButton(),
344///   );
345/// }
346/// ```
347/// {@end-tool}
348///
349/// See also:
350///
351///   * [Focus], a widget that manages a [FocusNode] and provides access to
352///     focus information and actions to its descendant widgets.
353///   * [FocusScope], a widget that manages a [FocusScopeNode] and provides
354///     access to scope information and actions to its descendant widgets.
355///   * [FocusAttachment], a widget that connects a [FocusScopeNode] to the
356///     widget tree.
357///   * [FocusManager], a singleton that manages the focus and distributes key
358///     events to focused nodes.
359///   * [FocusTraversalPolicy], a class used to determine how to move the focus
360///     to other nodes.
361///   * [DefaultFocusTraversal], a widget used to configure the default focus
362///     traversal policy for a widget subtree.
363class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
364  /// Creates a focus node.
365  ///
366  /// The [debugLabel] is ignored on release builds.
367  FocusNode({
368    String debugLabel,
369    FocusOnKeyCallback onKey,
370    bool skipTraversal = false,
371    bool canRequestFocus = true,
372  })  : assert(skipTraversal != null),
373        assert(canRequestFocus != null),
374        _skipTraversal = skipTraversal,
375        _canRequestFocus = canRequestFocus,
376        _onKey = onKey {
377    // Set it via the setter so that it does nothing on release builds.
378    this.debugLabel = debugLabel;
379  }
380
381  /// If true, tells the focus traversal policy to skip over this node for
382  /// purposes of the traversal algorithm.
383  ///
384  /// This may be used to place nodes in the focus tree that may be focused, but
385  /// not traversed, allowing them to receive key events as part of the focus
386  /// chain, but not be traversed to via focus traversal.
387  ///
388  /// This is different from [canRequestFocus] because it only implies that the
389  /// node can't be reached via traversal, not that it can't be focused. It may
390  /// still be focused explicitly.
391  bool get skipTraversal => _skipTraversal;
392  bool _skipTraversal;
393  set skipTraversal(bool value) {
394    if (value != _skipTraversal) {
395      _skipTraversal = value;
396      _notify();
397    }
398  }
399
400  /// If true, this focus node may request the primary focus.
401  ///
402  /// Defaults to true.  Set to false if you want this node to do nothing when
403  /// [requestFocus] is called on it. Does not affect the children of this node,
404  /// and [hasFocus] can still return true if this node is the ancestor of a
405  /// node with primary focus.
406  ///
407  /// This is different than [skipTraversal] because [skipTraversal] still
408  /// allows the node to be focused, just not traversed to via the
409  /// [FocusTraversalPolicy]
410  ///
411  /// Setting [canRequestFocus] to false implies that the node will also be
412  /// skipped for traversal purposes.
413  ///
414  /// See also:
415  ///
416  ///   - [DefaultFocusTraversal], a widget that sets the traversal policy for
417  ///     its descendants.
418  ///   - [FocusTraversalPolicy], a class that can be extended to describe a
419  ///     traversal policy.
420  bool get canRequestFocus => _canRequestFocus;
421  bool _canRequestFocus;
422  set canRequestFocus(bool value) {
423    if (value != _canRequestFocus) {
424      _canRequestFocus = value;
425      if (!_canRequestFocus) {
426        unfocus();
427      }
428      _notify();
429    }
430  }
431
432  /// The context that was supplied to [attach].
433  ///
434  /// This is typically the context for the widget that is being focused, as it
435  /// is used to determine the bounds of the widget.
436  BuildContext get context => _context;
437  BuildContext _context;
438
439  /// Called if this focus node receives a key event while focused (i.e. when
440  /// [hasFocus] returns true).
441  ///
442  /// {@macro flutter.widgets.focus_manager.focus.keyEvents}
443  FocusOnKeyCallback get onKey => _onKey;
444  FocusOnKeyCallback _onKey;
445
446  FocusManager _manager;
447  bool _hasKeyboardToken = false;
448
449  /// Returns the parent node for this object.
450  ///
451  /// All nodes except for the root [FocusScopeNode] ([FocusManager.rootScope])
452  /// will be given a parent when they are added to the focus tree, which is
453  /// done using [FocusAttachment.reparent].
454  FocusNode get parent => _parent;
455  FocusNode _parent;
456
457  /// An iterator over the children of this node.
458  Iterable<FocusNode> get children => _children;
459  final List<FocusNode> _children = <FocusNode>[];
460
461  /// An iterator over the children that are allowed to be traversed by the
462  /// [FocusTraversalPolicy].
463  Iterable<FocusNode> get traversalChildren {
464    return children.where(
465      (FocusNode node) => !node.skipTraversal && node.canRequestFocus,
466    );
467  }
468
469  /// A debug label that is used for diagnostic output.
470  ///
471  /// Will always return null in release builds.
472  String get debugLabel => _debugLabel;
473  String _debugLabel;
474  set debugLabel(String value) {
475    assert(() {
476      // Only set the value in debug builds.
477      _debugLabel = value;
478      return true;
479    }());
480  }
481
482  FocusAttachment _attachment;
483
484  /// An [Iterable] over the hierarchy of children below this one, in
485  /// depth-first order.
486  Iterable<FocusNode> get descendants sync* {
487    for (FocusNode child in _children) {
488      yield* child.descendants;
489      yield child;
490    }
491  }
492
493  /// Returns all descendants which do not have the [skipTraversal] flag set.
494  Iterable<FocusNode> get traversalDescendants => descendants.where((FocusNode node) => !node.skipTraversal && node.canRequestFocus);
495
496  /// An [Iterable] over the ancestors of this node.
497  ///
498  /// Iterates the ancestors of this node starting at the parent and iterating
499  /// over successively more remote ancestors of this node, ending at the root
500  /// [FocusScope] ([FocusManager.rootScope]).
501  Iterable<FocusNode> get ancestors sync* {
502    FocusNode parent = _parent;
503    while (parent != null) {
504      yield parent;
505      parent = parent._parent;
506    }
507  }
508
509  /// Whether this node has input focus.
510  ///
511  /// A [FocusNode] has focus when it is an ancestor of a node that returns true
512  /// from [hasPrimaryFocus], or it has the primary focus itself.
513  ///
514  /// The [hasFocus] accessor is different from [hasPrimaryFocus] in that
515  /// [hasFocus] is true if the node is anywhere in the focus chain, but for
516  /// [hasPrimaryFocus] the node must to be at the end of the chain to return
517  /// true.
518  ///
519  /// A node that returns true for [hasFocus] will receive key events if none of
520  /// its focused descendants returned true from their [onKey] handler.
521  ///
522  /// This object is a [ChangeNotifier], and notifies its [Listenable] listeners
523  /// (registered via [addListener]) whenever this value changes.
524  ///
525  /// See also:
526  ///
527  ///   * [Focus.isAt], which is a static method that will return the focus
528  ///     state of the nearest ancestor [Focus] widget's focus node.
529  bool get hasFocus {
530    if (_manager?.primaryFocus == null) {
531      return false;
532    }
533    if (hasPrimaryFocus) {
534      return true;
535    }
536    return _manager.primaryFocus.ancestors.contains(this);
537  }
538
539  /// Returns true if this node currently has the application-wide input focus.
540  ///
541  /// A [FocusNode] has the primary focus when the node is focused in its
542  /// nearest ancestor [FocusScopeNode] and [hasFocus] is true for all its
543  /// ancestor nodes, but none of its descendants.
544  ///
545  /// This is different from [hasFocus] in that [hasFocus] is true if the node
546  /// is anywhere in the focus chain, but here the node has to be at the end of
547  /// the chain to return true.
548  ///
549  /// A node that returns true for [hasPrimaryFocus] will be the first node to
550  /// receive key events through its [onKey] handler.
551  ///
552  /// This object notifies its listeners whenever this value changes.
553  bool get hasPrimaryFocus => _manager?.primaryFocus == this;
554
555  /// Returns the [FocusHighlightMode] that is currently in effect for this node.
556  FocusHighlightMode get highlightMode => WidgetsBinding.instance.focusManager.highlightMode;
557
558  /// Returns the nearest enclosing scope node above this node, including
559  /// this node, if it's a scope.
560  ///
561  /// Returns null if no scope is found.
562  ///
563  /// Use [enclosingScope] to look for scopes above this node.
564  FocusScopeNode get nearestScope => enclosingScope;
565
566  /// Returns the nearest enclosing scope node above this node, or null if the
567  /// node has not yet be added to the focus tree.
568  ///
569  /// If this node is itself a scope, this will only return ancestors of this
570  /// scope.
571  ///
572  /// Use [nearestScope] to start at this node instead of above it.
573  FocusScopeNode get enclosingScope {
574    return ancestors.firstWhere((FocusNode node) => node is FocusScopeNode, orElse: () => null);
575  }
576
577  /// Returns the size of the attached widget's [RenderObject], in logical
578  /// units.
579  Size get size {
580    assert(
581        context != null,
582        "Tried to get the size of a focus node that didn't have its context set yet.\n"
583        'The context needs to be set before trying to evaluate traversal policies. This '
584        'is typically done with the attach method.');
585    return context.findRenderObject().semanticBounds.size;
586  }
587
588  /// Returns the global offset to the upper left corner of the attached
589  /// widget's [RenderObject], in logical units.
590  Offset get offset {
591    assert(
592        context != null,
593        "Tried to get the offset of a focus node that didn't have its context set yet.\n"
594        'The context needs to be set before trying to evaluate traversal policies. This '
595        'is typically done with the attach method.');
596    final RenderObject object = context.findRenderObject();
597    return MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
598  }
599
600  /// Returns the global rectangle of the attached widget's [RenderObject], in
601  /// logical units.
602  Rect get rect {
603    assert(
604        context != null,
605        "Tried to get the bounds of a focus node that didn't have its context set yet.\n"
606        'The context needs to be set before trying to evaluate traversal policies. This '
607        'is typically done with the attach method.');
608    final RenderObject object = context.findRenderObject();
609    final Offset globalOffset = MatrixUtils.transformPoint(object.getTransformTo(null), object.semanticBounds.topLeft);
610    return globalOffset & object.semanticBounds.size;
611  }
612
613  /// Removes focus from a node that has the primary focus, and cancels any
614  /// outstanding requests to focus it.
615  ///
616  /// Calling [requestFocus] sends a request to the [FocusManager] to make that
617  /// node the primary focus, which schedules a microtask to resolve the latest
618  /// request into an update of the focus state on the tree. Calling [unfocus]
619  /// cancels a request that has been requested, but not yet acted upon.
620  ///
621  /// This method is safe to call regardless of whether this node has ever
622  /// requested focus.
623  ///
624  /// Has no effect on nodes that return true from [hasFocus], but false from
625  /// [hasPrimaryFocus].
626  void unfocus() {
627    if (hasPrimaryFocus) {
628      final FocusScopeNode scope = enclosingScope;
629      assert(scope != null, 'Node has primary focus, but no enclosingScope.');
630      scope._focusedChildren.remove(this);
631      _manager?._willUnfocusNode(this);
632      return;
633    }
634    if (hasFocus) {
635      // If we are in the focus chain, but not the primary focus, then unfocus
636      // the primary instead.
637      _manager.primaryFocus.unfocus();
638    }
639  }
640
641  /// Removes the keyboard token from this focus node if it has one.
642  ///
643  /// This mechanism helps distinguish between an input control gaining focus by
644  /// default and gaining focus as a result of an explicit user action.
645  ///
646  /// When a focus node requests the focus (either via
647  /// [FocusScopeNode.requestFocus] or [FocusScopeNode.autofocus]), the focus
648  /// node receives a keyboard token if it does not already have one. Later,
649  /// when the focus node becomes focused, the widget that manages the
650  /// [TextInputConnection] should show the keyboard (i.e. call
651  /// [TextInputConnection.show]) only if it successfully consumes the keyboard
652  /// token from the focus node.
653  ///
654  /// Returns true if this method successfully consumes the keyboard token.
655  bool consumeKeyboardToken() {
656    if (!_hasKeyboardToken) {
657      return false;
658    }
659    _hasKeyboardToken = false;
660    return true;
661  }
662
663  // Marks the node as dirty, meaning that it needs to notify listeners of a
664  // focus change the next time focus is resolved by the manager.
665  void _markAsDirty({FocusNode newFocus}) {
666    if (_manager != null) {
667      // If we have a manager, then let it handle the focus change.
668      _manager._dirtyNodes?.add(this);
669      _manager._markNeedsUpdate(newFocus: newFocus);
670    } else {
671      // If we don't have a manager, then change the focus locally.
672      newFocus?._setAsFocusedChild();
673      newFocus?._notify();
674      if (newFocus != this) {
675        _notify();
676      }
677    }
678  }
679
680  // Removes the given FocusNode and its children as a child of this node.
681  @mustCallSuper
682  void _removeChild(FocusNode node) {
683    assert(node != null);
684    assert(_children.contains(node), "Tried to remove a node that wasn't a child.");
685    assert(node._parent == this);
686    assert(node._manager == _manager);
687
688    node.enclosingScope?._focusedChildren?.remove(node);
689
690    node._parent = null;
691    _children.remove(node);
692    assert(_manager == null || !_manager.rootScope.descendants.contains(node));
693  }
694
695  void _updateManager(FocusManager manager) {
696    _manager = manager;
697    for (FocusNode descendant in descendants) {
698      descendant._manager = manager;
699    }
700  }
701
702  // Used by FocusAttachment.reparent to perform the actual parenting operation.
703  @mustCallSuper
704  void _reparent(FocusNode child) {
705    assert(child != null);
706    assert(child != this, 'Tried to make a child into a parent of itself.');
707    if (child._parent == this) {
708      assert(_children.contains(child), "Found a node that says it's a child, but doesn't appear in the child list.");
709      // The child is already a child of this parent.
710      return;
711    }
712    assert(_manager == null || child != _manager.rootScope, "Reparenting the root node isn't allowed.");
713    assert(!ancestors.contains(child), 'The supplied child is already an ancestor of this node. Loops are not allowed.');
714    final FocusScopeNode oldScope = child.enclosingScope;
715    final bool hadFocus = child.hasFocus;
716    child._parent?._removeChild(child);
717    _children.add(child);
718    child._parent = this;
719    child._updateManager(_manager);
720    if (hadFocus) {
721      // Update the focus chain for the current focus without changing it.
722      _manager?.primaryFocus?._setAsFocusedChild();
723    }
724    if (oldScope != null && child.context != null && child.enclosingScope != oldScope) {
725      DefaultFocusTraversal.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope);
726    }
727  }
728
729  /// Called by the _host_ [StatefulWidget] to attach a [FocusNode] to the
730  /// widget tree.
731  ///
732  /// In order to attach a [FocusNode] to the widget tree, call [attach],
733  /// typically from the [StatefulWidget]'s [State.initState] method.
734  ///
735  /// If the focus node in the host widget is swapped out, the new node will
736  /// need to be attached. [FocusAttachment.detach] should be called on the old
737  /// node, and then [attach] called on the new node. This typically happens in
738  /// the [State.didUpdateWidget] method.
739  @mustCallSuper
740  FocusAttachment attach(BuildContext context, {FocusOnKeyCallback onKey}) {
741    _context = context;
742    _onKey = onKey ?? _onKey;
743    _attachment = FocusAttachment._(this);
744    return _attachment;
745  }
746
747  @override
748  void dispose() {
749    _manager?._willDisposeFocusNode(this);
750    _attachment?.detach();
751    super.dispose();
752  }
753
754  @mustCallSuper
755  void _notify() {
756    if (_parent == null) {
757      // no longer part of the tree, so don't notify.
758      return;
759    }
760    if (hasPrimaryFocus) {
761      _setAsFocusedChild();
762    }
763    notifyListeners();
764  }
765
766  /// Requests the primary focus for this node, or for a supplied [node], which
767  /// will also give focus to its [ancestors].
768  ///
769  /// If called without a node, request focus for this node.
770  ///
771  /// If the given [node] is not yet a part of the focus tree, then this method
772  /// will add the [node] as a child of this node before requesting focus.
773  ///
774  /// If the given [node] is a [FocusScopeNode] and that focus scope node has a
775  /// non-null [focusedChild], then request the focus for the focused child.
776  /// This process is recursive and continues until it encounters either a focus
777  /// scope node with a null focused child or an ordinary (non-scope)
778  /// [FocusNode] is found.
779  ///
780  /// The node is notified that it has received the primary focus in a
781  /// microtask, so notification may lag the request by up to one frame.
782  void requestFocus([FocusNode node]) {
783    if (node != null) {
784      if (node._parent == null) {
785        _reparent(node);
786      }
787      assert(node.ancestors.contains(this), 'Focus was requested for a node that is not a descendant of the scope from which it was requested.');
788      node._doRequestFocus();
789      return;
790    }
791    _doRequestFocus();
792  }
793
794  // Note that this is overridden in FocusScopeNode.
795  void _doRequestFocus() {
796    if (!canRequestFocus) {
797      return;
798    }
799    _setAsFocusedChild();
800    if (hasPrimaryFocus) {
801      return;
802    }
803    _hasKeyboardToken = true;
804    _markAsDirty(newFocus: this);
805  }
806
807  /// Sets this node as the [FocusScopeNode.focusedChild] of the enclosing
808  /// scope.
809  ///
810  /// Sets this node as the focused child for the enclosing scope, and that
811  /// scope as the focused child for the scope above it, etc., until it reaches
812  /// the root node. It doesn't change the primary focus, it just changes what
813  /// node would be focused if the enclosing scope receives focus, and keeps
814  /// track of previously focused children in that scope, so that if the focused
815  /// child in that scope is removed, the previous focus returns.
816  void _setAsFocusedChild() {
817    FocusNode scopeFocus = this;
818    for (FocusScopeNode ancestor in ancestors.whereType<FocusScopeNode>()) {
819      assert(scopeFocus != ancestor, 'Somehow made a loop by setting focusedChild to its scope.');
820      // Remove it anywhere in the focused child history.
821      ancestor._focusedChildren.remove(scopeFocus);
822      // Add it to the end of the list, which is also the top of the queue: The
823      // end of the list represents the currently focused child.
824      ancestor._focusedChildren.add(scopeFocus);
825      scopeFocus = ancestor;
826    }
827  }
828
829  /// Request to move the focus to the next focus node, by calling the
830  /// [FocusTraversalPolicy.next] method.
831  ///
832  /// Returns true if it successfully found a node and requested focus.
833  bool nextFocus() => DefaultFocusTraversal.of(context).next(this);
834
835  /// Request to move the focus to the previous focus node, by calling the
836  /// [FocusTraversalPolicy.previous] method.
837  ///
838  /// Returns true if it successfully found a node and requested focus.
839  bool previousFocus() => DefaultFocusTraversal.of(context).previous(this);
840
841  /// Request to move the focus to the nearest focus node in the given
842  /// direction, by calling the [FocusTraversalPolicy.inDirection] method.
843  ///
844  /// Returns true if it successfully found a node and requested focus.
845  bool focusInDirection(TraversalDirection direction) => DefaultFocusTraversal.of(context).inDirection(this, direction);
846
847  @override
848  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
849    super.debugFillProperties(properties);
850    properties.add(DiagnosticsProperty<BuildContext>('context', context, defaultValue: null));
851    properties.add(FlagProperty('canRequestFocus', value: canRequestFocus, ifFalse: 'NOT FOCUSABLE', defaultValue: true));
852    properties.add(FlagProperty('hasFocus', value: hasFocus, ifTrue: 'FOCUSED', defaultValue: false));
853    properties.add(StringProperty('debugLabel', debugLabel, defaultValue: null));
854  }
855
856  @override
857  List<DiagnosticsNode> debugDescribeChildren() {
858    int count = 1;
859    return _children.map<DiagnosticsNode>((FocusNode child) {
860      return child.toDiagnosticsNode(name: 'Child ${count++}');
861    }).toList();
862  }
863}
864
865/// A subclass of [FocusNode] that acts as a scope for its descendants,
866/// maintaining information about which descendant is currently or was last
867/// focused.
868///
869/// _Please see the [FocusScope] and [Focus] widgets, which are utility widgets
870/// that manage their own [FocusScopeNode]s and [FocusNode]s, respectively. If
871/// they aren't appropriate, [FocusScopeNode]s can be managed directly._
872///
873/// [FocusScopeNode] organizes [FocusNodes] into _scopes_. Scopes form sub-trees
874/// of nodes that can be traversed as a group. Within a scope, the most recent
875/// nodes to have focus are remembered, and if a node is focused and then
876/// removed, the original node receives focus again.
877///
878/// From a [FocusScopeNode], calling [setFirstFocus], sets the given focus scope
879/// as the [focusedChild] of this node, adopting if it isn't already part of the
880/// focus tree.
881///
882/// {@macro flutter.widgets.focus_manager.focus.lifecycle}
883/// {@macro flutter.widgets.focus_manager.focus.keyEvents}
884///
885/// See also:
886///
887///   * [Focus], a widget that manages a [FocusNode] and provides access to
888///     focus information and actions to its descendant widgets.
889///   * [FocusScope], a widget that manages a [FocusScopeNode] and provides
890///     access to scope information and actions to its descendant widgets.
891///   * [FocusAttachment], a widget that connects a [FocusScopeNode] to the
892///     focus tree.
893///   * [FocusManager], a singleton that manages the focus and distributes key
894///     events to focused nodes.
895class FocusScopeNode extends FocusNode {
896  /// Creates a FocusScope node.
897  ///
898  /// All parameters are optional.
899  FocusScopeNode({
900    String debugLabel,
901    FocusOnKeyCallback onKey,
902  }) : super(debugLabel: debugLabel, onKey: onKey);
903
904  @override
905  FocusScopeNode get nearestScope => this;
906
907  /// Returns true if this scope is the focused child of its parent scope.
908  bool get isFirstFocus => enclosingScope.focusedChild == this;
909
910  /// Returns the child of this node that should receive focus if this scope
911  /// node receives focus.
912  ///
913  /// If [hasFocus] is true, then this points to the child of this node that is
914  /// currently focused.
915  ///
916  /// Returns null if there is no currently focused child.
917  FocusNode get focusedChild {
918    assert(_focusedChildren.isEmpty || _focusedChildren.last.enclosingScope == this, 'Focused child does not have the same idea of its enclosing scope as the scope does.');
919    return _focusedChildren.isNotEmpty ? _focusedChildren.last : null;
920  }
921
922  // A stack of the children that have been set as the focusedChild, most recent
923  // last (which is the top of the stack).
924  final List<FocusNode> _focusedChildren = <FocusNode>[];
925
926  /// Make the given [scope] the active child scope for this scope.
927  ///
928  /// If the given [scope] is not yet a part of the focus tree, then add it to
929  /// the tree as a child of this scope. If it is already part of the focus
930  /// tree, the given scope must be a descendant of this scope.
931  void setFirstFocus(FocusScopeNode scope) {
932    assert(scope != null);
933    assert(scope != this, 'Unexpected self-reference in setFirstFocus.');
934    if (scope._parent == null) {
935      _reparent(scope);
936    }
937    assert(scope.ancestors.contains(this), '$FocusScopeNode $scope must be a child of $this to set it as first focus.');
938    if (hasFocus) {
939      scope._doRequestFocus();
940    } else {
941      scope._setAsFocusedChild();
942    }
943  }
944
945  /// If this scope lacks a focus, request that the given node become the focus.
946  ///
947  /// If the given node is not yet part of the focus tree, then add it as a
948  /// child of this node.
949  ///
950  /// Useful for widgets that wish to grab the focus if no other widget already
951  /// has the focus.
952  ///
953  /// The node is notified that it has received the primary focus in a
954  /// microtask, so notification may lag the request by up to one frame.
955  void autofocus(FocusNode node) {
956    if (focusedChild == null) {
957      if (node._parent == null) {
958        _reparent(node);
959      }
960      assert(node.ancestors.contains(this), 'Autofocus was requested for a node that is not a descendant of the scope from which it was requested.');
961      node._doRequestFocus();
962    }
963  }
964
965  @override
966  void _doRequestFocus() {
967    if (!canRequestFocus) {
968      return;
969    }
970
971    // Start with the primary focus as the focused child of this scope, if there
972    // is one. Otherwise start with this node itself.
973    FocusNode primaryFocus = focusedChild ?? this;
974    // Keep going down through scopes until the ultimately focusable item is
975    // found, a scope doesn't have a focusedChild, or a non-scope is
976    // encountered.
977    while (primaryFocus is FocusScopeNode && primaryFocus.focusedChild != null) {
978      final FocusScopeNode scope = primaryFocus;
979      primaryFocus = scope.focusedChild;
980    }
981    if (primaryFocus is FocusScopeNode) {
982      // We didn't find a FocusNode at the leaf, so we're focusing the scope.
983      _setAsFocusedChild();
984      _markAsDirty(newFocus: primaryFocus);
985    } else {
986      // We found a FocusScope at the leaf, so ask it to focus itself instead of
987      // this scope. That will cause this scope to return true from hasFocus,
988      // but false from hasPrimaryFocus.
989      primaryFocus.requestFocus();
990    }
991  }
992
993  @override
994  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
995    super.debugFillProperties(properties);
996    properties.add(DiagnosticsProperty<FocusNode>('focusedChild', focusedChild, defaultValue: null));
997  }
998}
999
1000/// An enum to describe which kind of focus highlight behavior to use when
1001/// displaying focus information.
1002enum FocusHighlightMode {
1003  /// Touch interfaces will not show the focus highlight except for controls
1004  /// which bring up the soft keyboard.
1005  ///
1006  /// If a device that uses a traditional mouse and keyboard has a touch screen
1007  /// attached, it can also enter `touch` mode if the user is using the touch
1008  /// screen.
1009  touch,
1010
1011  /// Traditional interfaces (keyboard and mouse) will show the currently
1012  /// focused control via a focus highlight of some sort.
1013  ///
1014  /// If a touch device (like a mobile phone) has a keyboard and/or mouse
1015  /// attached, it also can enter `traditional` mode if the user is using these
1016  /// input devices.
1017  traditional,
1018}
1019
1020/// An enum to describe how the current value of [FocusManager.highlightMode] is
1021/// determined. The strategy is set on [FocusManager.highlightStrategy].
1022enum FocusHighlightStrategy {
1023  /// Automatic switches between the various highlight modes based on the last
1024  /// kind of input that was received. This is the default.
1025  automatic,
1026
1027  /// [FocusManager.highlightMode] always returns [FocusHighlightMode.touch].
1028  alwaysTouch,
1029
1030  /// [FocusManager.highlightMode] always returns [FocusHighlightMode.traditional].
1031  alwaysTraditional,
1032}
1033
1034/// Manages the focus tree.
1035///
1036/// The focus tree keeps track of which [FocusNode] is the user's current
1037/// keyboard focus. The widget that owns the [FocusNode] often listens for
1038/// keyboard events.
1039///
1040/// The focus manager is responsible for holding the [FocusScopeNode] that is
1041/// the root of the focus tree and tracking which [FocusNode] has the overall
1042/// focus.
1043///
1044/// The [FocusManager] is held by the [WidgetsBinding] as
1045/// [WidgetsBinding.focusManager]. The [FocusManager] is rarely accessed
1046/// directly. Instead, to find the [FocusScopeNode] for a given [BuildContext],
1047/// use [FocusScope.of].
1048///
1049/// The [FocusManager] knows nothing about [FocusNode]s other than the one that
1050/// is currently focused. If a [FocusScopeNode] is removed, then the
1051/// [FocusManager] will attempt to focus the next [FocusScopeNode] in the focus
1052/// tree that it maintains, but if the current focus in that [FocusScopeNode] is
1053/// null, it will stop there, and no [FocusNode] will have focus.
1054///
1055/// See also:
1056///
1057///  * [FocusNode], which is a node in the focus tree that can receive focus.
1058///  * [FocusScopeNode], which is a node in the focus tree used to collect
1059///    subtrees into groups.
1060///  * [Focus.of], which provides the nearest ancestor [FocusNode] for a given
1061///    [BuildContext].
1062///  * [FocusScope.of], which provides the nearest ancestor [FocusScopeNode] for
1063///    a given [BuildContext].
1064class FocusManager with DiagnosticableTreeMixin {
1065  /// Creates an object that manages the focus tree.
1066  ///
1067  /// This constructor is rarely called directly. To access the [FocusManager],
1068  /// consider using [WidgetsBinding.focusManager] instead.
1069  FocusManager() {
1070    rootScope._manager = this;
1071    RawKeyboard.instance.addListener(_handleRawKeyEvent);
1072    RendererBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
1073  }
1074
1075  bool _lastInteractionWasTouch = true;
1076
1077  /// Sets the strategy by which [highlightMode] is determined.
1078  ///
1079  /// If set to [FocusHighlightStrategy.automatic], then the highlight mode will
1080  /// change depending upon the interaction mode used last. For instance, if the
1081  /// last interaction was a touch interaction, then [highlightMode] will return
1082  /// [FocusHighlightMode.touch], and focus highlights will only appear on
1083  /// widgets that bring up a soft keyboard. If the last interaction was a
1084  /// non-touch interaction (hardware keyboard press, mouse click, etc.), then
1085  /// [highlightMode] will return [FocusHighlightMode.traditional], and focus
1086  /// highlights will appear on all widgets.
1087  ///
1088  /// If set to [FocusHighlightStrategy.alwaysTouch] or
1089  /// [FocusHighlightStrategy.alwaysTraditional], then [highlightMode] will
1090  /// always return [FocusHighlightMode.touch] or
1091  /// [FocusHighlightMode.traditional], respectively, regardless of the last UI
1092  /// interaction type.
1093  ///
1094  /// The initial value of [highlightMode] depends upon the value of
1095  /// [defaultTargetPlatform] and
1096  /// [RendererBinding.instance.mouseTracker.mouseIsConnected], making a guess
1097  /// about which interaction is most appropriate for the initial interaction
1098  /// mode.
1099  ///
1100  /// Defaults to [FocusHighlightStrategy.automatic].
1101  FocusHighlightStrategy get highlightStrategy => _highlightStrategy;
1102  FocusHighlightStrategy _highlightStrategy = FocusHighlightStrategy.automatic;
1103  set highlightStrategy(FocusHighlightStrategy highlightStrategy) {
1104    _highlightStrategy = highlightStrategy;
1105    _updateHighlightMode();
1106  }
1107
1108  /// Indicates the current interaction mode for focus highlights.
1109  ///
1110  /// The value returned depends upon the [highlightStrategy] used, and possibly
1111  /// (depending on the value of [highlightStrategy]) the most recent
1112  /// interaction mode that they user used.
1113  ///
1114  /// If [highlightMode] returns [FocusHighlightMode.touch], then widgets should
1115  /// not draw their focus highlight unless they perform text entry.
1116  ///
1117  /// If [highlightMode] returns [FocusHighlightMode.traditional], then widgets should
1118  /// draw their focus highlight whenever they are focused.
1119  FocusHighlightMode get highlightMode => _highlightMode;
1120  FocusHighlightMode _highlightMode = FocusHighlightMode.touch;
1121
1122  // Update function to be called whenever the state relating to highlightMode
1123  // changes.
1124  void _updateHighlightMode() {
1125    // Assume that if we're on one of these mobile platforms, or if there's no
1126    // mouse connected, that the initial interaction will be touch-based, and
1127    // that it's traditional mouse and keyboard on all others.
1128    //
1129    // This only affects the initial value: the ongoing value is updated as soon
1130    // as any input events are received.
1131    _lastInteractionWasTouch ??= Platform.isAndroid || Platform.isIOS || !WidgetsBinding.instance.mouseTracker.mouseIsConnected;
1132    FocusHighlightMode newMode;
1133    switch (highlightStrategy) {
1134      case FocusHighlightStrategy.automatic:
1135        if (_lastInteractionWasTouch) {
1136          newMode = FocusHighlightMode.touch;
1137        } else {
1138          newMode = FocusHighlightMode.traditional;
1139        }
1140        break;
1141      case FocusHighlightStrategy.alwaysTouch:
1142        newMode = FocusHighlightMode.touch;
1143        break;
1144      case FocusHighlightStrategy.alwaysTraditional:
1145        newMode = FocusHighlightMode.traditional;
1146        break;
1147    }
1148    if (newMode != _highlightMode) {
1149      _highlightMode = newMode;
1150      _notifyHighlightModeListeners();
1151    }
1152  }
1153
1154  // The list of listeners for [highlightMode] state changes.
1155  ObserverList<ValueChanged<FocusHighlightMode>> _listeners;
1156
1157  /// Register a closure to be called when the [FocusManager] notifies its listeners
1158  /// that the value of [highlightMode] has changed.
1159  void addHighlightModeListener(ValueChanged<FocusHighlightMode> listener) {
1160    _listeners ??= ObserverList<ValueChanged<FocusHighlightMode>>();
1161    _listeners.add(listener);
1162  }
1163
1164  /// Remove a previously registered closure from the list of closures that the
1165  /// [FocusManager] notifies.
1166  void removeHighlightModeListener(ValueChanged<FocusHighlightMode> listener) {
1167    _listeners?.remove(listener);
1168  }
1169
1170  void _notifyHighlightModeListeners() {
1171    if (_listeners != null) {
1172      final List<ValueChanged<FocusHighlightMode>> localListeners = List<ValueChanged<FocusHighlightMode>>.from(_listeners);
1173      for (ValueChanged<FocusHighlightMode> listener in localListeners) {
1174        try {
1175          if (_listeners.contains(listener)) {
1176            listener(_highlightMode);
1177          }
1178        } catch (exception, stack) {
1179          FlutterError.reportError(FlutterErrorDetails(
1180            exception: exception,
1181            stack: stack,
1182            library: 'widgets library',
1183            context: ErrorDescription('while dispatching notifications for $runtimeType'),
1184            informationCollector: () sync* {
1185              yield DiagnosticsProperty<FocusManager>(
1186                'The $runtimeType sending notification was',
1187                this,
1188                style: DiagnosticsTreeStyle.errorProperty,
1189              );
1190            },
1191          ));
1192        }
1193      }
1194    }
1195  }
1196
1197  /// The root [FocusScopeNode] in the focus tree.
1198  ///
1199  /// This field is rarely used directly. To find the nearest [FocusScopeNode]
1200  /// for a given [FocusNode], call [FocusNode.nearestScope].
1201  final FocusScopeNode rootScope = FocusScopeNode(debugLabel: 'Root Focus Scope');
1202
1203  void _handlePointerEvent(PointerEvent event) {
1204    bool newState;
1205    switch (event.kind) {
1206      case PointerDeviceKind.touch:
1207      case PointerDeviceKind.stylus:
1208      case PointerDeviceKind.invertedStylus:
1209        newState = true;
1210        break;
1211      case PointerDeviceKind.mouse:
1212      case PointerDeviceKind.unknown:
1213        newState = false;
1214        break;
1215    }
1216    if (_lastInteractionWasTouch != newState) {
1217      _lastInteractionWasTouch = newState;
1218      _updateHighlightMode();
1219    }
1220  }
1221
1222  void _handleRawKeyEvent(RawKeyEvent event) {
1223    // Update this first, since things responding to the keys might look at the
1224    // highlight mode, and it should be accurate.
1225    if (_lastInteractionWasTouch) {
1226      _lastInteractionWasTouch = false;
1227      _updateHighlightMode();
1228    }
1229
1230    // Walk the current focus from the leaf to the root, calling each one's
1231    // onKey on the way up, and if one responds that they handled it, stop.
1232    if (_primaryFocus == null) {
1233      return;
1234    }
1235    Iterable<FocusNode> allNodes(FocusNode node) sync* {
1236      yield node;
1237      for (FocusNode ancestor in node.ancestors) {
1238        yield ancestor;
1239      }
1240    }
1241
1242    for (FocusNode node in allNodes(_primaryFocus)) {
1243      if (node.onKey != null && node.onKey(node, event)) {
1244        break;
1245      }
1246    }
1247  }
1248
1249  /// The node that currently has the primary focus.
1250  FocusNode get primaryFocus => _primaryFocus;
1251  FocusNode _primaryFocus;
1252
1253  // The node that has requested to have the primary focus, but hasn't been
1254  // given it yet.
1255  FocusNode _nextFocus;
1256  // The set of nodes that need to notify their listeners of changes at the next
1257  // update.
1258  final Set<FocusNode> _dirtyNodes = <FocusNode>{};
1259
1260  // Called to indicate that the given node is being disposed.
1261  void _willDisposeFocusNode(FocusNode node) {
1262    assert(node != null);
1263    assert(_focusDebug('Disposing of node:', <String>[node.toString(), 'with enclosing scope ${node.enclosingScope}']));
1264    _willUnfocusNode(node);
1265    _dirtyNodes.remove(node);
1266  }
1267
1268  // Called to indicate that the given node is being unfocused, and that any
1269  // pending request to be focused should be canceled.
1270  void _willUnfocusNode(FocusNode node) {
1271    assert(node != null);
1272    assert(_focusDebug('Unfocusing node $node'));
1273    if (_primaryFocus == node) {
1274      _primaryFocus = null;
1275      _dirtyNodes.add(node);
1276      _markNeedsUpdate();
1277    }
1278    if (_nextFocus == node) {
1279      _nextFocus = null;
1280      _dirtyNodes.add(node);
1281      _markNeedsUpdate();
1282    }
1283    assert(_focusDebug('Unfocused node $node:', <String>['primary focus is $_primaryFocus', 'next focus will be $_nextFocus']));
1284  }
1285
1286  // True indicates that there is an update pending.
1287  bool _haveScheduledUpdate = false;
1288
1289  // Request that an update be scheduled, optionally requesting focus for the
1290  // given newFocus node.
1291  void _markNeedsUpdate({FocusNode newFocus}) {
1292    // If newFocus isn't specified, then don't mess with _nextFocus, just
1293    // schedule the update.
1294    _nextFocus = newFocus ?? _nextFocus;
1295    assert(_focusDebug('Scheduling update, next focus will be $_nextFocus'));
1296    if (_haveScheduledUpdate) {
1297      return;
1298    }
1299    _haveScheduledUpdate = true;
1300    scheduleMicrotask(_applyFocusChange);
1301  }
1302
1303  void _applyFocusChange() {
1304    _haveScheduledUpdate = false;
1305    assert(_focusDebug('Refreshing focus state. Next focus will be $_nextFocus'));
1306    final FocusNode previousFocus = _primaryFocus;
1307    if (_primaryFocus == null && _nextFocus == null) {
1308      // If we don't have any current focus, and nobody has asked to focus yet,
1309      // then pick a first one using widget order as a default.
1310      _nextFocus = rootScope;
1311    }
1312    if (_nextFocus != null && _nextFocus != _primaryFocus) {
1313      _primaryFocus = _nextFocus;
1314      final Set<FocusNode> previousPath = previousFocus?.ancestors?.toSet() ?? <FocusNode>{};
1315      final Set<FocusNode> nextPath = _nextFocus.ancestors.toSet();
1316      // Notify nodes that are newly focused.
1317      _dirtyNodes.addAll(nextPath.difference(previousPath));
1318      // Notify nodes that are no longer focused
1319      _dirtyNodes.addAll(previousPath.difference(nextPath));
1320      _nextFocus = null;
1321    }
1322    if (previousFocus != _primaryFocus) {
1323      assert(_focusDebug('Updating focus from $previousFocus to $_primaryFocus'));
1324      if (previousFocus != null) {
1325        _dirtyNodes.add(previousFocus);
1326      }
1327      if (_primaryFocus != null) {
1328        _dirtyNodes.add(_primaryFocus);
1329      }
1330    }
1331    assert(_focusDebug('Notifying ${_dirtyNodes.length} dirty nodes:', _dirtyNodes.toList().map<String>((FocusNode node) => node.toString())));
1332    for (FocusNode node in _dirtyNodes) {
1333      node._notify();
1334    }
1335    _dirtyNodes.clear();
1336    assert(() {
1337      if (_kDebugFocus) {
1338        debugDumpFocusTree();
1339      }
1340      return true;
1341    }());
1342  }
1343
1344  @override
1345  List<DiagnosticsNode> debugDescribeChildren() {
1346    return <DiagnosticsNode>[
1347      rootScope.toDiagnosticsNode(name: 'rootScope'),
1348    ];
1349  }
1350
1351  @override
1352  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1353    properties.add(FlagProperty('haveScheduledUpdate', value: _haveScheduledUpdate, ifTrue: 'UPDATE SCHEDULED'));
1354    properties.add(DiagnosticsProperty<FocusNode>('primaryFocus', primaryFocus, defaultValue: null));
1355    final Element element = primaryFocus?.context;
1356    if (element != null) {
1357      properties.add(DiagnosticsProperty<String>('primaryFocusCreator', element.debugGetCreatorChain(20)));
1358    }
1359  }
1360}
1361
1362/// Returns a text representation of the current focus tree, along with the
1363/// current attributes on each node.
1364///
1365/// Will return an empty string in release builds.
1366String debugDescribeFocusTree() {
1367  assert(WidgetsBinding.instance != null);
1368  String result;
1369  assert(() {
1370    result = WidgetsBinding.instance.focusManager.toStringDeep();
1371    return true;
1372  }());
1373  return result ?? '';
1374}
1375
1376/// Prints a text representation of the current focus tree, along with the
1377/// current attributes on each node.
1378///
1379/// Will do nothing in release builds.
1380void debugDumpFocusTree() {
1381  assert(() {
1382    debugPrint(debugDescribeFocusTree());
1383    return true;
1384  }());
1385}
1386