1// Copyright 2015 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. 4import 'package:flutter/foundation.dart'; 5 6import 'basic.dart'; 7import 'focus_manager.dart'; 8import 'framework.dart'; 9import 'inherited_notifier.dart'; 10 11/// A widget that manages a [FocusNode] to allow keyboard focus to be given 12/// to this widget and its descendants. 13/// 14/// When the focus is gained or lost, [onFocusChanged] is called. 15/// 16/// For keyboard events, [onKey] is called if [FocusNode.hasFocus] is true for 17/// this widget's [focusNode], unless a focused descendant's [onKey] callback 18/// returns false when called. 19/// 20/// This widget does not provide any visual indication that the focus has 21/// changed. Any desired visual changes should be made when [onFocusChanged] is 22/// called. 23/// 24/// To access the [FocusNode] of the nearest ancestor [Focus] widget and 25/// establish a relationship that will rebuild the widget when the focus 26/// changes, use the [Focus.of] and [FocusScope.of] static methods. 27/// 28/// To access the focused state of the nearest [Focus] widget, use 29/// [Focus.hasFocus] from a build method, which also establishes a relationship 30/// between the calling widget and the [Focus] widget that will rebuild the 31/// calling widget when the focus changes. 32/// 33/// Managing a [FocusNode] means managing its lifecycle, listening for changes 34/// in focus, and re-parenting it when needed to keep the focus hierarchy in 35/// sync with the widget hierarchy. See [FocusNode] for more information about 36/// the details of what node management entails if not using a [Focus] widget. 37/// 38/// To collect a sub-tree of nodes into a group, use a [FocusScope]. 39/// 40/// {@tool snippet --template=stateful_widget_scaffold} 41/// This example shows how to manage focus using the [Focus] and [FocusScope] 42/// widgets. See [FocusNode] for a similar example that doesn't use [Focus] or 43/// [FocusScope]. 44/// 45/// ```dart imports 46/// import 'package:flutter/services.dart'; 47/// ``` 48/// 49/// ```dart 50/// Color _color = Colors.white; 51/// 52/// bool _handleKeyPress(FocusNode node, RawKeyEvent event) { 53/// if (event is RawKeyDownEvent) { 54/// print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}'); 55/// if (event.logicalKey == LogicalKeyboardKey.keyR) { 56/// print('Changing color to red.'); 57/// setState(() { 58/// _color = Colors.red; 59/// }); 60/// return true; 61/// } else if (event.logicalKey == LogicalKeyboardKey.keyG) { 62/// print('Changing color to green.'); 63/// setState(() { 64/// _color = Colors.green; 65/// }); 66/// return true; 67/// } else if (event.logicalKey == LogicalKeyboardKey.keyB) { 68/// print('Changing color to blue.'); 69/// setState(() { 70/// _color = Colors.blue; 71/// }); 72/// return true; 73/// } 74/// } 75/// return false; 76/// } 77/// 78/// @override 79/// Widget build(BuildContext context) { 80/// final TextTheme textTheme = Theme.of(context).textTheme; 81/// return FocusScope( 82/// debugLabel: 'Scope', 83/// autofocus: true, 84/// child: DefaultTextStyle( 85/// style: textTheme.display1, 86/// child: Focus( 87/// onKey: _handleKeyPress, 88/// debugLabel: 'Button', 89/// child: Builder( 90/// builder: (BuildContext context) { 91/// final FocusNode focusNode = Focus.of(context); 92/// final bool hasFocus = focusNode.hasFocus; 93/// return GestureDetector( 94/// onTap: () { 95/// if (hasFocus) { 96/// focusNode.unfocus(); 97/// } else { 98/// focusNode.requestFocus(); 99/// } 100/// }, 101/// child: Center( 102/// child: Container( 103/// width: 400, 104/// height: 100, 105/// alignment: Alignment.center, 106/// color: hasFocus ? _color : Colors.white, 107/// child: Text(hasFocus ? "I'm in color! Press R,G,B!" : 'Press to focus'), 108/// ), 109/// ), 110/// ); 111/// }, 112/// ), 113/// ), 114/// ), 115/// ); 116/// } 117/// ``` 118/// {@end-tool} 119/// 120/// See also: 121/// 122/// * [FocusNode], which represents a node in the focus hierarchy and 123/// [FocusNode]'s API documentation includes a detailed explanation of its 124/// role in the overall focus system. 125/// * [FocusScope], a widget that manages a group of focusable widgets using a 126/// [FocusScopeNode]. 127/// * [FocusScopeNode], a node that collects focus nodes into a group for 128/// traversal. 129/// * [FocusManager], a singleton that manages the primary focus and 130/// distributes key events to focused nodes. 131/// * [FocusTraversalPolicy], an object used to determine how to move the 132/// focus to other nodes. 133/// * [DefaultFocusTraversal], a widget used to configure the default focus 134/// traversal policy for a widget subtree. 135class Focus extends StatefulWidget { 136 /// Creates a widget that manages a [FocusNode]. 137 /// 138 /// The [child] argument is required and must not be null. 139 /// 140 /// The [autofocus] and [skipTraversal] arguments must not be null. 141 const Focus({ 142 Key key, 143 @required this.child, 144 this.focusNode, 145 this.autofocus = false, 146 this.onFocusChange, 147 this.onKey, 148 this.debugLabel, 149 this.canRequestFocus, 150 this.skipTraversal, 151 }) : assert(child != null), 152 assert(autofocus != null), 153 super(key: key); 154 155 /// A debug label for this widget. 156 /// 157 /// Not used for anything except to be printed in the diagnostic output from 158 /// [toString] or [toStringDeep]. Also unused if a [focusNode] is provided, 159 /// since that node can have its own [FocusNode.debugLabel]. 160 /// 161 /// To get a string with the entire tree, call [debugDescribeFocusTree]. To 162 /// print it to the console call [debugDumpFocusTree]. 163 /// 164 /// Defaults to null. 165 final String debugLabel; 166 167 /// The child widget of this [Focus]. 168 /// 169 /// {@macro flutter.widgets.child} 170 final Widget child; 171 172 /// Handler for keys pressed when this object or one of its children has 173 /// focus. 174 /// 175 /// Key events are first given to the [FocusNode] that has primary focus, and 176 /// if its [onKey] method return false, then they are given to each ancestor 177 /// node up the focus hierarchy in turn. If an event reaches the root of the 178 /// hierarchy, it is discarded. 179 /// 180 /// This is not the way to get text input in the manner of a text field: it 181 /// leaves out support for input method editors, and doesn't support soft 182 /// keyboards in general. For text input, consider [TextField], 183 /// [EditableText], or [CupertinoTextField] instead, which do support these 184 /// things. 185 final FocusOnKeyCallback onKey; 186 187 /// Handler called when the focus changes. 188 /// 189 /// Called with true if this node gains focus, and false if it loses 190 /// focus. 191 final ValueChanged<bool> onFocusChange; 192 193 /// {@template flutter.widgets.Focus.autofocus} 194 /// True if this widget will be selected as the initial focus when no other 195 /// node in its scope is currently focused. 196 /// 197 /// Ideally, there is only one widget with autofocus set in each [FocusScope]. 198 /// If there is more than one widget with autofocus set, then the first one 199 /// added to the tree will get focus. 200 /// 201 /// Must not be null. Defaults to false. 202 /// {@endtemplate} 203 final bool autofocus; 204 205 /// {@template flutter.widgets.Focus.focusNode} 206 /// An optional focus node to use as the focus node for this widget. 207 /// 208 /// If one is not supplied, then one will be automatically allocated, owned, 209 /// and managed by this widget. The widget will be focusable even if a 210 /// [focusNode] is not supplied. If supplied, the given `focusNode` will be 211 /// _hosted_ by this widget, but not owned. See [FocusNode] for more 212 /// information on what being hosted and/or owned implies. 213 /// 214 /// Supplying a focus node is sometimes useful if an ancestor to this widget 215 /// wants to control when this widget has the focus. The owner will be 216 /// responsible for calling [FocusNode.dispose] on the focus node when it is 217 /// done with it, but this widget will attach/detach and reparent the node 218 /// when needed. 219 /// {@endtemplate} 220 final FocusNode focusNode; 221 222 /// Sets the [FocusNode.skipTraversal] flag on the focus node so that it won't 223 /// be visited by the [FocusTraversalPolicy]. 224 /// 225 /// This is sometimes useful if a Focus widget should receive key events as 226 /// part of the focus chain, but shouldn't be accessible via focus traversal. 227 /// 228 /// This is different from [canRequestFocus] because it only implies that the 229 /// widget can't be reached via traversal, not that it can't be focused. It may 230 /// still be focused explicitly. 231 final bool skipTraversal; 232 233 /// If true, this widget may request the primary focus. 234 /// 235 /// Defaults to true. Set to false if you want the [FocusNode] this widget 236 /// manages to do nothing when [requestFocus] is called on it. Does not affect 237 /// the children of this node, and [FocusNode.hasFocus] can still return true 238 /// if this node is the ancestor of the primary focus. 239 /// 240 /// This is different than [skipTraversal] because [skipTraversal] still 241 /// allows the widget to be focused, just not traversed to. 242 /// 243 /// Setting [canRequestFocus] to false implies that the widget will also be 244 /// skipped for traversal purposes. 245 /// 246 /// See also: 247 /// 248 /// - [DefaultFocusTraversal], a widget that sets the traversal policy for 249 /// its descendants. 250 /// - [FocusTraversalPolicy], a class that can be extended to describe a 251 /// traversal policy. 252 final bool canRequestFocus; 253 254 /// Returns the [focusNode] of the [Focus] that most tightly encloses the 255 /// given [BuildContext]. 256 /// 257 /// If no [Focus] node is found before reaching the nearest [FocusScope] 258 /// widget, or there is no [Focus] widget in scope, then this method will 259 /// throw an exception. To return null instead of throwing, pass true for 260 /// [nullOk]. 261 /// 262 /// The [context] and [nullOk] arguments must not be null. 263 static FocusNode of(BuildContext context, { bool nullOk = false }) { 264 assert(context != null); 265 assert(nullOk != null); 266 final _FocusMarker marker = context.inheritFromWidgetOfExactType(_FocusMarker); 267 final FocusNode node = marker?.notifier; 268 if (node is FocusScopeNode) { 269 if (!nullOk) { 270 throw FlutterError( 271 'Focus.of() was called with a context that does not contain a Focus between the given ' 272 'context and the nearest FocusScope widget.\n' 273 'No Focus ancestor could be found starting from the context that was passed to ' 274 'Focus.of() to the point where it found the nearest FocusScope widget. This can happen ' 275 'because you are using a widget that looks for a Focus ancestor, and do not have a ' 276 'Focus widget ancestor in the current FocusScope.\n' 277 'The context used was:\n' 278 ' $context' 279 ); 280 } 281 return null; 282 } 283 if (node == null) { 284 if (!nullOk) { 285 throw FlutterError( 286 'Focus.of() was called with a context that does not contain a Focus widget.\n' 287 'No Focus widget ancestor could be found starting from the context that was passed to ' 288 'Focus.of(). This can happen because you are using a widget that looks for a Focus ' 289 'ancestor, and do not have a Focus widget descendant in the nearest FocusScope.\n' 290 'The context used was:\n' 291 ' $context' 292 ); 293 } 294 return null; 295 } 296 return node; 297 } 298 299 /// Returns true if the nearest enclosing [Focus] widget's node is focused. 300 /// 301 /// A convenience method to allow build methods to write: 302 /// `Focus.isAt(context)` to get whether or not the nearest [Focus] above them 303 /// in the widget hierarchy currently has the input focus. 304 /// 305 /// Returns false if no [Focus] widget is found before reaching the nearest 306 /// [FocusScope], or if the root of the focus tree is reached without finding 307 /// a [Focus] widget. 308 static bool isAt(BuildContext context) => Focus.of(context, nullOk: true)?.hasFocus ?? false; 309 310 @override 311 void debugFillProperties(DiagnosticPropertiesBuilder properties) { 312 super.debugFillProperties(properties); 313 properties.add(StringProperty('debugLabel', debugLabel, defaultValue: null)); 314 properties.add(FlagProperty('autofocus', value: autofocus, ifTrue: 'AUTOFOCUS', defaultValue: false)); 315 properties.add(DiagnosticsProperty<FocusNode>('node', focusNode, defaultValue: null)); 316 } 317 318 @override 319 _FocusState createState() => _FocusState(); 320} 321 322class _FocusState extends State<Focus> { 323 FocusNode _internalNode; 324 FocusNode get focusNode => widget.focusNode ?? _internalNode; 325 bool _hasFocus; 326 bool _didAutofocus = false; 327 FocusAttachment _focusAttachment; 328 329 @override 330 void initState() { 331 super.initState(); 332 _initNode(); 333 } 334 335 void _initNode() { 336 if (widget.focusNode == null) { 337 // Only create a new node if the widget doesn't have one. 338 // This calls a function instead of just allocating in place because 339 // _createNode is overridden in _FocusScopeState. 340 _internalNode ??= _createNode(); 341 } 342 focusNode.skipTraversal = widget.skipTraversal ?? focusNode.skipTraversal; 343 focusNode.canRequestFocus = widget.canRequestFocus ?? focusNode.canRequestFocus; 344 _focusAttachment = focusNode.attach(context, onKey: widget.onKey); 345 _hasFocus = focusNode.hasFocus; 346 347 // Add listener even if the _internalNode existed before, since it should 348 // not be listening now if we're re-using a previous one because it should 349 // have already removed its listener. 350 focusNode.addListener(_handleFocusChanged); 351 } 352 353 FocusNode _createNode() { 354 return FocusNode( 355 debugLabel: widget.debugLabel, 356 canRequestFocus: widget.canRequestFocus ?? true, 357 skipTraversal: widget.skipTraversal ?? false, 358 ); 359 } 360 361 @override 362 void dispose() { 363 // Regardless of the node owner, we need to remove it from the tree and stop 364 // listening to it. 365 focusNode.removeListener(_handleFocusChanged); 366 _focusAttachment.detach(); 367 368 // Don't manage the lifetime of external nodes given to the widget, just the 369 // internal node. 370 _internalNode?.dispose(); 371 super.dispose(); 372 } 373 374 @override 375 void didChangeDependencies() { 376 super.didChangeDependencies(); 377 _focusAttachment?.reparent(); 378 if (!_didAutofocus && widget.autofocus) { 379 FocusScope.of(context).autofocus(focusNode); 380 _didAutofocus = true; 381 } 382 } 383 384 @override 385 void deactivate() { 386 super.deactivate(); 387 _didAutofocus = false; 388 } 389 390 @override 391 void didUpdateWidget(Focus oldWidget) { 392 super.didUpdateWidget(oldWidget); 393 assert(() { 394 // Only update the debug label in debug builds, and only if we own the 395 // node. 396 if (oldWidget.debugLabel != widget.debugLabel && _internalNode != null) { 397 _internalNode.debugLabel = widget.debugLabel; 398 } 399 return true; 400 }()); 401 402 if (oldWidget.focusNode == widget.focusNode) { 403 focusNode.skipTraversal = widget.skipTraversal ?? focusNode.skipTraversal; 404 focusNode.canRequestFocus = widget.canRequestFocus ?? focusNode.canRequestFocus; 405 return; 406 } 407 408 _focusAttachment.detach(); 409 focusNode.removeListener(_handleFocusChanged); 410 _initNode(); 411 } 412 413 void _handleFocusChanged() { 414 if (_hasFocus != focusNode.hasFocus) { 415 setState(() { 416 _hasFocus = focusNode.hasFocus; 417 }); 418 if (widget.onFocusChange != null) { 419 widget.onFocusChange(focusNode.hasFocus); 420 } 421 } 422 } 423 424 @override 425 Widget build(BuildContext context) { 426 _focusAttachment.reparent(); 427 return _FocusMarker( 428 node: focusNode, 429 child: widget.child, 430 ); 431 } 432} 433 434/// A [FocusScope] is similar to a [Focus], but also serves as a scope for other 435/// [Focus]s and [FocusScope]s, grouping them together. 436/// 437/// Like [Focus], [FocusScope] provides an [onFocusChange] as a way to be 438/// notified when the focus is given to or removed from this widget. 439/// 440/// The [onKey] argument allows specification of a key event handler that is 441/// invoked when this node or one of its children has focus. Keys are handed to 442/// the primary focused widget first, and then they propagate through the 443/// ancestors of that node, stopping if one of them returns true from [onKey], 444/// indicating that it has handled the event. 445/// 446/// A [FocusScope] manages a [FocusScopeNode]. Managing a [FocusScopeNode] means 447/// managing its lifecycle, listening for changes in focus, and re-parenting it 448/// when the widget hierarchy changes. See [FocusNode] and [FocusScopeNode] for 449/// more information about the details of what node management entails if not 450/// using a [FocusScope] widget. 451/// 452/// A [DefaultTraversalPolicy] widget provides the [FocusTraversalPolicy] for 453/// the [FocusScopeNode]s owned by its descendant widgets. Each [FocusScopeNode] 454/// has [FocusNode] descendants. The traversal policy defines what "previous 455/// focus", "next focus", and "move focus in this direction" means for them. 456/// 457/// [FocusScopeNode]s remember the last [FocusNode] that was focused within 458/// their descendants, and can move that focus to the next/previous node, or a 459/// node in a particular direction when the [FocusNode.nextFocus], 460/// [FocusNode.previousFocus], or [FocusNode.focusInDirection] are called on a 461/// [FocusNode] or [FocusScopeNode]. 462/// 463/// To move the focus, use methods on [FocusScopeNode]. For instance, to move 464/// the focus to the next node, call `Focus.of(context).nextFocus()`. 465/// 466/// See also: 467/// 468/// * [FocusScopeNode], which represents a scope node in the focus hierarchy. 469/// * [FocusNode], which represents a node in the focus hierarchy and has an 470/// explanation of the focus system. 471/// * [Focus], a widget that manages a [FocusNode] and allows easy access to 472/// managing focus without having to manage the node. 473/// * [FocusManager], a singleton that manages the focus and distributes key 474/// events to focused nodes. 475/// * [FocusTraversalPolicy], an object used to determine how to move the 476/// focus to other nodes. 477/// * [DefaultFocusTraversal], a widget used to configure the default focus 478/// traversal policy for a widget subtree. 479class FocusScope extends Focus { 480 /// Creates a widget that manages a [FocusScopeNode]. 481 /// 482 /// The [child] argument is required and must not be null. 483 /// 484 /// The [autofocus], and [showDecorations] arguments must not be null. 485 const FocusScope({ 486 Key key, 487 FocusScopeNode node, 488 @required Widget child, 489 bool autofocus = false, 490 ValueChanged<bool> onFocusChange, 491 FocusOnKeyCallback onKey, 492 String debugLabel, 493 }) : assert(child != null), 494 assert(autofocus != null), 495 super( 496 key: key, 497 child: child, 498 focusNode: node, 499 autofocus: autofocus, 500 onFocusChange: onFocusChange, 501 onKey: onKey, 502 debugLabel: debugLabel, 503 ); 504 505 /// Returns the [FocusScopeNode] of the [FocusScope] that most tightly 506 /// encloses the given [context]. 507 /// 508 /// If this node doesn't have a [Focus] widget ancestor, then the 509 /// [FocusManager.rootScope] is returned. 510 /// 511 /// The [context] argument must not be null. 512 static FocusScopeNode of(BuildContext context) { 513 assert(context != null); 514 final _FocusMarker marker = context.inheritFromWidgetOfExactType(_FocusMarker); 515 return marker?.notifier?.nearestScope ?? context.owner.focusManager.rootScope; 516 } 517 518 @override 519 _FocusScopeState createState() => _FocusScopeState(); 520} 521 522class _FocusScopeState extends _FocusState { 523 @override 524 FocusScopeNode _createNode() { 525 return FocusScopeNode( 526 debugLabel: widget.debugLabel, 527 ); 528 } 529 530 @override 531 Widget build(BuildContext context) { 532 _focusAttachment.reparent(); 533 return Semantics( 534 explicitChildNodes: true, 535 child: _FocusMarker( 536 node: focusNode, 537 child: widget.child, 538 ), 539 ); 540 } 541} 542 543// The InheritedWidget marker for Focus and FocusScope. 544class _FocusMarker extends InheritedNotifier<FocusNode> { 545 const _FocusMarker({ 546 Key key, 547 @required FocusNode node, 548 @required Widget child, 549 }) : assert(node != null), 550 assert(child != null), 551 super(key: key, notifier: node, child: child); 552} 553