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