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