1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/foundation.dart';
6
7import 'basic.dart';
8import 'focus_manager.dart';
9import 'framework.dart';
10import 'inherited_notifier.dart';
11
12/// A widget that manages a [FocusNode] to allow keyboard focus to be given
13/// to this widget and its descendants.
14///
15/// {@youtube 560 315 https://www.youtube.com/watch?v=JCDfh5bs1xc}
16///
17/// When the focus is gained or lost, [onFocusChange] is called.
18///
19/// For keyboard events, [onKey] and [onKeyEvent] are called if
20/// [FocusNode.hasFocus] is true for this widget's [focusNode], unless a focused
21/// descendant's [onKey] or [onKeyEvent] callback returned
22/// [KeyEventResult.handled] when called.
23///
24/// This widget does not provide any visual indication that the focus has
25/// changed. Any desired visual changes should be made when [onFocusChange] is
26/// called.
27///
28/// To access the [FocusNode] of the nearest ancestor [Focus] widget and
29/// establish a relationship that will rebuild the widget when the focus
30/// changes, use the [Focus.of] and [FocusScope.of] static methods.
31///
32/// To access the focused state of the nearest [Focus] widget, use
33/// [FocusNode.hasFocus] from a build method, which also establishes a
34/// relationship between the calling widget and the [Focus] widget that will
35/// rebuild the calling widget when the focus changes.
36///
37/// Managing a [FocusNode] means managing its lifecycle, listening for changes
38/// in focus, and re-parenting it when needed to keep the focus hierarchy in
39/// sync with the widget hierarchy. This widget does all of those things for
40/// you. See [FocusNode] for more information about the details of what node
41/// management entails if you are not using a [Focus] widget and you need to do
42/// it yourself.
43///
44/// If the [Focus] default constructor is used, then this widget will manage any
45/// given [focusNode] by overwriting the appropriate values of the [focusNode]
46/// with the values of [FocusNode.onKey], [FocusNode.onKeyEvent],
47/// [FocusNode.skipTraversal], [FocusNode.canRequestFocus], and
48/// [FocusNode.descendantsAreFocusable] whenever the [Focus] widget is updated.
49///
50/// If the [Focus.withExternalFocusNode] is used instead, then the values
51/// returned by [onKey], [onKeyEvent], [skipTraversal], [canRequestFocus], and
52/// [descendantsAreFocusable] will be the values in the external focus node, and
53/// the external focus node's values will not be overwritten when the widget is
54/// updated.
55///
56/// To collect a sub-tree of nodes into an exclusive group that restricts focus
57/// traversal to the group, use a [FocusScope]. To collect a sub-tree of nodes
58/// into a group that has a specific order to its traversal but allows the
59/// traversal to escape the group, use a [FocusTraversalGroup].
60///
61/// To move the focus, use methods on [FocusNode] by getting the [FocusNode]
62/// through the [of] method. For instance, to move the focus to the next node in
63/// the focus traversal order, call `Focus.of(context).nextFocus()`. To unfocus
64/// a widget, call `Focus.of(context).unfocus()`.
65///
66/// {@tool dartpad}
67/// This example shows how to manage focus using the [Focus] and [FocusScope]
68/// widgets. See [FocusNode] for a similar example that doesn't use [Focus] or
69/// [FocusScope].
70///
71/// ** See code in examples/api/lib/widgets/focus_scope/focus.0.dart **
72/// {@end-tool}
73///
74/// {@tool dartpad}
75/// This example shows how to wrap another widget in a [Focus] widget to make it
76/// focusable. It wraps a [Container], and changes the container's color when it
77/// is set as the [FocusManager.primaryFocus].
78///
79/// If you also want to handle mouse hover and/or keyboard actions on a widget,
80/// consider using a [FocusableActionDetector], which combines several different
81/// widgets to provide those capabilities.
82///
83/// ** See code in examples/api/lib/widgets/focus_scope/focus.1.dart **
84/// {@end-tool}
85///
86/// {@tool dartpad}
87/// This example shows how to focus a newly-created widget immediately after it
88/// is created.
89///
90/// The focus node will not actually be given the focus until after the frame in
91/// which it has requested focus is drawn, so it is OK to call
92/// [FocusNode.requestFocus] on a node which is not yet in the focus tree.
93///
94/// ** See code in examples/api/lib/widgets/focus_scope/focus.2.dart **
95/// {@end-tool}
96///
97/// See also:
98///
99/// * [FocusNode], which represents a node in the focus hierarchy and
100/// [FocusNode]'s API documentation includes a detailed explanation of its role
101/// in the overall focus system.
102/// * [FocusScope], a widget that manages a group of focusable widgets using a
103/// [FocusScopeNode].
104/// * [FocusScopeNode], a node that collects focus nodes into a group for
105/// traversal.
106/// * [FocusManager], a singleton that manages the primary focus and
107/// distributes key events to focused nodes.
108/// * [FocusTraversalPolicy], an object used to determine how to move the focus
109/// to other nodes.
110/// * [FocusTraversalGroup], a widget that groups together and imposes a
111/// traversal policy on the [Focus] nodes below it in the widget hierarchy.
112class Focus extends StatefulWidget {
113 /// Creates a widget that manages a [FocusNode].
114 const Focus({
115 super.key,
116 required this.child,
117 this.focusNode,
118 this.parentNode,
119 this.autofocus = false,
120 this.onFocusChange,
121 FocusOnKeyEventCallback? onKeyEvent,
122 @Deprecated(
123 'Use onKeyEvent instead. '
124 'This feature was deprecated after v3.18.0-2.0.pre.',
125 )
126 FocusOnKeyCallback? onKey,
127 bool? canRequestFocus,
128 bool? skipTraversal,
129 bool? descendantsAreFocusable,
130 bool? descendantsAreTraversable,
131 this.includeSemantics = true,
132 String? debugLabel,
133 }) : _onKeyEvent = onKeyEvent,
134 _onKey = onKey,
135 _canRequestFocus = canRequestFocus,
136 _skipTraversal = skipTraversal,
137 _descendantsAreFocusable = descendantsAreFocusable,
138 _descendantsAreTraversable = descendantsAreTraversable,
139 _debugLabel = debugLabel;
140
141 /// Creates a Focus widget that uses the given [focusNode] as the source of
142 /// truth for attributes on the node, rather than the attributes of this widget.
143 const factory Focus.withExternalFocusNode({
144 Key? key,
145 required Widget child,
146 required FocusNode focusNode,
147 FocusNode? parentNode,
148 bool autofocus,
149 ValueChanged<bool>? onFocusChange,
150 bool includeSemantics,
151 }) = _FocusWithExternalFocusNode;
152
153 // Indicates whether the widget's focusNode attributes should have priority
154 // when then widget is updated.
155 bool get _usingExternalFocus => false;
156
157 /// The optional parent node to use when reparenting the [focusNode] for this
158 /// [Focus] widget.
159 ///
160 /// If [parentNode] is null, then [Focus.maybeOf] is used to find the parent
161 /// in the widget tree, which is typically what is desired, since it is easier
162 /// to reason about the focus tree if it mirrors the shape of the widget tree.
163 ///
164 /// Set this property if the focus tree needs to have a different shape than
165 /// the widget tree. This is typically in cases where a dialog is in an
166 /// [Overlay] (or another part of the widget tree), and focus should
167 /// behave as if the widgets in the overlay are descendants of the given
168 /// [parentNode] for purposes of focus.
169 ///
170 /// Defaults to null.
171 final FocusNode? parentNode;
172
173 /// The child widget of this [Focus].
174 ///
175 /// {@macro flutter.widgets.ProxyWidget.child}
176 final Widget child;
177
178 /// {@template flutter.widgets.Focus.focusNode}
179 /// An optional focus node to use as the focus node for this widget.
180 ///
181 /// If one is not supplied, then one will be automatically allocated, owned,
182 /// and managed by this widget. The widget will be focusable even if a
183 /// [focusNode] is not supplied. If supplied, the given [focusNode] will be
184 /// _hosted_ by this widget, but not owned. See [FocusNode] for more
185 /// information on what being hosted and/or owned implies.
186 ///
187 /// Supplying a focus node is sometimes useful if an ancestor to this widget
188 /// wants to control when this widget has the focus. The owner will be
189 /// responsible for calling [FocusNode.dispose] on the focus node when it is
190 /// done with it, but this widget will attach/detach and reparent the node
191 /// when needed.
192 /// {@endtemplate}
193 ///
194 /// A non-null [focusNode] must be supplied if using the
195 /// [Focus.withExternalFocusNode] constructor.
196 final FocusNode? focusNode;
197
198 /// {@template flutter.widgets.Focus.autofocus}
199 /// True if this widget will be selected as the initial focus when no other
200 /// node in its scope is currently focused.
201 ///
202 /// Ideally, there is only one widget with autofocus set in each [FocusScope].
203 /// If there is more than one widget with autofocus set, then the first one
204 /// added to the tree will get focus.
205 ///
206 /// Defaults to false.
207 /// {@endtemplate}
208 final bool autofocus;
209
210 /// Handler called when the focus changes.
211 ///
212 /// Called with true if this widget's node gains focus, and false if it loses
213 /// focus.
214 final ValueChanged<bool>? onFocusChange;
215
216 /// A handler for keys that are pressed when this object or one of its
217 /// children has focus.
218 ///
219 /// Key events are first given to the [FocusNode] that has primary focus, and
220 /// if its [onKeyEvent] method returns [KeyEventResult.ignored], then they are
221 /// given to each ancestor node up the focus hierarchy in turn. If an event
222 /// reaches the root of the hierarchy, it is discarded.
223 ///
224 /// This is not the way to get text input in the manner of a text field: it
225 /// leaves out support for input method editors, and doesn't support soft
226 /// keyboards in general. For text input, consider [TextField],
227 /// [EditableText], or [CupertinoTextField] instead, which do support these
228 /// things.
229 FocusOnKeyEventCallback? get onKeyEvent => _onKeyEvent ?? focusNode?.onKeyEvent;
230 final FocusOnKeyEventCallback? _onKeyEvent;
231
232 /// A handler for keys that are pressed when this object or one of its
233 /// children has focus.
234 ///
235 /// This property is deprecated and will be removed. Use [onKeyEvent] instead.
236 ///
237 /// Key events are first given to the [FocusNode] that has primary focus, and
238 /// if its [onKey] method return false, then they are given to each ancestor
239 /// node up the focus hierarchy in turn. If an event reaches the root of the
240 /// hierarchy, it is discarded.
241 ///
242 /// This is not the way to get text input in the manner of a text field: it
243 /// leaves out support for input method editors, and doesn't support soft
244 /// keyboards in general. For text input, consider [TextField],
245 /// [EditableText], or [CupertinoTextField] instead, which do support these
246 /// things.
247 @Deprecated(
248 'Use onKeyEvent instead. '
249 'This feature was deprecated after v3.18.0-2.0.pre.',
250 )
251 FocusOnKeyCallback? get onKey => _onKey ?? focusNode?.onKey;
252 final FocusOnKeyCallback? _onKey;
253
254 /// {@template flutter.widgets.Focus.canRequestFocus}
255 /// If true, this widget may request the primary focus.
256 ///
257 /// Defaults to true. Set to false if you want the [FocusNode] this widget
258 /// manages to do nothing when [FocusNode.requestFocus] is called on it. Does
259 /// not affect the children of this node, and [FocusNode.hasFocus] can still
260 /// return true if this node is the ancestor of the primary focus.
261 ///
262 /// This is different than [Focus.skipTraversal] because [Focus.skipTraversal]
263 /// still allows the widget to be focused, just not traversed to.
264 ///
265 /// Setting [FocusNode.canRequestFocus] to false implies that the widget will
266 /// also be skipped for traversal purposes.
267 ///
268 /// See also:
269 ///
270 /// * [FocusTraversalGroup], a widget that sets the traversal policy for its
271 /// descendants.
272 /// * [FocusTraversalPolicy], a class that can be extended to describe a
273 /// traversal policy.
274 /// {@endtemplate}
275 bool get canRequestFocus => _canRequestFocus ?? focusNode?.canRequestFocus ?? true;
276 final bool? _canRequestFocus;
277
278 /// Sets the [FocusNode.skipTraversal] flag on the focus node so that it won't
279 /// be visited by the [FocusTraversalPolicy].
280 ///
281 /// This is sometimes useful if a [Focus] widget should receive key events as
282 /// part of the focus chain, but shouldn't be accessible via focus traversal.
283 ///
284 /// This is different from [FocusNode.canRequestFocus] because it only implies
285 /// that the widget can't be reached via traversal, not that it can't be
286 /// focused. It may still be focused explicitly.
287 bool get skipTraversal => _skipTraversal ?? focusNode?.skipTraversal ?? false;
288 final bool? _skipTraversal;
289
290 /// {@template flutter.widgets.Focus.descendantsAreFocusable}
291 /// If false, will make this widget's descendants unfocusable.
292 ///
293 /// Defaults to true. Does not affect focusability of this node (just its
294 /// descendants): for that, use [FocusNode.canRequestFocus].
295 ///
296 /// If any descendants are focused when this is set to false, they will be
297 /// unfocused. When [descendantsAreFocusable] is set to true again, they will
298 /// not be refocused, although they will be able to accept focus again.
299 ///
300 /// Does not affect the value of [FocusNode.canRequestFocus] on the
301 /// descendants.
302 ///
303 /// If a descendant node loses focus when this value is changed, the focus
304 /// will move to the scope enclosing this node.
305 ///
306 /// See also:
307 ///
308 /// * [ExcludeFocus], a widget that uses this property to conditionally
309 /// exclude focus for a subtree.
310 /// * [descendantsAreTraversable], which makes this widget's descendants
311 /// untraversable.
312 /// * [ExcludeFocusTraversal], a widget that conditionally excludes focus
313 /// traversal for a subtree.
314 /// * [FocusTraversalGroup], a widget used to group together and configure the
315 /// focus traversal policy for a widget subtree that has a
316 /// `descendantsAreFocusable` parameter to conditionally block focus for a
317 /// subtree.
318 /// {@endtemplate}
319 bool get descendantsAreFocusable => _descendantsAreFocusable ?? focusNode?.descendantsAreFocusable ?? true;
320 final bool? _descendantsAreFocusable;
321
322 /// {@template flutter.widgets.Focus.descendantsAreTraversable}
323 /// If false, will make this widget's descendants untraversable.
324 ///
325 /// Defaults to true. Does not affect traversability of this node (just its
326 /// descendants): for that, use [FocusNode.skipTraversal].
327 ///
328 /// Does not affect the value of [FocusNode.skipTraversal] on the
329 /// descendants. Does not affect focusability of the descendants.
330 ///
331 /// See also:
332 ///
333 /// * [ExcludeFocusTraversal], a widget that uses this property to
334 /// conditionally exclude focus traversal for a subtree.
335 /// * [descendantsAreFocusable], which makes this widget's descendants
336 /// unfocusable.
337 /// * [ExcludeFocus], a widget that conditionally excludes focus for a subtree.
338 /// * [FocusTraversalGroup], a widget used to group together and configure the
339 /// focus traversal policy for a widget subtree that has a
340 /// `descendantsAreFocusable` parameter to conditionally block focus for a
341 /// subtree.
342 /// {@endtemplate}
343 bool get descendantsAreTraversable => _descendantsAreTraversable ?? focusNode?.descendantsAreTraversable ?? true;
344 final bool? _descendantsAreTraversable;
345
346 /// {@template flutter.widgets.Focus.includeSemantics}
347 /// Include semantics information in this widget.
348 ///
349 /// If true, this widget will include a [Semantics] node that indicates the
350 /// [SemanticsProperties.focusable] and [SemanticsProperties.focused]
351 /// properties.
352 ///
353 /// It is not typical to set this to false, as that can affect the semantics
354 /// information available to accessibility systems.
355 ///
356 /// Defaults to true.
357 /// {@endtemplate}
358 final bool includeSemantics;
359
360 /// A debug label for this widget.
361 ///
362 /// Not used for anything except to be printed in the diagnostic output from
363 /// [toString] or [toStringDeep].
364 ///
365 /// To get a string with the entire tree, call [debugDescribeFocusTree]. To
366 /// print it to the console call [debugDumpFocusTree].
367 ///
368 /// Defaults to null.
369 String? get debugLabel => _debugLabel ?? focusNode?.debugLabel;
370 final String? _debugLabel;
371
372 /// Returns the [focusNode] of the [Focus] that most tightly encloses the
373 /// given [BuildContext].
374 ///
375 /// If no [Focus] node is found before reaching the nearest [FocusScope]
376 /// widget, or there is no [Focus] widget in the context, then this method
377 /// will throw an exception.
378 ///
379 /// {@macro flutter.widgets.focus_scope.Focus.maybeOf}
380 ///
381 /// See also:
382 ///
383 /// * [maybeOf], which is similar to this function, but will return null
384 /// instead of throwing if it doesn't find a [Focus] node.
385 static FocusNode of(BuildContext context, { bool scopeOk = false, bool createDependency = true }) {
386 final FocusNode? node = Focus.maybeOf(context, scopeOk: scopeOk, createDependency: createDependency);
387 assert(() {
388 if (node == null) {
389 throw FlutterError(
390 'Focus.of() was called with a context that does not contain a Focus widget.\n'
391 'No Focus widget ancestor could be found starting from the context that was passed to '
392 'Focus.of(). This can happen because you are using a widget that looks for a Focus '
393 'ancestor, and do not have a Focus widget descendant in the nearest FocusScope.\n'
394 'The context used was:\n'
395 ' $context',
396 );
397 }
398 return true;
399 }());
400 assert(() {
401 if (!scopeOk && node is FocusScopeNode) {
402 throw FlutterError(
403 'Focus.of() was called with a context that does not contain a Focus between the given '
404 'context and the nearest FocusScope widget.\n'
405 'No Focus ancestor could be found starting from the context that was passed to '
406 'Focus.of() to the point where it found the nearest FocusScope widget. This can happen '
407 'because you are using a widget that looks for a Focus ancestor, and do not have a '
408 'Focus widget ancestor in the current FocusScope.\n'
409 'The context used was:\n'
410 ' $context',
411 );
412 }
413 return true;
414 }());
415 return node!;
416 }
417
418 /// Returns the [focusNode] of the [Focus] that most tightly encloses the
419 /// given [BuildContext].
420 ///
421 /// If no [Focus] node is found before reaching the nearest [FocusScope]
422 /// widget, or there is no [Focus] widget in scope, then this method will
423 /// return null.
424 ///
425 /// {@template flutter.widgets.focus_scope.Focus.maybeOf}
426 /// If `createDependency` is true (which is the default), calling this
427 /// function creates a dependency that will rebuild the given context when the
428 /// focus node gains or loses focus.
429 /// {@endtemplate}
430 ///
431 /// See also:
432 ///
433 /// * [of], which is similar to this function, but will throw an exception if
434 /// it doesn't find a [Focus] node, instead of returning null.
435 static FocusNode? maybeOf(BuildContext context, { bool scopeOk = false, bool createDependency = true }) {
436 final _FocusInheritedScope? scope;
437 if (createDependency) {
438 scope = context.dependOnInheritedWidgetOfExactType<_FocusInheritedScope>();
439 } else {
440 scope = context.getInheritedWidgetOfExactType<_FocusInheritedScope>();
441 }
442 final FocusNode? node = scope?.notifier;
443 if (node == null) {
444 return null;
445 }
446 if (!scopeOk && node is FocusScopeNode) {
447 return null;
448 }
449 return node;
450 }
451
452 /// Returns true if the nearest enclosing [Focus] widget's node is focused.
453 ///
454 /// A convenience method to allow build methods to write:
455 /// `Focus.isAt(context)` to get whether or not the nearest [Focus] above them
456 /// in the widget hierarchy currently has the input focus.
457 ///
458 /// Returns false if no [Focus] widget is found before reaching the nearest
459 /// [FocusScope], or if the root of the focus tree is reached without finding
460 /// a [Focus] widget.
461 ///
462 /// Calling this function creates a dependency that will rebuild the given
463 /// context when the focus changes.
464 static bool isAt(BuildContext context) => Focus.maybeOf(context)?.hasFocus ?? false;
465
466 @override
467 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
468 super.debugFillProperties(properties);
469 properties.add(StringProperty('debugLabel', debugLabel, defaultValue: null));
470 properties.add(FlagProperty('autofocus', value: autofocus, ifTrue: 'AUTOFOCUS', defaultValue: false));
471 properties.add(FlagProperty('canRequestFocus', value: canRequestFocus, ifFalse: 'NOT FOCUSABLE', defaultValue: false));
472 properties.add(FlagProperty('descendantsAreFocusable', value: descendantsAreFocusable, ifFalse: 'DESCENDANTS UNFOCUSABLE', defaultValue: true));
473 properties.add(FlagProperty('descendantsAreTraversable', value: descendantsAreTraversable, ifFalse: 'DESCENDANTS UNTRAVERSABLE', defaultValue: true));
474 properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
475 }
476
477 @override
478 State<Focus> createState() => _FocusState();
479}
480
481// Implements the behavior differences when the Focus.withExternalFocusNode
482// constructor is used.
483class _FocusWithExternalFocusNode extends Focus {
484 const _FocusWithExternalFocusNode({
485 super.key,
486 required super.child,
487 required FocusNode super.focusNode,
488 super.parentNode,
489 super.autofocus,
490 super.onFocusChange,
491 super.includeSemantics,
492 });
493
494 @override
495 bool get _usingExternalFocus => true;
496 @override
497 FocusOnKeyEventCallback? get onKeyEvent => focusNode!.onKeyEvent;
498 @override
499 FocusOnKeyCallback? get onKey => focusNode!.onKey;
500 @override
501 bool get canRequestFocus => focusNode!.canRequestFocus;
502 @override
503 bool get skipTraversal => focusNode!.skipTraversal;
504 @override
505 bool get descendantsAreFocusable => focusNode!.descendantsAreFocusable;
506 @override
507 bool? get _descendantsAreTraversable => focusNode!.descendantsAreTraversable;
508 @override
509 String? get debugLabel => focusNode!.debugLabel;
510}
511
512class _FocusState extends State<Focus> {
513 FocusNode? _internalNode;
514 FocusNode get focusNode => widget.focusNode ?? _internalNode!;
515 late bool _hadPrimaryFocus;
516 late bool _couldRequestFocus;
517 late bool _descendantsWereFocusable;
518 late bool _descendantsWereTraversable;
519 bool _didAutofocus = false;
520 FocusAttachment? _focusAttachment;
521
522 @override
523 void initState() {
524 super.initState();
525 _initNode();
526 }
527
528 void _initNode() {
529 if (widget.focusNode == null) {
530 // Only create a new node if the widget doesn't have one.
531 // This calls a function instead of just allocating in place because
532 // _createNode is overridden in _FocusScopeState.
533 _internalNode ??= _createNode();
534 }
535 focusNode.descendantsAreFocusable = widget.descendantsAreFocusable;
536 focusNode.descendantsAreTraversable = widget.descendantsAreTraversable;
537 focusNode.skipTraversal = widget.skipTraversal;
538 if (widget._canRequestFocus != null) {
539 focusNode.canRequestFocus = widget._canRequestFocus!;
540 }
541 _couldRequestFocus = focusNode.canRequestFocus;
542 _descendantsWereFocusable = focusNode.descendantsAreFocusable;
543 _descendantsWereTraversable = focusNode.descendantsAreTraversable;
544 _hadPrimaryFocus = focusNode.hasPrimaryFocus;
545 _focusAttachment = focusNode.attach(context, onKeyEvent: widget.onKeyEvent, onKey: widget.onKey);
546
547 // Add listener even if the _internalNode existed before, since it should
548 // not be listening now if we're re-using a previous one because it should
549 // have already removed its listener.
550 focusNode.addListener(_handleFocusChanged);
551 }
552
553 FocusNode _createNode() {
554 return FocusNode(
555 debugLabel: widget.debugLabel,
556 canRequestFocus: widget.canRequestFocus,
557 descendantsAreFocusable: widget.descendantsAreFocusable,
558 descendantsAreTraversable: widget.descendantsAreTraversable,
559 skipTraversal: widget.skipTraversal,
560 );
561 }
562
563 @override
564 void dispose() {
565 // Regardless of the node owner, we need to remove it from the tree and stop
566 // listening to it.
567 focusNode.removeListener(_handleFocusChanged);
568 _focusAttachment!.detach();
569
570 // Don't manage the lifetime of external nodes given to the widget, just the
571 // internal node.
572 _internalNode?.dispose();
573 super.dispose();
574 }
575
576 @override
577 void didChangeDependencies() {
578 super.didChangeDependencies();
579 _focusAttachment?.reparent();
580 _handleAutofocus();
581 }
582
583 void _handleAutofocus() {
584 if (!_didAutofocus && widget.autofocus) {
585 FocusScope.of(context).autofocus(focusNode);
586 _didAutofocus = true;
587 }
588 }
589
590 @override
591 void deactivate() {
592 super.deactivate();
593 // The focus node's location in the tree is no longer valid here. But
594 // we can't unfocus or remove the node from the tree because if the widget
595 // is moved to a different part of the tree (via global key) it should
596 // retain its focus state. That's why we temporarily park it on the root
597 // focus node (via reparent) until it either gets moved to a different part
598 // of the tree (via didChangeDependencies) or until it is disposed.
599 _focusAttachment?.reparent();
600 _didAutofocus = false;
601 }
602
603 @override
604 void didUpdateWidget(Focus oldWidget) {
605 super.didUpdateWidget(oldWidget);
606 assert(() {
607 // Only update the debug label in debug builds.
608 if (oldWidget.focusNode == widget.focusNode &&
609 !widget._usingExternalFocus &&
610 oldWidget.debugLabel != widget.debugLabel) {
611 focusNode.debugLabel = widget.debugLabel;
612 }
613 return true;
614 }());
615
616 if (oldWidget.focusNode == widget.focusNode) {
617 if (!widget._usingExternalFocus) {
618 if (widget.onKey != focusNode.onKey) {
619 focusNode.onKey = widget.onKey;
620 }
621 if (widget.onKeyEvent != focusNode.onKeyEvent) {
622 focusNode.onKeyEvent = widget.onKeyEvent;
623 }
624 focusNode.skipTraversal = widget.skipTraversal;
625 if (widget._canRequestFocus != null) {
626 focusNode.canRequestFocus = widget._canRequestFocus!;
627 }
628 focusNode.descendantsAreFocusable = widget.descendantsAreFocusable;
629 focusNode.descendantsAreTraversable = widget.descendantsAreTraversable;
630 }
631 } else {
632 _focusAttachment!.detach();
633 oldWidget.focusNode?.removeListener(_handleFocusChanged);
634 _initNode();
635 }
636
637 if (oldWidget.autofocus != widget.autofocus) {
638 _handleAutofocus();
639 }
640 }
641
642 void _handleFocusChanged() {
643 final bool hasPrimaryFocus = focusNode.hasPrimaryFocus;
644 final bool canRequestFocus = focusNode.canRequestFocus;
645 final bool descendantsAreFocusable = focusNode.descendantsAreFocusable;
646 final bool descendantsAreTraversable = focusNode.descendantsAreTraversable;
647 widget.onFocusChange?.call(focusNode.hasFocus);
648 // Check the cached states that matter here, and call setState if they have
649 // changed.
650 if (_hadPrimaryFocus != hasPrimaryFocus) {
651 setState(() {
652 _hadPrimaryFocus = hasPrimaryFocus;
653 });
654 }
655 if (_couldRequestFocus != canRequestFocus) {
656 setState(() {
657 _couldRequestFocus = canRequestFocus;
658 });
659 }
660 if (_descendantsWereFocusable != descendantsAreFocusable) {
661 setState(() {
662 _descendantsWereFocusable = descendantsAreFocusable;
663 });
664 }
665 if (_descendantsWereTraversable != descendantsAreTraversable) {
666 setState(() {
667 _descendantsWereTraversable = descendantsAreTraversable;
668 });
669 }
670 }
671
672 @override
673 Widget build(BuildContext context) {
674 _focusAttachment!.reparent(parent: widget.parentNode);
675 Widget child = widget.child;
676 if (widget.includeSemantics) {
677 child = Semantics(
678 focusable: _couldRequestFocus,
679 focused: _hadPrimaryFocus,
680 child: widget.child,
681 );
682 }
683 return _FocusInheritedScope(
684 node: focusNode,
685 child: child,
686 );
687 }
688}
689
690/// A [FocusScope] is similar to a [Focus], but also serves as a scope for its
691/// descendants, restricting focus traversal to the scoped controls.
692///
693/// For example a new [FocusScope] is created automatically when a route is
694/// pushed, keeping the focus traversal from moving to a control in a previous
695/// route.
696///
697/// If you just want to group widgets together in a group so that they are
698/// traversed in a particular order, but the focus can still leave the group,
699/// use a [FocusTraversalGroup].
700///
701/// Like [Focus], [FocusScope] provides an [onFocusChange] as a way to be
702/// notified when the focus is given to or removed from this widget.
703///
704/// The [onKey] argument allows specification of a key event handler that is
705/// invoked when this node or one of its children has focus. Keys are handed to
706/// the primary focused widget first, and then they propagate through the
707/// ancestors of that node, stopping if one of them returns
708/// [KeyEventResult.handled] from [onKey], indicating that it has handled the
709/// event.
710///
711/// Managing a [FocusScopeNode] means managing its lifecycle, listening for
712/// changes in focus, and re-parenting it when needed to keep the focus
713/// hierarchy in sync with the widget hierarchy. This widget does all of those
714/// things for you. See [FocusScopeNode] for more information about the details
715/// of what node management entails if you are not using a [FocusScope] widget
716/// and you need to do it yourself.
717///
718/// [FocusScopeNode]s remember the last [FocusNode] that was focused within
719/// their descendants, and can move that focus to the next/previous node, or a
720/// node in a particular direction when the [FocusNode.nextFocus],
721/// [FocusNode.previousFocus], or [FocusNode.focusInDirection] are called on a
722/// [FocusNode] or [FocusScopeNode].
723///
724/// To move the focus, use methods on [FocusNode] by getting the [FocusNode]
725/// through the [of] method. For instance, to move the focus to the next node in
726/// the focus traversal order, call `Focus.of(context).nextFocus()`. To unfocus
727/// a widget, call `Focus.of(context).unfocus()`.
728///
729/// {@tool dartpad}
730/// This example demonstrates using a [FocusScope] to restrict focus to a particular
731/// portion of the app. In this case, restricting focus to the visible part of a
732/// Stack.
733///
734/// ** See code in examples/api/lib/widgets/focus_scope/focus_scope.0.dart **
735/// {@end-tool}
736///
737/// See also:
738///
739/// * [FocusScopeNode], which represents a scope node in the focus hierarchy.
740/// * [FocusNode], which represents a node in the focus hierarchy and has an
741/// explanation of the focus system.
742/// * [Focus], a widget that manages a [FocusNode] and allows easy access to
743/// managing focus without having to manage the node.
744/// * [FocusManager], a singleton that manages the focus and distributes key
745/// events to focused nodes.
746/// * [FocusTraversalPolicy], an object used to determine how to move the focus
747/// to other nodes.
748/// * [FocusTraversalGroup], a widget used to configure the focus traversal
749/// policy for a widget subtree.
750class FocusScope extends Focus {
751 /// Creates a widget that manages a [FocusScopeNode].
752 const FocusScope({
753 super.key,
754 FocusScopeNode? node,
755 super.parentNode,
756 required super.child,
757 super.autofocus,
758 super.onFocusChange,
759 super.canRequestFocus,
760 super.skipTraversal,
761 super.onKeyEvent,
762 super.onKey,
763 super.debugLabel,
764 }) : super(
765 focusNode: node,
766 );
767
768 /// Creates a FocusScope widget that uses the given [focusScopeNode] as the
769 /// source of truth for attributes on the node, rather than the attributes of
770 /// this widget.
771 const factory FocusScope.withExternalFocusNode({
772 Key? key,
773 required Widget child,
774 required FocusScopeNode focusScopeNode,
775 FocusNode? parentNode,
776 bool autofocus,
777 ValueChanged<bool>? onFocusChange,
778 }) = _FocusScopeWithExternalFocusNode;
779
780 /// Returns the [FocusNode.nearestScope] of the [Focus] or [FocusScope] that
781 /// most tightly encloses the given [context].
782 ///
783 /// If this node doesn't have a [Focus] or [FocusScope] widget ancestor, then
784 /// the [FocusManager.rootScope] is returned.
785 ///
786 /// {@macro flutter.widgets.focus_scope.Focus.maybeOf}
787 static FocusScopeNode of(BuildContext context, { bool createDependency = true }) {
788 return Focus.maybeOf(context, scopeOk: true, createDependency: createDependency)?.nearestScope
789 ?? context.owner!.focusManager.rootScope;
790 }
791
792 @override
793 State<Focus> createState() => _FocusScopeState();
794}
795
796// Implements the behavior differences when the FocusScope.withExternalFocusNode
797// constructor is used.
798class _FocusScopeWithExternalFocusNode extends FocusScope {
799 const _FocusScopeWithExternalFocusNode({
800 super.key,
801 required super.child,
802 required FocusScopeNode focusScopeNode,
803 super.parentNode,
804 super.autofocus,
805 super.onFocusChange,
806 }) : super(
807 node: focusScopeNode,
808 );
809
810 @override
811 bool get _usingExternalFocus => true;
812 @override
813 FocusOnKeyEventCallback? get onKeyEvent => focusNode!.onKeyEvent;
814 @override
815 FocusOnKeyCallback? get onKey => focusNode!.onKey;
816 @override
817 bool get canRequestFocus => focusNode!.canRequestFocus;
818 @override
819 bool get skipTraversal => focusNode!.skipTraversal;
820 @override
821 bool get descendantsAreFocusable => focusNode!.descendantsAreFocusable;
822 @override
823 bool get descendantsAreTraversable => focusNode!.descendantsAreTraversable;
824 @override
825 String? get debugLabel => focusNode!.debugLabel;
826}
827
828class _FocusScopeState extends _FocusState {
829 @override
830 FocusScopeNode _createNode() {
831 return FocusScopeNode(
832 debugLabel: widget.debugLabel,
833 canRequestFocus: widget.canRequestFocus,
834 skipTraversal: widget.skipTraversal,
835 );
836 }
837
838 @override
839 Widget build(BuildContext context) {
840 _focusAttachment!.reparent(parent: widget.parentNode);
841 return Semantics(
842 explicitChildNodes: true,
843 child: _FocusInheritedScope(
844 node: focusNode,
845 child: widget.child,
846 ),
847 );
848 }
849}
850
851// The InheritedWidget for Focus and FocusScope.
852class _FocusInheritedScope extends InheritedNotifier<FocusNode> {
853 const _FocusInheritedScope({
854 required FocusNode node,
855 required super.child,
856 }) : super(notifier: node);
857}
858
859/// A widget that controls whether or not the descendants of this widget are
860/// focusable.
861///
862/// Does not affect the value of [Focus.canRequestFocus] on the descendants.
863///
864/// See also:
865///
866/// * [Focus], a widget for adding and managing a [FocusNode] in the widget tree.
867/// * [FocusTraversalGroup], a widget that groups widgets for focus traversal,
868/// and can also be used in the same way as this widget by setting its
869/// `descendantsAreFocusable` attribute.
870class ExcludeFocus extends StatelessWidget {
871 /// Const constructor for [ExcludeFocus] widget.
872 const ExcludeFocus({
873 super.key,
874 this.excluding = true,
875 required this.child,
876 });
877
878 /// If true, will make this widget's descendants unfocusable.
879 ///
880 /// Defaults to true.
881 ///
882 /// If any descendants are focused when this is set to true, they will be
883 /// unfocused. When [excluding] is set to false again, they will not be
884 /// refocused, although they will be able to accept focus again.
885 ///
886 /// Does not affect the value of [FocusNode.canRequestFocus] on the
887 /// descendants.
888 ///
889 /// See also:
890 ///
891 /// * [Focus.descendantsAreFocusable], the attribute of a [Focus] widget that
892 /// controls this same property for focus widgets.
893 /// * [FocusTraversalGroup], a widget used to group together and configure the
894 /// focus traversal policy for a widget subtree that has a
895 /// `descendantsAreFocusable` parameter to conditionally block focus for a
896 /// subtree.
897 final bool excluding;
898
899 /// The child widget of this [ExcludeFocus].
900 ///
901 /// {@macro flutter.widgets.ProxyWidget.child}
902 final Widget child;
903
904 @override
905 Widget build(BuildContext context) {
906 return Focus(
907 canRequestFocus: false,
908 skipTraversal: true,
909 includeSemantics: false,
910 descendantsAreFocusable: !excluding,
911 child: child,
912 );
913 }
914}
915