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 | import 'package:flutter/foundation.dart'; |
6 | |
7 | import 'basic.dart'; |
8 | import 'focus_manager.dart'; |
9 | import 'framework.dart'; |
10 | import '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. |
112 | class 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. |
483 | class _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 | |
512 | class _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. |
750 | class 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. |
798 | class _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 | |
828 | class _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. |
852 | class _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. |
870 | class 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 | |