• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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