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 'dart:ui'; |
6 | /// |
7 | /// @docImport 'package:flutter/animation.dart'; |
8 | /// @docImport 'package:flutter/material.dart'; |
9 | /// @docImport 'package:flutter/widgets.dart'; |
10 | /// @docImport 'package:flutter_test/flutter_test.dart'; |
11 | library; |
12 | |
13 | import 'dart:async'; |
14 | import 'dart:collection'; |
15 | |
16 | import 'package:flutter/foundation.dart'; |
17 | import 'package:flutter/rendering.dart'; |
18 | |
19 | import 'binding.dart'; |
20 | import 'debug.dart'; |
21 | import 'focus_manager.dart'; |
22 | import 'inherited_model.dart'; |
23 | import 'notification_listener.dart'; |
24 | import 'widget_inspector.dart'; |
25 | |
26 | export 'package:flutter/foundation.dart' show |
27 | factory, |
28 | immutable, |
29 | mustCallSuper, |
30 | optionalTypeArgs, |
31 | protected, |
32 | required, |
33 | visibleForTesting; |
34 | export 'package:flutter/foundation.dart' show ErrorDescription, ErrorHint, ErrorSummary, FlutterError, debugPrint, debugPrintStack; |
35 | export 'package:flutter/foundation.dart' show ValueChanged, ValueGetter, ValueSetter, VoidCallback; |
36 | export 'package:flutter/foundation.dart' show DiagnosticLevel, DiagnosticsNode; |
37 | export 'package:flutter/foundation.dart' show Key, LocalKey, ValueKey; |
38 | export 'package:flutter/rendering.dart' show RenderBox, RenderObject, debugDumpLayerTree, debugDumpRenderTree; |
39 | |
40 | // Examples can assume: |
41 | // late BuildContext context; |
42 | // void setState(VoidCallback fn) { } |
43 | // abstract class RenderFrogJar extends RenderObject { } |
44 | // abstract class FrogJar extends RenderObjectWidget { const FrogJar({super.key}); } |
45 | // abstract class FrogJarParentData extends ParentData { late Size size; } |
46 | // abstract class SomeWidget extends StatefulWidget { const SomeWidget({super.key}); } |
47 | // typedef ChildWidget = Placeholder; |
48 | // class _SomeWidgetState extends State { @override Widget build(BuildContext context) => widget; } |
49 | // abstract class RenderFoo extends RenderObject { } |
50 | // abstract class Foo extends RenderObjectWidget { const Foo({super.key}); } |
51 | // abstract class StatefulWidgetX { const StatefulWidgetX({this.key}); final Key? key; Widget build(BuildContext context, State state); } |
52 | // class SpecialWidget extends StatelessWidget { const SpecialWidget({ super.key, this.handler }); final VoidCallback? handler; @override Widget build(BuildContext context) => this; } |
53 | // late Object? _myState, newValue; |
54 | // int _counter = 0; |
55 | // Future getApplicationDocumentsDirectory() async => Directory(''); |
56 | // late AnimationController animation; |
57 | |
58 | class _DebugOnly { |
59 | const _DebugOnly(); |
60 | } |
61 | |
62 | /// An annotation used by test_analysis package to verify patterns are followed |
63 | /// that allow for tree-shaking of both fields and their initializers. This |
64 | /// annotation has no impact on code by itself, but indicates the following pattern |
65 | /// should be followed for a given field: |
66 | /// |
67 | /// ```dart |
68 | /// class Bar { |
69 | /// final Object? bar = kDebugMode ? Object() : null; |
70 | /// } |
71 | /// ``` |
72 | const _DebugOnly _debugOnly = _DebugOnly(); |
73 | |
74 | // KEYS |
75 | |
76 | /// A key that takes its identity from the object used as its value. |
77 | /// |
78 | /// Used to tie the identity of a widget to the identity of an object used to |
79 | /// generate that widget. |
80 | /// |
81 | /// See also: |
82 | /// |
83 | /// * [Key], the base class for all keys. |
84 | /// * The discussion at [Widget.key] for more information about how widgets use |
85 | /// keys. |
86 | class ObjectKey extends LocalKey { |
87 | /// Creates a key that uses [identical] on [value] for its [operator==]. |
88 | const ObjectKey(this.value); |
89 | |
90 | /// The object whose identity is used by this key's [operator==]. |
91 | final Object? value; |
92 | |
93 | @override |
94 | bool operator ==(Object other) { |
95 | if (other.runtimeType != runtimeType) { |
96 | return false; |
97 | } |
98 | return other is ObjectKey |
99 | && identical(other.value, value); |
100 | } |
101 | |
102 | @override |
103 | int get hashCode => Object.hash(runtimeType, identityHashCode(value)); |
104 | |
105 | @override |
106 | String toString() { |
107 | if (runtimeType == ObjectKey) { |
108 | return '[ ${describeIdentity(value)}]' ; |
109 | } |
110 | return '[ ${objectRuntimeType(this, 'ObjectKey' )} ${describeIdentity(value)}]' ; |
111 | } |
112 | } |
113 | |
114 | /// A key that is unique across the entire app. |
115 | /// |
116 | /// Global keys uniquely identify elements. Global keys provide access to other |
117 | /// objects that are associated with those elements, such as [BuildContext]. |
118 | /// For [StatefulWidget]s, global keys also provide access to [State]. |
119 | /// |
120 | /// Widgets that have global keys reparent their subtrees when they are moved |
121 | /// from one location in the tree to another location in the tree. In order to |
122 | /// reparent its subtree, a widget must arrive at its new location in the tree |
123 | /// in the same animation frame in which it was removed from its old location in |
124 | /// the tree. |
125 | /// |
126 | /// Reparenting an [Element] using a global key is relatively expensive, as |
127 | /// this operation will trigger a call to [State.deactivate] on the associated |
128 | /// [State] and all of its descendants; then force all widgets that depends |
129 | /// on an [InheritedWidget] to rebuild. |
130 | /// |
131 | /// If you don't need any of the features listed above, consider using a [Key], |
132 | /// [ValueKey], [ObjectKey], or [UniqueKey] instead. |
133 | /// |
134 | /// You cannot simultaneously include two widgets in the tree with the same |
135 | /// global key. Attempting to do so will assert at runtime. |
136 | /// |
137 | /// ## Pitfalls |
138 | /// |
139 | /// GlobalKeys should not be re-created on every build. They should usually be |
140 | /// long-lived objects owned by a [State] object, for example. |
141 | /// |
142 | /// Creating a new GlobalKey on every build will throw away the state of the |
143 | /// subtree associated with the old key and create a new fresh subtree for the |
144 | /// new key. Besides harming performance, this can also cause unexpected |
145 | /// behavior in widgets in the subtree. For example, a [GestureDetector] in the |
146 | /// subtree will be unable to track ongoing gestures since it will be recreated |
147 | /// on each build. |
148 | /// |
149 | /// Instead, a good practice is to let a State object own the GlobalKey, and |
150 | /// instantiate it outside the build method, such as in [State.initState]. |
151 | /// |
152 | /// See also: |
153 | /// |
154 | /// * The discussion at [Widget.key] for more information about how widgets use |
155 | /// keys. |
156 | @optionalTypeArgs |
157 | abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { |
158 | /// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a label used for |
159 | /// debugging. |
160 | /// |
161 | /// The label is purely for debugging and not used for comparing the identity |
162 | /// of the key. |
163 | factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel); |
164 | |
165 | /// Creates a global key without a label. |
166 | /// |
167 | /// Used by subclasses because the factory constructor shadows the implicit |
168 | /// constructor. |
169 | const GlobalKey.constructor() : super.empty(); |
170 | |
171 | Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this]; |
172 | |
173 | /// The build context in which the widget with this key builds. |
174 | /// |
175 | /// The current context is null if there is no widget in the tree that matches |
176 | /// this global key. |
177 | BuildContext? get currentContext => _currentElement; |
178 | |
179 | /// The widget in the tree that currently has this global key. |
180 | /// |
181 | /// The current widget is null if there is no widget in the tree that matches |
182 | /// this global key. |
183 | Widget? get currentWidget => _currentElement?.widget; |
184 | |
185 | /// The [State] for the widget in the tree that currently has this global key. |
186 | /// |
187 | /// The current state is null if (1) there is no widget in the tree that |
188 | /// matches this global key, (2) that widget is not a [StatefulWidget], or the |
189 | /// associated [State] object is not a subtype of `T`. |
190 | T? get currentState => switch (_currentElement) { |
191 | StatefulElement(:final T state) => state, |
192 | _ => null, |
193 | }; |
194 | } |
195 | |
196 | /// A global key with a debugging label. |
197 | /// |
198 | /// The debug label is useful for documentation and for debugging. The label |
199 | /// does not affect the key's identity. |
200 | @optionalTypeArgs |
201 | class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
202 | /// Creates a global key with a debugging label. |
203 | /// |
204 | /// The label does not affect the key's identity. |
205 | // ignore: prefer_const_constructors_in_immutables , never use const for this class |
206 | LabeledGlobalKey(this._debugLabel) : super.constructor(); |
207 | |
208 | final String? _debugLabel; |
209 | |
210 | @override |
211 | String toString() { |
212 | final String label = _debugLabel != null ? ' $_debugLabel' : '' ; |
213 | if (runtimeType == LabeledGlobalKey) { |
214 | return '[GlobalKey# ${shortHash(this)}$label]' ; |
215 | } |
216 | return '[ ${describeIdentity(this)}$label]' ; |
217 | }
|
218 | }
|
219 |
|
220 | /// A global key that takes its identity from the object used as its value.
|
221 | ///
|
222 | /// Used to tie the identity of a widget to the identity of an object used to
|
223 | /// generate that widget.
|
224 | ///
|
225 | /// Any [GlobalObjectKey] created for the same object will match.
|
226 | ///
|
227 | /// If the object is not private, then it is possible that collisions will occur
|
228 | /// where independent widgets will reuse the same object as their
|
229 | /// [GlobalObjectKey] value in a different part of the tree, leading to a global
|
230 | /// key conflict. To avoid this problem, create a private [GlobalObjectKey]
|
231 | /// subclass, as in:
|
232 | ///
|
233 | /// ```dart
|
234 | /// class _MyKey extends GlobalObjectKey {
|
235 | /// const _MyKey(super.value);
|
236 | /// }
|
237 | /// ```
|
238 | ///
|
239 | /// Since the [runtimeType] of the key is part of its identity, this will
|
240 | /// prevent clashes with other [GlobalObjectKey]s even if they have the same
|
241 | /// value.
|
242 | @optionalTypeArgs
|
243 | class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
|
244 | /// Creates a global key that uses [identical] on [value] for its [operator==].
|
245 | const GlobalObjectKey(this.value) : super.constructor();
|
246 |
|
247 | /// The object whose identity is used by this key's [operator==].
|
248 | final Object value;
|
249 |
|
250 | @override
|
251 | bool operator ==(Object other) {
|
252 | if (other.runtimeType != runtimeType) {
|
253 | return false;
|
254 | }
|
255 | return other is GlobalObjectKey<T>
|
256 | && identical(other.value, value);
|
257 | }
|
258 |
|
259 | @override
|
260 | int get hashCode => identityHashCode(value);
|
261 |
|
262 | @override
|
263 | String toString() {
|
264 | String selfType = objectRuntimeType(this, 'GlobalObjectKey' );
|
265 | // The runtimeType string of a GlobalObjectKey() returns 'GlobalObjectKey>'
|
266 | // because GlobalObjectKey is instantiated to its bounds. To avoid cluttering the output
|
267 | // we remove the suffix.
|
268 | const String suffix = '<State<StatefulWidget>>' ;
|
269 | if (selfType.endsWith(suffix)) {
|
270 | selfType = selfType.substring(0, selfType.length - suffix.length);
|
271 | }
|
272 | return '[ $selfType ${describeIdentity(value)}]' ;
|
273 | }
|
274 | }
|
275 |
|
276 | /// Describes the configuration for an [Element].
|
277 | ///
|
278 | /// Widgets are the central class hierarchy in the Flutter framework. A widget
|
279 | /// is an immutable description of part of a user interface. Widgets can be
|
280 | /// inflated into elements, which manage the underlying render tree.
|
281 | ///
|
282 | /// Widgets themselves have no mutable state (all their fields must be final).
|
283 | /// If you wish to associate mutable state with a widget, consider using a
|
284 | /// [StatefulWidget], which creates a [State] object (via
|
285 | /// [StatefulWidget.createState]) whenever it is inflated into an element and
|
286 | /// incorporated into the tree.
|
287 | ///
|
288 | /// A given widget can be included in the tree zero or more times. In particular
|
289 | /// a given widget can be placed in the tree multiple times. Each time a widget
|
290 | /// is placed in the tree, it is inflated into an [Element], which means a
|
291 | /// widget that is incorporated into the tree multiple times will be inflated
|
292 | /// multiple times.
|
293 | ///
|
294 | /// The [key] property controls how one widget replaces another widget in the
|
295 | /// tree. If the [runtimeType] and [key] properties of the two widgets are
|
296 | /// [operator==], respectively, then the new widget replaces the old widget by
|
297 | /// updating the underlying element (i.e., by calling [Element.update] with the
|
298 | /// new widget). Otherwise, the old element is removed from the tree, the new
|
299 | /// widget is inflated into an element, and the new element is inserted into the
|
300 | /// tree.
|
301 | ///
|
302 | /// See also:
|
303 | ///
|
304 | /// * [StatefulWidget] and [State], for widgets that can build differently
|
305 | /// several times over their lifetime.
|
306 | /// * [InheritedWidget], for widgets that introduce ambient state that can
|
307 | /// be read by descendant widgets.
|
308 | /// * [StatelessWidget], for widgets that always build the same way given a
|
309 | /// particular configuration and ambient state.
|
310 | @immutable
|
311 | abstract class Widget extends DiagnosticableTree {
|
312 | /// Initializes [key] for subclasses.
|
313 | const Widget({ this.key });
|
314 |
|
315 | /// Controls how one widget replaces another widget in the tree.
|
316 | ///
|
317 | /// If the [runtimeType] and [key] properties of the two widgets are
|
318 | /// [operator==], respectively, then the new widget replaces the old widget by
|
319 | /// updating the underlying element (i.e., by calling [Element.update] with the
|
320 | /// new widget). Otherwise, the old element is removed from the tree, the new
|
321 | /// widget is inflated into an element, and the new element is inserted into the
|
322 | /// tree.
|
323 | ///
|
324 | /// In addition, using a [GlobalKey] as the widget's [key] allows the element
|
325 | /// to be moved around the tree (changing parent) without losing state. When a
|
326 | /// new widget is found (its key and type do not match a previous widget in
|
327 | /// the same location), but there was a widget with that same global key
|
328 | /// elsewhere in the tree in the previous frame, then that widget's element is
|
329 | /// moved to the new location.
|
330 | ///
|
331 | /// Generally, a widget that is the only child of another widget does not need
|
332 | /// an explicit key.
|
333 | ///
|
334 | /// See also:
|
335 | ///
|
336 | /// * The discussions at [Key] and [GlobalKey].
|
337 | final Key? key;
|
338 |
|
339 | /// Inflates this configuration to a concrete instance.
|
340 | ///
|
341 | /// A given widget can be included in the tree zero or more times. In particular
|
342 | /// a given widget can be placed in the tree multiple times. Each time a widget
|
343 | /// is placed in the tree, it is inflated into an [Element], which means a
|
344 | /// widget that is incorporated into the tree multiple times will be inflated
|
345 | /// multiple times.
|
346 | @protected
|
347 | @factory
|
348 | Element createElement();
|
349 |
|
350 | /// A short, textual description of this widget.
|
351 | @override
|
352 | String toStringShort() {
|
353 | final String type = objectRuntimeType(this, 'Widget' );
|
354 | return key == null ? type : ' $type- $key' ;
|
355 | }
|
356 |
|
357 | @override
|
358 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
359 | super.debugFillProperties(properties);
|
360 | properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
|
361 | }
|
362 |
|
363 | @override
|
364 | @nonVirtual
|
365 | bool operator ==(Object other) => super == other;
|
366 |
|
367 | @override
|
368 | @nonVirtual
|
369 | int get hashCode => super.hashCode;
|
370 |
|
371 | /// Whether the `newWidget` can be used to update an [Element] that currently
|
372 | /// has the `oldWidget` as its configuration.
|
373 | ///
|
374 | /// An element that uses a given widget as its configuration can be updated to
|
375 | /// use another widget as its configuration if, and only if, the two widgets
|
376 | /// have [runtimeType] and [key] properties that are [operator==].
|
377 | ///
|
378 | /// If the widgets have no key (their key is null), then they are considered a
|
379 | /// match if they have the same type, even if their children are completely
|
380 | /// different.
|
381 | static bool canUpdate(Widget oldWidget, Widget newWidget) {
|
382 | return oldWidget.runtimeType == newWidget.runtimeType
|
383 | && oldWidget.key == newWidget.key;
|
384 | }
|
385 |
|
386 | // Return a numeric encoding of the specific `Widget` concrete subtype.
|
387 | // This is used in `Element.updateChild` to determine if a hot reload modified the
|
388 | // superclass of a mounted element's configuration. The encoding of each `Widget`
|
389 | // must match the corresponding `Element` encoding in `Element._debugConcreteSubtype`.
|
390 | static int _debugConcreteSubtype(Widget widget) {
|
391 | return widget is StatefulWidget ? 1 :
|
392 | widget is StatelessWidget ? 2 :
|
393 | 0;
|
394 | }
|
395 | }
|
396 |
|
397 | /// A widget that does not require mutable state.
|
398 | ///
|
399 | /// A stateless widget is a widget that describes part of the user interface by
|
400 | /// building a constellation of other widgets that describe the user interface
|
401 | /// more concretely. The building process continues recursively until the
|
402 | /// description of the user interface is fully concrete (e.g., consists
|
403 | /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s).
|
404 | ///
|
405 | /// {@youtube 560 315 https://www.youtube.com/watch?v=wE7khGHVkYY}
|
406 | ///
|
407 | /// Stateless widget are useful when the part of the user interface you are
|
408 | /// describing does not depend on anything other than the configuration
|
409 | /// information in the object itself and the [BuildContext] in which the widget
|
410 | /// is inflated. For compositions that can change dynamically, e.g. due to
|
411 | /// having an internal clock-driven state, or depending on some system state,
|
412 | /// consider using [StatefulWidget].
|
413 | ///
|
414 | /// ## Performance considerations
|
415 | ///
|
416 | /// The [build] method of a stateless widget is typically only called in three
|
417 | /// situations: the first time the widget is inserted in the tree, when the
|
418 | /// widget's parent changes its configuration (see [Element.rebuild]), and when
|
419 | /// an [InheritedWidget] it depends on changes.
|
420 | ///
|
421 | /// If a widget's parent will regularly change the widget's configuration, or if
|
422 | /// it depends on inherited widgets that frequently change, then it is important
|
423 | /// to optimize the performance of the [build] method to maintain a fluid
|
424 | /// rendering performance.
|
425 | ///
|
426 | /// There are several techniques one can use to minimize the impact of
|
427 | /// rebuilding a stateless widget:
|
428 | ///
|
429 | /// * Minimize the number of nodes transitively created by the build method and
|
430 | /// any widgets it creates. For example, instead of an elaborate arrangement
|
431 | /// of [Row]s, [Column]s, [Padding]s, and [SizedBox]es to position a single
|
432 | /// child in a particularly fancy manner, consider using just an [Align] or a
|
433 | /// [CustomSingleChildLayout]. Instead of an intricate layering of multiple
|
434 | /// [Container]s and with [Decoration]s to draw just the right graphical
|
435 | /// effect, consider a single [CustomPaint] widget.
|
436 | ///
|
437 | /// * Use `const` widgets where possible, and provide a `const` constructor for
|
438 | /// the widget so that users of the widget can also do so.
|
439 | ///
|
440 | /// * Consider refactoring the stateless widget into a stateful widget so that
|
441 | /// it can use some of the techniques described at [StatefulWidget], such as
|
442 | /// caching common parts of subtrees and using [GlobalKey]s when changing the
|
443 | /// tree structure.
|
444 | ///
|
445 | /// * If the widget is likely to get rebuilt frequently due to the use of
|
446 | /// [InheritedWidget]s, consider refactoring the stateless widget into
|
447 | /// multiple widgets, with the parts of the tree that change being pushed to
|
448 | /// the leaves. For example instead of building a tree with four widgets, the
|
449 | /// inner-most widget depending on the [Theme], consider factoring out the
|
450 | /// part of the build function that builds the inner-most widget into its own
|
451 | /// widget, so that only the inner-most widget needs to be rebuilt when the
|
452 | /// theme changes.
|
453 | /// {@template flutter.flutter.widgets.framework.prefer_const_over_helper}
|
454 | /// * When trying to create a reusable piece of UI, prefer using a widget
|
455 | /// rather than a helper method. For example, if there was a function used to
|
456 | /// build a widget, a [State.setState] call would require Flutter to entirely
|
457 | /// rebuild the returned wrapping widget. If a [Widget] was used instead,
|
458 | /// Flutter would be able to efficiently re-render only those parts that
|
459 | /// really need to be updated. Even better, if the created widget is `const`,
|
460 | /// Flutter would short-circuit most of the rebuild work.
|
461 | /// {@endtemplate}
|
462 | ///
|
463 | /// This video gives more explanations on why `const` constructors are important
|
464 | /// and why a [Widget] is better than a helper method.
|
465 | ///
|
466 | /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo}
|
467 | ///
|
468 | /// {@tool snippet}
|
469 | ///
|
470 | /// The following is a skeleton of a stateless widget subclass called `GreenFrog`.
|
471 | ///
|
472 | /// Normally, widgets have more constructor arguments, each of which corresponds
|
473 | /// to a `final` property.
|
474 | ///
|
475 | /// ```dart
|
476 | /// class GreenFrog extends StatelessWidget {
|
477 | /// const GreenFrog({ super.key });
|
478 | ///
|
479 | /// @override
|
480 | /// Widget build(BuildContext context) {
|
481 | /// return Container(color: const Color(0xFF2DBD3A));
|
482 | /// }
|
483 | /// }
|
484 | /// ```
|
485 | /// {@end-tool}
|
486 | ///
|
487 | /// {@tool snippet}
|
488 | ///
|
489 | /// This next example shows the more generic widget `Frog` which can be given
|
490 | /// a color and a child:
|
491 | ///
|
492 | /// ```dart
|
493 | /// class Frog extends StatelessWidget {
|
494 | /// const Frog({
|
495 | /// super.key,
|
496 | /// this.color = const Color(0xFF2DBD3A),
|
497 | /// this.child,
|
498 | /// });
|
499 | ///
|
500 | /// final Color color;
|
501 | /// final Widget? child;
|
502 | ///
|
503 | /// @override
|
504 | /// Widget build(BuildContext context) {
|
505 | /// return ColoredBox(color: color, child: child);
|
506 | /// }
|
507 | /// }
|
508 | /// ```
|
509 | /// {@end-tool}
|
510 | ///
|
511 | /// By convention, widget constructors only use named arguments. Also by
|
512 | /// convention, the first argument is [key], and the last argument is `child`,
|
513 | /// `children`, or the equivalent.
|
514 | ///
|
515 | /// See also:
|
516 | ///
|
517 | /// * [StatefulWidget] and [State], for widgets that can build differently
|
518 | /// several times over their lifetime.
|
519 | /// * [InheritedWidget], for widgets that introduce ambient state that can
|
520 | /// be read by descendant widgets.
|
521 | abstract class StatelessWidget extends Widget {
|
522 | /// Initializes [key] for subclasses.
|
523 | const StatelessWidget({ super.key });
|
524 |
|
525 | /// Creates a [StatelessElement] to manage this widget's location in the tree.
|
526 | ///
|
527 | /// It is uncommon for subclasses to override this method.
|
528 | @override
|
529 | StatelessElement createElement() => StatelessElement(this);
|
530 |
|
531 | /// Describes the part of the user interface represented by this widget.
|
532 | ///
|
533 | /// The framework calls this method when this widget is inserted into the tree
|
534 | /// in a given [BuildContext] and when the dependencies of this widget change
|
535 | /// (e.g., an [InheritedWidget] referenced by this widget changes). This
|
536 | /// method can potentially be called in every frame and should not have any side
|
537 | /// effects beyond building a widget.
|
538 | ///
|
539 | /// The framework replaces the subtree below this widget with the widget
|
540 | /// returned by this method, either by updating the existing subtree or by
|
541 | /// removing the subtree and inflating a new subtree, depending on whether the
|
542 | /// widget returned by this method can update the root of the existing
|
543 | /// subtree, as determined by calling [Widget.canUpdate].
|
544 | ///
|
545 | /// Typically implementations return a newly created constellation of widgets
|
546 | /// that are configured with information from this widget's constructor and
|
547 | /// from the given [BuildContext].
|
548 | ///
|
549 | /// The given [BuildContext] contains information about the location in the
|
550 | /// tree at which this widget is being built. For example, the context
|
551 | /// provides the set of inherited widgets for this location in the tree. A
|
552 | /// given widget might be built with multiple different [BuildContext]
|
553 | /// arguments over time if the widget is moved around the tree or if the
|
554 | /// widget is inserted into the tree in multiple places at once.
|
555 | ///
|
556 | /// The implementation of this method must only depend on:
|
557 | ///
|
558 | /// * the fields of the widget, which themselves must not change over time,
|
559 | /// and
|
560 | /// * any ambient state obtained from the `context` using
|
561 | /// [BuildContext.dependOnInheritedWidgetOfExactType].
|
562 | ///
|
563 | /// If a widget's [build] method is to depend on anything else, use a
|
564 | /// [StatefulWidget] instead.
|
565 | ///
|
566 | /// See also:
|
567 | ///
|
568 | /// * [StatelessWidget], which contains the discussion on performance considerations.
|
569 | @protected
|
570 | Widget build(BuildContext context);
|
571 | }
|
572 |
|
573 | /// A widget that has mutable state.
|
574 | ///
|
575 | /// State is information that (1) can be read synchronously when the widget is
|
576 | /// built and (2) might change during the lifetime of the widget. It is the
|
577 | /// responsibility of the widget implementer to ensure that the [State] is
|
578 | /// promptly notified when such state changes, using [State.setState].
|
579 | ///
|
580 | /// A stateful widget is a widget that describes part of the user interface by
|
581 | /// building a constellation of other widgets that describe the user interface
|
582 | /// more concretely. The building process continues recursively until the
|
583 | /// description of the user interface is fully concrete (e.g., consists
|
584 | /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s).
|
585 | ///
|
586 | /// Stateful widgets are useful when the part of the user interface you are
|
587 | /// describing can change dynamically, e.g. due to having an internal
|
588 | /// clock-driven state, or depending on some system state. For compositions that
|
589 | /// depend only on the configuration information in the object itself and the
|
590 | /// [BuildContext] in which the widget is inflated, consider using
|
591 | /// [StatelessWidget].
|
592 | ///
|
593 | /// {@youtube 560 315 https://www.youtube.com/watch?v=AqCMFXEmf3w}
|
594 | ///
|
595 | /// [StatefulWidget] instances themselves are immutable and store their mutable
|
596 | /// state either in separate [State] objects that are created by the
|
597 | /// [createState] method, or in objects to which that [State] subscribes, for
|
598 | /// example [Stream] or [ChangeNotifier] objects, to which references are stored
|
599 | /// in final fields on the [StatefulWidget] itself.
|
600 | ///
|
601 | /// The framework calls [createState] whenever it inflates a
|
602 | /// [StatefulWidget], which means that multiple [State] objects might be
|
603 | /// associated with the same [StatefulWidget] if that widget has been inserted
|
604 | /// into the tree in multiple places. Similarly, if a [StatefulWidget] is
|
605 | /// removed from the tree and later inserted in to the tree again, the framework
|
606 | /// will call [createState] again to create a fresh [State] object, simplifying
|
607 | /// the lifecycle of [State] objects.
|
608 | ///
|
609 | /// A [StatefulWidget] keeps the same [State] object when moving from one
|
610 | /// location in the tree to another if its creator used a [GlobalKey] for its
|
611 | /// [key]. Because a widget with a [GlobalKey] can be used in at most one
|
612 | /// location in the tree, a widget that uses a [GlobalKey] has at most one
|
613 | /// associated element. The framework takes advantage of this property when
|
614 | /// moving a widget with a global key from one location in the tree to another
|
615 | /// by grafting the (unique) subtree associated with that widget from the old
|
616 | /// location to the new location (instead of recreating the subtree at the new
|
617 | /// location). The [State] objects associated with [StatefulWidget] are grafted
|
618 | /// along with the rest of the subtree, which means the [State] object is reused
|
619 | /// (instead of being recreated) in the new location. However, in order to be
|
620 | /// eligible for grafting, the widget must be inserted into the new location in
|
621 | /// the same animation frame in which it was removed from the old location.
|
622 | ///
|
623 | /// ## Performance considerations
|
624 | ///
|
625 | /// There are two primary categories of [StatefulWidget]s.
|
626 | ///
|
627 | /// The first is one which allocates resources in [State.initState] and disposes
|
628 | /// of them in [State.dispose], but which does not depend on [InheritedWidget]s
|
629 | /// or call [State.setState]. Such widgets are commonly used at the root of an
|
630 | /// application or page, and communicate with subwidgets via [ChangeNotifier]s,
|
631 | /// [Stream]s, or other such objects. Stateful widgets following such a pattern
|
632 | /// are relatively cheap (in terms of CPU and GPU cycles), because they are
|
633 | /// built once then never update. They can, therefore, have somewhat complicated
|
634 | /// and deep build methods.
|
635 | ///
|
636 | /// The second category is widgets that use [State.setState] or depend on
|
637 | /// [InheritedWidget]s. These will typically rebuild many times during the
|
638 | /// application's lifetime, and it is therefore important to minimize the impact
|
639 | /// of rebuilding such a widget. (They may also use [State.initState] or
|
640 | /// [State.didChangeDependencies] and allocate resources, but the important part
|
641 | /// is that they rebuild.)
|
642 | ///
|
643 | /// There are several techniques one can use to minimize the impact of
|
644 | /// rebuilding a stateful widget:
|
645 | ///
|
646 | /// * Push the state to the leaves. For example, if your page has a ticking
|
647 | /// clock, rather than putting the state at the top of the page and
|
648 | /// rebuilding the entire page each time the clock ticks, create a dedicated
|
649 | /// clock widget that only updates itself.
|
650 | ///
|
651 | /// * Minimize the number of nodes transitively created by the build method and
|
652 | /// any widgets it creates. Ideally, a stateful widget would only create a
|
653 | /// single widget, and that widget would be a [RenderObjectWidget].
|
654 | /// (Obviously this isn't always practical, but the closer a widget gets to
|
655 | /// this ideal, the more efficient it will be.)
|
656 | ///
|
657 | /// * If a subtree does not change, cache the widget that represents that
|
658 | /// subtree and re-use it each time it can be used. To do this, assign
|
659 | /// a widget to a `final` state variable and re-use it in the build method. It
|
660 | /// is massively more efficient for a widget to be re-used than for a new (but
|
661 | /// identically-configured) widget to be created. Another caching strategy
|
662 | /// consists in extracting the mutable part of the widget into a [StatefulWidget]
|
663 | /// which accepts a child parameter.
|
664 | ///
|
665 | /// * Use `const` widgets where possible. (This is equivalent to caching a
|
666 | /// widget and re-using it.)
|
667 | ///
|
668 | /// * Avoid changing the depth of any created subtrees or changing the type of
|
669 | /// any widgets in the subtree. For example, rather than returning either the
|
670 | /// child or the child wrapped in an [IgnorePointer], always wrap the child
|
671 | /// widget in an [IgnorePointer] and control the [IgnorePointer.ignoring]
|
672 | /// property. This is because changing the depth of the subtree requires
|
673 | /// rebuilding, laying out, and painting the entire subtree, whereas just
|
674 | /// changing the property will require the least possible change to the
|
675 | /// render tree (in the case of [IgnorePointer], for example, no layout or
|
676 | /// repaint is necessary at all).
|
677 | ///
|
678 | /// * If the depth must be changed for some reason, consider wrapping the
|
679 | /// common parts of the subtrees in widgets that have a [GlobalKey] that
|
680 | /// remains consistent for the life of the stateful widget. (The
|
681 | /// [KeyedSubtree] widget may be useful for this purpose if no other widget
|
682 | /// can conveniently be assigned the key.)
|
683 | ///
|
684 | /// {@macro flutter.flutter.widgets.framework.prefer_const_over_helper}
|
685 | ///
|
686 | /// This video gives more explanations on why `const` constructors are important
|
687 | /// and why a [Widget] is better than a helper method.
|
688 | ///
|
689 | /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo}
|
690 | ///
|
691 | /// For more details on the mechanics of rebuilding a widget, see
|
692 | /// the discussion at [Element.rebuild].
|
693 | ///
|
694 | /// {@tool snippet}
|
695 | ///
|
696 | /// This is a skeleton of a stateful widget subclass called `YellowBird`.
|
697 | ///
|
698 | /// In this example, the [State] has no actual state. State is normally
|
699 | /// represented as private member fields. Also, normally widgets have more
|
700 | /// constructor arguments, each of which corresponds to a `final` property.
|
701 | ///
|
702 | /// ```dart
|
703 | /// class YellowBird extends StatefulWidget {
|
704 | /// const YellowBird({ super.key });
|
705 | ///
|
706 | /// @override
|
707 | /// State<YellowBird> createState() => _YellowBirdState();
|
708 | /// }
|
709 | ///
|
710 | /// class _YellowBirdState extends State<YellowBird> {
|
711 | /// @override
|
712 | /// Widget build(BuildContext context) {
|
713 | /// return Container(color: const Color(0xFFFFE306));
|
714 | /// }
|
715 | /// }
|
716 | /// ```
|
717 | /// {@end-tool}
|
718 | /// {@tool snippet}
|
719 | ///
|
720 | /// This example shows the more generic widget `Bird` which can be given a
|
721 | /// color and a child, and which has some internal state with a method that
|
722 | /// can be called to mutate it:
|
723 | ///
|
724 | /// ```dart
|
725 | /// class Bird extends StatefulWidget {
|
726 | /// const Bird({
|
727 | /// super.key,
|
728 | /// this.color = const Color(0xFFFFE306),
|
729 | /// this.child,
|
730 | /// });
|
731 | ///
|
732 | /// final Color color;
|
733 | /// final Widget? child;
|
734 | ///
|
735 | /// @override
|
736 | /// State<Bird> createState() => _BirdState();
|
737 | /// }
|
738 | ///
|
739 | /// class _BirdState extends State<Bird> {
|
740 | /// double _size = 1.0;
|
741 | ///
|
742 | /// void grow() {
|
743 | /// setState(() { _size += 0.1; });
|
744 | /// }
|
745 | ///
|
746 | /// @override
|
747 | /// Widget build(BuildContext context) {
|
748 | /// return Container(
|
749 | /// color: widget.color,
|
750 | /// transform: Matrix4.diagonal3Values(_size, _size, 1.0),
|
751 | /// child: widget.child,
|
752 | /// );
|
753 | /// }
|
754 | /// }
|
755 | /// ```
|
756 | /// {@end-tool}
|
757 | ///
|
758 | /// By convention, widget constructors only use named arguments. Also by
|
759 | /// convention, the first argument is [key], and the last argument is `child`,
|
760 | /// `children`, or the equivalent.
|
761 | ///
|
762 | /// See also:
|
763 | ///
|
764 | /// * [State], where the logic behind a [StatefulWidget] is hosted.
|
765 | /// * [StatelessWidget], for widgets that always build the same way given a
|
766 | /// particular configuration and ambient state.
|
767 | /// * [InheritedWidget], for widgets that introduce ambient state that can
|
768 | /// be read by descendant widgets.
|
769 | abstract class StatefulWidget extends Widget {
|
770 | /// Initializes [key] for subclasses.
|
771 | const StatefulWidget({ super.key });
|
772 |
|
773 | /// Creates a [StatefulElement] to manage this widget's location in the tree.
|
774 | ///
|
775 | /// It is uncommon for subclasses to override this method.
|
776 | @override
|
777 | StatefulElement createElement() => StatefulElement(this);
|
778 |
|
779 | /// Creates the mutable state for this widget at a given location in the tree.
|
780 | ///
|
781 | /// Subclasses should override this method to return a newly created
|
782 | /// instance of their associated [State] subclass:
|
783 | ///
|
784 | /// ```dart
|
785 | /// @override
|
786 | /// State<SomeWidget> createState() => _SomeWidgetState();
|
787 | /// ```
|
788 | ///
|
789 | /// The framework can call this method multiple times over the lifetime of
|
790 | /// a [StatefulWidget]. For example, if the widget is inserted into the tree
|
791 | /// in multiple locations, the framework will create a separate [State] object
|
792 | /// for each location. Similarly, if the widget is removed from the tree and
|
793 | /// later inserted into the tree again, the framework will call [createState]
|
794 | /// again to create a fresh [State] object, simplifying the lifecycle of
|
795 | /// [State] objects.
|
796 | @protected
|
797 | @factory
|
798 | State createState();
|
799 | }
|
800 |
|
801 | /// Tracks the lifecycle of [State] objects when asserts are enabled.
|
802 | enum _StateLifecycle {
|
803 | /// The [State] object has been created. [State.initState] is called at this
|
804 | /// time.
|
805 | created,
|
806 |
|
807 | /// The [State.initState] method has been called but the [State] object is
|
808 | /// not yet ready to build. [State.didChangeDependencies] is called at this time.
|
809 | initialized,
|
810 |
|
811 | /// The [State] object is ready to build and [State.dispose] has not yet been
|
812 | /// called.
|
813 | ready,
|
814 |
|
815 | /// The [State.dispose] method has been called and the [State] object is
|
816 | /// no longer able to build.
|
817 | defunct,
|
818 | }
|
819 |
|
820 | /// The signature of [State.setState] functions.
|
821 | typedef StateSetter = void Function(VoidCallback fn);
|
822 |
|
823 | const String _flutterWidgetsLibrary = 'package:flutter/widgets.dart' ;
|
824 |
|
825 | /// The logic and internal state for a [StatefulWidget].
|
826 | ///
|
827 | /// State is information that (1) can be read synchronously when the widget is
|
828 | /// built and (2) might change during the lifetime of the widget. It is the
|
829 | /// responsibility of the widget implementer to ensure that the [State] is
|
830 | /// promptly notified when such state changes, using [State.setState].
|
831 | ///
|
832 | /// [State] objects are created by the framework by calling the
|
833 | /// [StatefulWidget.createState] method when inflating a [StatefulWidget] to
|
834 | /// insert it into the tree. Because a given [StatefulWidget] instance can be
|
835 | /// inflated multiple times (e.g., the widget is incorporated into the tree in
|
836 | /// multiple places at once), there might be more than one [State] object
|
837 | /// associated with a given [StatefulWidget] instance. Similarly, if a
|
838 | /// [StatefulWidget] is removed from the tree and later inserted in to the tree
|
839 | /// again, the framework will call [StatefulWidget.createState] again to create
|
840 | /// a fresh [State] object, simplifying the lifecycle of [State] objects.
|
841 | ///
|
842 | /// [State] objects have the following lifecycle:
|
843 | ///
|
844 | /// * The framework creates a [State] object by calling
|
845 | /// [StatefulWidget.createState].
|
846 | /// * The newly created [State] object is associated with a [BuildContext].
|
847 | /// This association is permanent: the [State] object will never change its
|
848 | /// [BuildContext]. However, the [BuildContext] itself can be moved around
|
849 | /// the tree along with its subtree. At this point, the [State] object is
|
850 | /// considered [mounted].
|
851 | /// * The framework calls [initState]. Subclasses of [State] should override
|
852 | /// [initState] to perform one-time initialization that depends on the
|
853 | /// [BuildContext] or the widget, which are available as the [context] and
|
854 | /// [widget] properties, respectively, when the [initState] method is
|
855 | /// called.
|
856 | /// * The framework calls [didChangeDependencies]. Subclasses of [State] should
|
857 | /// override [didChangeDependencies] to perform initialization involving
|
858 | /// [InheritedWidget]s. If [BuildContext.dependOnInheritedWidgetOfExactType] is
|
859 | /// called, the [didChangeDependencies] method will be called again if the
|
860 | /// inherited widgets subsequently change or if the widget moves in the tree.
|
861 | /// * At this point, the [State] object is fully initialized and the framework
|
862 | /// might call its [build] method any number of times to obtain a
|
863 | /// description of the user interface for this subtree. [State] objects can
|
864 | /// spontaneously request to rebuild their subtree by calling their
|
865 | /// [setState] method, which indicates that some of their internal state
|
866 | /// has changed in a way that might impact the user interface in this
|
867 | /// subtree.
|
868 | /// * During this time, a parent widget might rebuild and request that this
|
869 | /// location in the tree update to display a new widget with the same
|
870 | /// [runtimeType] and [Widget.key]. When this happens, the framework will
|
871 | /// update the [widget] property to refer to the new widget and then call the
|
872 | /// [didUpdateWidget] method with the previous widget as an argument. [State]
|
873 | /// objects should override [didUpdateWidget] to respond to changes in their
|
874 | /// associated widget (e.g., to start implicit animations). The framework
|
875 | /// always calls [build] after calling [didUpdateWidget], which means any
|
876 | /// calls to [setState] in [didUpdateWidget] are redundant. (See also the
|
877 | /// discussion at [Element.rebuild].)
|
878 | /// * During development, if a hot reload occurs (whether initiated from the
|
879 | /// command line `flutter` tool by pressing `r`, or from an IDE), the
|
880 | /// [reassemble] method is called. This provides an opportunity to
|
881 | /// reinitialize any data that was prepared in the [initState] method.
|
882 | /// * If the subtree containing the [State] object is removed from the tree
|
883 | /// (e.g., because the parent built a widget with a different [runtimeType]
|
884 | /// or [Widget.key]), the framework calls the [deactivate] method. Subclasses
|
885 | /// should override this method to clean up any links between this object
|
886 | /// and other elements in the tree (e.g. if you have provided an ancestor
|
887 | /// with a pointer to a descendant's [RenderObject]).
|
888 | /// * At this point, the framework might reinsert this subtree into another
|
889 | /// part of the tree. If that happens, the framework will ensure that it
|
890 | /// calls [build] to give the [State] object a chance to adapt to its new
|
891 | /// location in the tree. If the framework does reinsert this subtree, it
|
892 | /// will do so before the end of the animation frame in which the subtree was
|
893 | /// removed from the tree. For this reason, [State] objects can defer
|
894 | /// releasing most resources until the framework calls their [dispose]
|
895 | /// method.
|
896 | /// * If the framework does not reinsert this subtree by the end of the current
|
897 | /// animation frame, the framework will call [dispose], which indicates that
|
898 | /// this [State] object will never build again. Subclasses should override
|
899 | /// this method to release any resources retained by this object (e.g.,
|
900 | /// stop any active animations).
|
901 | /// * After the framework calls [dispose], the [State] object is considered
|
902 | /// unmounted and the [mounted] property is false. It is an error to call
|
903 | /// [setState] at this point. This stage of the lifecycle is terminal: there
|
904 | /// is no way to remount a [State] object that has been disposed.
|
905 | ///
|
906 | /// See also:
|
907 | ///
|
908 | /// * [StatefulWidget], where the current configuration of a [State] is hosted,
|
909 | /// and whose documentation has sample code for [State].
|
910 | /// * [StatelessWidget], for widgets that always build the same way given a
|
911 | /// particular configuration and ambient state.
|
912 | /// * [InheritedWidget], for widgets that introduce ambient state that can
|
913 | /// be read by descendant widgets.
|
914 | /// * [Widget], for an overview of widgets in general.
|
915 | @optionalTypeArgs
|
916 | abstract class State<T extends StatefulWidget> with Diagnosticable {
|
917 | /// The current configuration.
|
918 | ///
|
919 | /// A [State] object's configuration is the corresponding [StatefulWidget]
|
920 | /// instance. This property is initialized by the framework before calling
|
921 | /// [initState]. If the parent updates this location in the tree to a new
|
922 | /// widget with the same [runtimeType] and [Widget.key] as the current
|
923 | /// configuration, the framework will update this property to refer to the new
|
924 | /// widget and then call [didUpdateWidget], passing the old configuration as
|
925 | /// an argument.
|
926 | T get widget => _widget!;
|
927 | T? _widget;
|
928 |
|
929 | /// The current stage in the lifecycle for this state object.
|
930 | ///
|
931 | /// This field is used by the framework when asserts are enabled to verify
|
932 | /// that [State] objects move through their lifecycle in an orderly fashion.
|
933 | _StateLifecycle _debugLifecycleState = _StateLifecycle.created;
|
934 |
|
935 | /// Verifies that the [State] that was created is one that expects to be
|
936 | /// created for that particular [Widget].
|
937 | bool _debugTypesAreRight(Widget widget) => widget is T;
|
938 |
|
939 | /// The location in the tree where this widget builds.
|
940 | ///
|
941 | /// The framework associates [State] objects with a [BuildContext] after
|
942 | /// creating them with [StatefulWidget.createState] and before calling
|
943 | /// [initState]. The association is permanent: the [State] object will never
|
944 | /// change its [BuildContext]. However, the [BuildContext] itself can be moved
|
945 | /// around the tree.
|
946 | ///
|
947 | /// After calling [dispose], the framework severs the [State] object's
|
948 | /// connection with the [BuildContext].
|
949 | BuildContext get context {
|
950 | assert(() {
|
951 | if (_element == null) {
|
952 | throw FlutterError(
|
953 | 'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n'
|
954 | 'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.' ,
|
955 | );
|
956 | }
|
957 | return true;
|
958 | }());
|
959 | return _element!;
|
960 | }
|
961 | StatefulElement? _element;
|
962 |
|
963 | /// Whether this [State] object is currently in a tree.
|
964 | ///
|
965 | /// After creating a [State] object and before calling [initState], the
|
966 | /// framework "mounts" the [State] object by associating it with a
|
967 | /// [BuildContext]. The [State] object remains mounted until the framework
|
968 | /// calls [dispose], after which time the framework will never ask the [State]
|
969 | /// object to [build] again.
|
970 | ///
|
971 | /// It is an error to call [setState] unless [mounted] is true.
|
972 | bool get mounted => _element != null;
|
973 |
|
974 | /// Called when this object is inserted into the tree.
|
975 | ///
|
976 | /// The framework will call this method exactly once for each [State] object
|
977 | /// it creates.
|
978 | ///
|
979 | /// Override this method to perform initialization that depends on the
|
980 | /// location at which this object was inserted into the tree (i.e., [context])
|
981 | /// or on the widget used to configure this object (i.e., [widget]).
|
982 | ///
|
983 | /// {@template flutter.widgets.State.initState}
|
984 | /// If a [State]'s [build] method depends on an object that can itself
|
985 | /// change state, for example a [ChangeNotifier] or [Stream], or some
|
986 | /// other object to which one can subscribe to receive notifications, then
|
987 | /// be sure to subscribe and unsubscribe properly in [initState],
|
988 | /// [didUpdateWidget], and [dispose]:
|
989 | ///
|
990 | /// * In [initState], subscribe to the object.
|
991 | /// * In [didUpdateWidget] unsubscribe from the old object and subscribe
|
992 | /// to the new one if the updated widget configuration requires
|
993 | /// replacing the object.
|
994 | /// * In [dispose], unsubscribe from the object.
|
995 | ///
|
996 | /// {@endtemplate}
|
997 | ///
|
998 | /// You should not use [BuildContext.dependOnInheritedWidgetOfExactType] from this
|
999 | /// method. However, [didChangeDependencies] will be called immediately
|
1000 | /// following this method, and [BuildContext.dependOnInheritedWidgetOfExactType] can
|
1001 | /// be used there.
|
1002 | ///
|
1003 | /// Implementations of this method should start with a call to the inherited
|
1004 | /// method, as in `super.initState()`.
|
1005 | @protected
|
1006 | @mustCallSuper
|
1007 | void initState() {
|
1008 | assert(_debugLifecycleState == _StateLifecycle.created);
|
1009 | if (kFlutterMemoryAllocationsEnabled) {
|
1010 | FlutterMemoryAllocations.instance.dispatchObjectCreated(
|
1011 | library: _flutterWidgetsLibrary,
|
1012 | className: ' $State' ,
|
1013 | object: this,
|
1014 | );
|
1015 | }
|
1016 | }
|
1017 |
|
1018 | /// Called whenever the widget configuration changes.
|
1019 | ///
|
1020 | /// If the parent widget rebuilds and requests that this location in the tree
|
1021 | /// update to display a new widget with the same [runtimeType] and
|
1022 | /// [Widget.key], the framework will update the [widget] property of this
|
1023 | /// [State] object to refer to the new widget and then call this method
|
1024 | /// with the previous widget as an argument.
|
1025 | ///
|
1026 | /// Override this method to respond when the [widget] changes (e.g., to start
|
1027 | /// implicit animations).
|
1028 | ///
|
1029 | /// The framework always calls [build] after calling [didUpdateWidget], which
|
1030 | /// means any calls to [setState] in [didUpdateWidget] are redundant.
|
1031 | ///
|
1032 | /// {@macro flutter.widgets.State.initState}
|
1033 | ///
|
1034 | /// Implementations of this method should start with a call to the inherited
|
1035 | /// method, as in `super.didUpdateWidget(oldWidget)`.
|
1036 | ///
|
1037 | /// _See the discussion at [Element.rebuild] for more information on when this
|
1038 | /// method is called._
|
1039 | @mustCallSuper
|
1040 | @protected
|
1041 | void didUpdateWidget(covariant T oldWidget) { }
|
1042 |
|
1043 | /// {@macro flutter.widgets.Element.reassemble}
|
1044 | ///
|
1045 | /// In addition to this method being invoked, it is guaranteed that the
|
1046 | /// [build] method will be invoked when a reassemble is signaled. Most
|
1047 | /// widgets therefore do not need to do anything in the [reassemble] method.
|
1048 | ///
|
1049 | /// See also:
|
1050 | ///
|
1051 | /// * [Element.reassemble]
|
1052 | /// * [BindingBase.reassembleApplication]
|
1053 | /// * [Image], which uses this to reload images.
|
1054 | @protected
|
1055 | @mustCallSuper
|
1056 | void reassemble() { }
|
1057 |
|
1058 | /// Notify the framework that the internal state of this object has changed.
|
1059 | ///
|
1060 | /// Whenever you change the internal state of a [State] object, make the
|
1061 | /// change in a function that you pass to [setState]:
|
1062 | ///
|
1063 | /// ```dart
|
1064 | /// setState(() { _myState = newValue; });
|
1065 | /// ```
|
1066 | ///
|
1067 | /// The provided callback is immediately called synchronously. It must not
|
1068 | /// return a future (the callback cannot be `async`), since then it would be
|
1069 | /// unclear when the state was actually being set.
|
1070 | ///
|
1071 | /// Calling [setState] notifies the framework that the internal state of this
|
1072 | /// object has changed in a way that might impact the user interface in this
|
1073 | /// subtree, which causes the framework to schedule a [build] for this [State]
|
1074 | /// object.
|
1075 | ///
|
1076 | /// If you just change the state directly without calling [setState], the
|
1077 | /// framework might not schedule a [build] and the user interface for this
|
1078 | /// subtree might not be updated to reflect the new state.
|
1079 | ///
|
1080 | /// Generally it is recommended that the [setState] method only be used to
|
1081 | /// wrap the actual changes to the state, not any computation that might be
|
1082 | /// associated with the change. For example, here a value used by the [build]
|
1083 | /// function is incremented, and then the change is written to disk, but only
|
1084 | /// the increment is wrapped in the [setState]:
|
1085 | ///
|
1086 | /// ```dart
|
1087 | /// Future<void> _incrementCounter() async {
|
1088 | /// setState(() {
|
1089 | /// _counter++;
|
1090 | /// });
|
1091 | /// Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package
|
1092 | /// final String dirName = directory.path;
|
1093 | /// await File('$dirName/counter.txt').writeAsString('$_counter');
|
1094 | /// }
|
1095 | /// ```
|
1096 | ///
|
1097 | /// Sometimes, the changed state is in some other object not owned by the
|
1098 | /// widget [State], but the widget nonetheless needs to be updated to react to
|
1099 | /// the new state. This is especially common with [Listenable]s, such as
|
1100 | /// [AnimationController]s.
|
1101 | ///
|
1102 | /// In such cases, it is good practice to leave a comment in the callback
|
1103 | /// passed to [setState] that explains what state changed:
|
1104 | ///
|
1105 | /// ```dart
|
1106 | /// void _update() {
|
1107 | /// setState(() { /* The animation changed. */ });
|
1108 | /// }
|
1109 | /// //...
|
1110 | /// animation.addListener(_update);
|
1111 | /// ```
|
1112 | ///
|
1113 | /// It is an error to call this method after the framework calls [dispose].
|
1114 | /// You can determine whether it is legal to call this method by checking
|
1115 | /// whether the [mounted] property is true. That said, it is better practice
|
1116 | /// to cancel whatever work might trigger the [setState] rather than merely
|
1117 | /// checking for [mounted] before calling [setState], as otherwise CPU cycles
|
1118 | /// will be wasted.
|
1119 | ///
|
1120 | /// ## Design discussion
|
1121 | ///
|
1122 | /// The original version of this API was a method called `markNeedsBuild`, for
|
1123 | /// consistency with [RenderObject.markNeedsLayout],
|
1124 | /// [RenderObject.markNeedsPaint], _et al_.
|
1125 | ///
|
1126 | /// However, early user testing of the Flutter framework revealed that people
|
1127 | /// would call `markNeedsBuild()` much more often than necessary. Essentially,
|
1128 | /// people used it like a good luck charm, any time they weren't sure if they
|
1129 | /// needed to call it, they would call it, just in case.
|
1130 | ///
|
1131 | /// Naturally, this led to performance issues in applications.
|
1132 | ///
|
1133 | /// When the API was changed to take a callback instead, this practice was
|
1134 | /// greatly reduced. One hypothesis is that prompting developers to actually
|
1135 | /// update their state in a callback caused developers to think more carefully
|
1136 | /// about what exactly was being updated, and thus improved their understanding
|
1137 | /// of the appropriate times to call the method.
|
1138 | ///
|
1139 | /// In practice, the [setState] method's implementation is trivial: it calls
|
1140 | /// the provided callback synchronously, then calls [Element.markNeedsBuild].
|
1141 | ///
|
1142 | /// ## Performance considerations
|
1143 | ///
|
1144 | /// There is minimal _direct_ overhead to calling this function, and as it is
|
1145 | /// expected to be called at most once per frame, the overhead is irrelevant
|
1146 | /// anyway. Nonetheless, it is best to avoid calling this function redundantly
|
1147 | /// (e.g. in a tight loop), as it does involve creating a closure and calling
|
1148 | /// it. The method is idempotent, there is no benefit to calling it more than
|
1149 | /// once per [State] per frame.
|
1150 | ///
|
1151 | /// The _indirect_ cost of causing this function, however, is high: it causes
|
1152 | /// the widget to rebuild, possibly triggering rebuilds for the entire subtree
|
1153 | /// rooted at this widget, and further triggering a relayout and repaint of
|
1154 | /// the entire corresponding [RenderObject] subtree.
|
1155 | ///
|
1156 | /// For this reason, this method should only be called when the [build] method
|
1157 | /// will, as a result of whatever state change was detected, change its result
|
1158 | /// meaningfully.
|
1159 | ///
|
1160 | /// See also:
|
1161 | ///
|
1162 | /// * [StatefulWidget], the API documentation for which has a section on
|
1163 | /// performance considerations that are relevant here.
|
1164 | @protected
|
1165 | void setState(VoidCallback fn) {
|
1166 | assert(() {
|
1167 | if (_debugLifecycleState == _StateLifecycle.defunct) {
|
1168 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
1169 | ErrorSummary('setState() called after dispose(): $this' ),
|
1170 | ErrorDescription(
|
1171 | 'This error happens if you call setState() on a State object for a widget that '
|
1172 | 'no longer appears in the widget tree (e.g., whose parent widget no longer '
|
1173 | 'includes the widget in its build). This error can occur when code calls '
|
1174 | 'setState() from a timer or an animation callback.' ,
|
1175 | ),
|
1176 | ErrorHint(
|
1177 | 'The preferred solution is '
|
1178 | 'to cancel the timer or stop listening to the animation in the dispose() '
|
1179 | 'callback. Another solution is to check the "mounted" property of this '
|
1180 | 'object before calling setState() to ensure the object is still in the '
|
1181 | 'tree.' ,
|
1182 | ),
|
1183 | ErrorHint(
|
1184 | 'This error might indicate a memory leak if setState() is being called '
|
1185 | 'because another object is retaining a reference to this State object '
|
1186 | 'after it has been removed from the tree. To avoid memory leaks, '
|
1187 | 'consider breaking the reference to this object during dispose().' ,
|
1188 | ),
|
1189 | ]);
|
1190 | }
|
1191 | if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
|
1192 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
1193 | ErrorSummary('setState() called in constructor: $this' ),
|
1194 | ErrorHint(
|
1195 | 'This happens when you call setState() on a State object for a widget that '
|
1196 | "hasn't been inserted into the widget tree yet. It is not necessary to call "
|
1197 | 'setState() in the constructor, since the state is already assumed to be dirty '
|
1198 | 'when it is initially created.' ,
|
1199 | ),
|
1200 | ]);
|
1201 | }
|
1202 | return true;
|
1203 | }());
|
1204 | final Object? result = fn() as dynamic;
|
1205 | assert(() {
|
1206 | if (result is Future) {
|
1207 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
1208 | ErrorSummary('setState() callback argument returned a Future.' ),
|
1209 | ErrorDescription(
|
1210 | 'The setState() method on $this was called with a closure or method that '
|
1211 | 'returned a Future. Maybe it is marked as "async".' ,
|
1212 | ),
|
1213 | ErrorHint(
|
1214 | 'Instead of performing asynchronous work inside a call to setState(), first '
|
1215 | 'execute the work (without updating the widget state), and then synchronously '
|
1216 | 'update the state inside a call to setState().' ,
|
1217 | ),
|
1218 | ]);
|
1219 | }
|
1220 | // We ignore other types of return values so that you can do things like:
|
1221 | // setState(() => x = 3);
|
1222 | return true;
|
1223 | }());
|
1224 | _element!.markNeedsBuild();
|
1225 | }
|
1226 |
|
1227 | /// Called when this object is removed from the tree.
|
1228 | ///
|
1229 | /// The framework calls this method whenever it removes this [State] object
|
1230 | /// from the tree. In some cases, the framework will reinsert the [State]
|
1231 | /// object into another part of the tree (e.g., if the subtree containing this
|
1232 | /// [State] object is grafted from one location in the tree to another due to
|
1233 | /// the use of a [GlobalKey]). If that happens, the framework will call
|
1234 | /// [activate] to give the [State] object a chance to reacquire any resources
|
1235 | /// that it released in [deactivate]. It will then also call [build] to give
|
1236 | /// the [State] object a chance to adapt to its new location in the tree. If
|
1237 | /// the framework does reinsert this subtree, it will do so before the end of
|
1238 | /// the animation frame in which the subtree was removed from the tree. For
|
1239 | /// this reason, [State] objects can defer releasing most resources until the
|
1240 | /// framework calls their [dispose] method.
|
1241 | ///
|
1242 | /// Subclasses should override this method to clean up any links between
|
1243 | /// this object and other elements in the tree (e.g. if you have provided an
|
1244 | /// ancestor with a pointer to a descendant's [RenderObject]).
|
1245 | ///
|
1246 | /// Implementations of this method should end with a call to the inherited
|
1247 | /// method, as in `super.deactivate()`.
|
1248 | ///
|
1249 | /// See also:
|
1250 | ///
|
1251 | /// * [dispose], which is called after [deactivate] if the widget is removed
|
1252 | /// from the tree permanently.
|
1253 | @protected
|
1254 | @mustCallSuper
|
1255 | void deactivate() { }
|
1256 |
|
1257 | /// Called when this object is reinserted into the tree after having been
|
1258 | /// removed via [deactivate].
|
1259 | ///
|
1260 | /// In most cases, after a [State] object has been deactivated, it is _not_
|
1261 | /// reinserted into the tree, and its [dispose] method will be called to
|
1262 | /// signal that it is ready to be garbage collected.
|
1263 | ///
|
1264 | /// In some cases, however, after a [State] object has been deactivated, the
|
1265 | /// framework will reinsert it into another part of the tree (e.g., if the
|
1266 | /// subtree containing this [State] object is grafted from one location in
|
1267 | /// the tree to another due to the use of a [GlobalKey]). If that happens,
|
1268 | /// the framework will call [activate] to give the [State] object a chance to
|
1269 | /// reacquire any resources that it released in [deactivate]. It will then
|
1270 | /// also call [build] to give the object a chance to adapt to its new
|
1271 | /// location in the tree. If the framework does reinsert this subtree, it
|
1272 | /// will do so before the end of the animation frame in which the subtree was
|
1273 | /// removed from the tree. For this reason, [State] objects can defer
|
1274 | /// releasing most resources until the framework calls their [dispose] method.
|
1275 | ///
|
1276 | /// The framework does not call this method the first time a [State] object
|
1277 | /// is inserted into the tree. Instead, the framework calls [initState] in
|
1278 | /// that situation.
|
1279 | ///
|
1280 | /// Implementations of this method should start with a call to the inherited
|
1281 | /// method, as in `super.activate()`.
|
1282 | ///
|
1283 | /// See also:
|
1284 | ///
|
1285 | /// * [Element.activate], the corresponding method when an element
|
1286 | /// transitions from the "inactive" to the "active" lifecycle state.
|
1287 | @protected
|
1288 | @mustCallSuper
|
1289 | void activate() { }
|
1290 |
|
1291 | /// Called when this object is removed from the tree permanently.
|
1292 | ///
|
1293 | /// The framework calls this method when this [State] object will never
|
1294 | /// build again. After the framework calls [dispose], the [State] object is
|
1295 | /// considered unmounted and the [mounted] property is false. It is an error
|
1296 | /// to call [setState] at this point. This stage of the lifecycle is terminal:
|
1297 | /// there is no way to remount a [State] object that has been disposed.
|
1298 | ///
|
1299 | /// Subclasses should override this method to release any resources retained
|
1300 | /// by this object (e.g., stop any active animations).
|
1301 | ///
|
1302 | /// {@macro flutter.widgets.State.initState}
|
1303 | ///
|
1304 | /// Implementations of this method should end with a call to the inherited
|
1305 | /// method, as in `super.dispose()`.
|
1306 | ///
|
1307 | /// ## Caveats
|
1308 | ///
|
1309 | /// This method is _not_ invoked at times where a developer might otherwise
|
1310 | /// expect it, such as application shutdown or dismissal via platform
|
1311 | /// native methods.
|
1312 | ///
|
1313 | /// ### Application shutdown
|
1314 | ///
|
1315 | /// There is no way to predict when application shutdown will happen. For
|
1316 | /// example, a user's battery could catch fire, or the user could drop the
|
1317 | /// device into a swimming pool, or the operating system could unilaterally
|
1318 | /// terminate the application process due to memory pressure.
|
1319 | ///
|
1320 | /// Applications are responsible for ensuring that they are well-behaved
|
1321 | /// even in the face of a rapid unscheduled termination.
|
1322 | ///
|
1323 | /// To artificially cause the entire widget tree to be disposed, consider
|
1324 | /// calling [runApp] with a widget such as [SizedBox.shrink].
|
1325 | ///
|
1326 | /// To listen for platform shutdown messages (and other lifecycle changes),
|
1327 | /// consider the [AppLifecycleListener] API.
|
1328 | ///
|
1329 | /// {@macro flutter.widgets.runApp.dismissal}
|
1330 | ///
|
1331 | /// See the method used to bootstrap the app (e.g. [runApp] or [runWidget])
|
1332 | /// for suggestions on how to release resources more eagerly.
|
1333 | ///
|
1334 | /// See also:
|
1335 | ///
|
1336 | /// * [deactivate], which is called prior to [dispose].
|
1337 | @protected
|
1338 | @mustCallSuper
|
1339 | void dispose() {
|
1340 | assert(_debugLifecycleState == _StateLifecycle.ready);
|
1341 | assert(() {
|
1342 | _debugLifecycleState = _StateLifecycle.defunct;
|
1343 | return true;
|
1344 | }());
|
1345 | if (kFlutterMemoryAllocationsEnabled) {
|
1346 | FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
1347 | }
|
1348 | }
|
1349 |
|
1350 | /// Describes the part of the user interface represented by this widget.
|
1351 | ///
|
1352 | /// The framework calls this method in a number of different situations. For
|
1353 | /// example:
|
1354 | ///
|
1355 | /// * After calling [initState].
|
1356 | /// * After calling [didUpdateWidget].
|
1357 | /// * After receiving a call to [setState].
|
1358 | /// * After a dependency of this [State] object changes (e.g., an
|
1359 | /// [InheritedWidget] referenced by the previous [build] changes).
|
1360 | /// * After calling [deactivate] and then reinserting the [State] object into
|
1361 | /// the tree at another location.
|
1362 | ///
|
1363 | /// This method can potentially be called in every frame and should not have
|
1364 | /// any side effects beyond building a widget.
|
1365 | ///
|
1366 | /// The framework replaces the subtree below this widget with the widget
|
1367 | /// returned by this method, either by updating the existing subtree or by
|
1368 | /// removing the subtree and inflating a new subtree, depending on whether the
|
1369 | /// widget returned by this method can update the root of the existing
|
1370 | /// subtree, as determined by calling [Widget.canUpdate].
|
1371 | ///
|
1372 | /// Typically implementations return a newly created constellation of widgets
|
1373 | /// that are configured with information from this widget's constructor, the
|
1374 | /// given [BuildContext], and the internal state of this [State] object.
|
1375 | ///
|
1376 | /// The given [BuildContext] contains information about the location in the
|
1377 | /// tree at which this widget is being built. For example, the context
|
1378 | /// provides the set of inherited widgets for this location in the tree. The
|
1379 | /// [BuildContext] argument is always the same as the [context] property of
|
1380 | /// this [State] object and will remain the same for the lifetime of this
|
1381 | /// object. The [BuildContext] argument is provided redundantly here so that
|
1382 | /// this method matches the signature for a [WidgetBuilder].
|
1383 | ///
|
1384 | /// ## Design discussion
|
1385 | ///
|
1386 | /// ### Why is the [build] method on [State], and not [StatefulWidget]?
|
1387 | ///
|
1388 | /// Putting a `Widget build(BuildContext context)` method on [State] rather
|
1389 | /// than putting a `Widget build(BuildContext context, State state)` method
|
1390 | /// on [StatefulWidget] gives developers more flexibility when subclassing
|
1391 | /// [StatefulWidget].
|
1392 | ///
|
1393 | /// For example, [AnimatedWidget] is a subclass of [StatefulWidget] that
|
1394 | /// introduces an abstract `Widget build(BuildContext context)` method for its
|
1395 | /// subclasses to implement. If [StatefulWidget] already had a [build] method
|
1396 | /// that took a [State] argument, [AnimatedWidget] would be forced to provide
|
1397 | /// its [State] object to subclasses even though its [State] object is an
|
1398 | /// internal implementation detail of [AnimatedWidget].
|
1399 | ///
|
1400 | /// Conceptually, [StatelessWidget] could also be implemented as a subclass of
|
1401 | /// [StatefulWidget] in a similar manner. If the [build] method were on
|
1402 | /// [StatefulWidget] rather than [State], that would not be possible anymore.
|
1403 | ///
|
1404 | /// Putting the [build] function on [State] rather than [StatefulWidget] also
|
1405 | /// helps avoid a category of bugs related to closures implicitly capturing
|
1406 | /// `this`. If you defined a closure in a [build] function on a
|
1407 | /// [StatefulWidget], that closure would implicitly capture `this`, which is
|
1408 | /// the current widget instance, and would have the (immutable) fields of that
|
1409 | /// instance in scope:
|
1410 | ///
|
1411 | /// ```dart
|
1412 | /// // (this is not valid Flutter code)
|
1413 | /// class MyButton extends StatefulWidgetX {
|
1414 | /// MyButton({super.key, required this.color});
|
1415 | ///
|
1416 | /// final Color color;
|
1417 | ///
|
1418 | /// @override
|
1419 | /// Widget build(BuildContext context, State state) {
|
1420 | /// return SpecialWidget(
|
1421 | /// handler: () { print('color: $color'); },
|
1422 | /// );
|
1423 | /// }
|
1424 | /// }
|
1425 | /// ```
|
1426 | ///
|
1427 | /// For example, suppose the parent builds `MyButton` with `color` being blue,
|
1428 | /// the `$color` in the print function refers to blue, as expected. Now,
|
1429 | /// suppose the parent rebuilds `MyButton` with green. The closure created by
|
1430 | /// the first build still implicitly refers to the original widget and the
|
1431 | /// `$color` still prints blue even through the widget has been updated to
|
1432 | /// green; should that closure outlive its widget, it would print outdated
|
1433 | /// information.
|
1434 | ///
|
1435 | /// In contrast, with the [build] function on the [State] object, closures
|
1436 | /// created during [build] implicitly capture the [State] instance instead of
|
1437 | /// the widget instance:
|
1438 | ///
|
1439 | /// ```dart
|
1440 | /// class MyButton extends StatefulWidget {
|
1441 | /// const MyButton({super.key, this.color = Colors.teal});
|
1442 | ///
|
1443 | /// final Color color;
|
1444 | /// // ...
|
1445 | /// }
|
1446 | ///
|
1447 | /// class MyButtonState extends State<MyButton> {
|
1448 | /// // ...
|
1449 | /// @override
|
1450 | /// Widget build(BuildContext context) {
|
1451 | /// return SpecialWidget(
|
1452 | /// handler: () { print('color: ${widget.color}'); },
|
1453 | /// );
|
1454 | /// }
|
1455 | /// }
|
1456 | /// ```
|
1457 | ///
|
1458 | /// Now when the parent rebuilds `MyButton` with green, the closure created by
|
1459 | /// the first build still refers to [State] object, which is preserved across
|
1460 | /// rebuilds, but the framework has updated that [State] object's [widget]
|
1461 | /// property to refer to the new `MyButton` instance and `${widget.color}`
|
1462 | /// prints green, as expected.
|
1463 | ///
|
1464 | /// See also:
|
1465 | ///
|
1466 | /// * [StatefulWidget], which contains the discussion on performance considerations.
|
1467 | @protected
|
1468 | Widget build(BuildContext context);
|
1469 |
|
1470 | /// Called when a dependency of this [State] object changes.
|
1471 | ///
|
1472 | /// For example, if the previous call to [build] referenced an
|
1473 | /// [InheritedWidget] that later changed, the framework would call this
|
1474 | /// method to notify this object about the change.
|
1475 | ///
|
1476 | /// This method is also called immediately after [initState]. It is safe to
|
1477 | /// call [BuildContext.dependOnInheritedWidgetOfExactType] from this method.
|
1478 | ///
|
1479 | /// Subclasses rarely override this method because the framework always
|
1480 | /// calls [build] after a dependency changes. Some subclasses do override
|
1481 | /// this method because they need to do some expensive work (e.g., network
|
1482 | /// fetches) when their dependencies change, and that work would be too
|
1483 | /// expensive to do for every build.
|
1484 | @protected
|
1485 | @mustCallSuper
|
1486 | void didChangeDependencies() { }
|
1487 |
|
1488 | @override
|
1489 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
1490 | super.debugFillProperties(properties);
|
1491 | assert(() {
|
1492 | properties.add(EnumProperty<_StateLifecycle>('lifecycle state' , _debugLifecycleState, defaultValue: _StateLifecycle.ready));
|
1493 | return true;
|
1494 | }());
|
1495 | properties.add(ObjectFlagProperty<T>('_widget' , _widget, ifNull: 'no widget' ));
|
1496 | properties.add(ObjectFlagProperty<StatefulElement>('_element' , _element, ifNull: 'not mounted' ));
|
1497 | }
|
1498 | }
|
1499 |
|
1500 | /// A widget that has a child widget provided to it, instead of building a new
|
1501 | /// widget.
|
1502 | ///
|
1503 | /// Useful as a base class for other widgets, such as [InheritedWidget] and
|
1504 | /// [ParentDataWidget].
|
1505 | ///
|
1506 | /// See also:
|
1507 | ///
|
1508 | /// * [InheritedWidget], for widgets that introduce ambient state that can
|
1509 | /// be read by descendant widgets.
|
1510 | /// * [ParentDataWidget], for widgets that populate the
|
1511 | /// [RenderObject.parentData] slot of their child's [RenderObject] to
|
1512 | /// configure the parent widget's layout.
|
1513 | /// * [StatefulWidget] and [State], for widgets that can build differently
|
1514 | /// several times over their lifetime.
|
1515 | /// * [StatelessWidget], for widgets that always build the same way given a
|
1516 | /// particular configuration and ambient state.
|
1517 | /// * [Widget], for an overview of widgets in general.
|
1518 | abstract class ProxyWidget extends Widget {
|
1519 | /// Creates a widget that has exactly one child widget.
|
1520 | const ProxyWidget({ super.key, required this.child });
|
1521 |
|
1522 | /// The widget below this widget in the tree.
|
1523 | ///
|
1524 | /// {@template flutter.widgets.ProxyWidget.child}
|
1525 | /// This widget can only have one child. To lay out multiple children, let this
|
1526 | /// widget's child be a widget such as [Row], [Column], or [Stack], which have a
|
1527 | /// `children` property, and then provide the children to that widget.
|
1528 | /// {@endtemplate}
|
1529 | final Widget child;
|
1530 | }
|
1531 |
|
1532 | /// Base class for widgets that hook [ParentData] information to children of
|
1533 | /// [RenderObjectWidget]s.
|
1534 | ///
|
1535 | /// This can be used to provide per-child configuration for
|
1536 | /// [RenderObjectWidget]s with more than one child. For example, [Stack] uses
|
1537 | /// the [Positioned] parent data widget to position each child.
|
1538 | ///
|
1539 | /// A [ParentDataWidget] is specific to a particular kind of [ParentData]. That
|
1540 | /// class is `T`, the [ParentData] type argument.
|
1541 | ///
|
1542 | /// {@tool snippet}
|
1543 | ///
|
1544 | /// This example shows how you would build a [ParentDataWidget] to configure a
|
1545 | /// `FrogJar` widget's children by specifying a [Size] for each one.
|
1546 | ///
|
1547 | /// ```dart
|
1548 | /// class FrogSize extends ParentDataWidget<FrogJarParentData> {
|
1549 | /// const FrogSize({
|
1550 | /// super.key,
|
1551 | /// required this.size,
|
1552 | /// required super.child,
|
1553 | /// });
|
1554 | ///
|
1555 | /// final Size size;
|
1556 | ///
|
1557 | /// @override
|
1558 | /// void applyParentData(RenderObject renderObject) {
|
1559 | /// final FrogJarParentData parentData = renderObject.parentData! as FrogJarParentData;
|
1560 | /// if (parentData.size != size) {
|
1561 | /// parentData.size = size;
|
1562 | /// final RenderFrogJar targetParent = renderObject.parent! as RenderFrogJar;
|
1563 | /// targetParent.markNeedsLayout();
|
1564 | /// }
|
1565 | /// }
|
1566 | ///
|
1567 | /// @override
|
1568 | /// Type get debugTypicalAncestorWidgetClass => FrogJar;
|
1569 | /// }
|
1570 | /// ```
|
1571 | /// {@end-tool}
|
1572 | ///
|
1573 | /// See also:
|
1574 | ///
|
1575 | /// * [RenderObject], the superclass for layout algorithms.
|
1576 | /// * [RenderObject.parentData], the slot that this class configures.
|
1577 | /// * [ParentData], the superclass of the data that will be placed in
|
1578 | /// [RenderObject.parentData] slots. The `T` type parameter for
|
1579 | /// [ParentDataWidget] is a [ParentData].
|
1580 | /// * [RenderObjectWidget], the class for widgets that wrap [RenderObject]s.
|
1581 | /// * [StatefulWidget] and [State], for widgets that can build differently
|
1582 | /// several times over their lifetime.
|
1583 | abstract class ParentDataWidget<T extends ParentData> extends ProxyWidget {
|
1584 | /// Abstract const constructor. This constructor enables subclasses to provide
|
1585 | /// const constructors so that they can be used in const expressions.
|
1586 | const ParentDataWidget({ super.key, required super.child });
|
1587 |
|
1588 | @override
|
1589 | ParentDataElement<T> createElement() => ParentDataElement<T>(this);
|
1590 |
|
1591 | /// Checks if this widget can apply its parent data to the provided
|
1592 | /// `renderObject`.
|
1593 | ///
|
1594 | /// The [RenderObject.parentData] of the provided `renderObject` is
|
1595 | /// typically set up by an ancestor [RenderObjectWidget] of the type returned
|
1596 | /// by [debugTypicalAncestorWidgetClass].
|
1597 | ///
|
1598 | /// This is called just before [applyParentData] is invoked with the same
|
1599 | /// [RenderObject] provided to that method.
|
1600 | bool debugIsValidRenderObject(RenderObject renderObject) {
|
1601 | assert(T != dynamic);
|
1602 | assert(T != ParentData);
|
1603 | return renderObject.parentData is T;
|
1604 | }
|
1605 |
|
1606 | /// Describes the [RenderObjectWidget] that is typically used to set up the
|
1607 | /// [ParentData] that [applyParentData] will write to.
|
1608 | ///
|
1609 | /// This is only used in error messages to tell users what widget typically
|
1610 | /// wraps this [ParentDataWidget] through
|
1611 | /// [debugTypicalAncestorWidgetDescription].
|
1612 | ///
|
1613 | /// ## Implementations
|
1614 | ///
|
1615 | /// The returned Type should describe a subclass of `RenderObjectWidget`. If
|
1616 | /// more than one Type is supported, use
|
1617 | /// [debugTypicalAncestorWidgetDescription], which typically inserts this
|
1618 | /// value but can be overridden to describe more than one Type.
|
1619 | ///
|
1620 | /// ```dart
|
1621 | /// @override
|
1622 | /// Type get debugTypicalAncestorWidgetClass => FrogJar;
|
1623 | /// ```
|
1624 | ///
|
1625 | /// If the "typical" parent is generic (`Foo<T>`), consider specifying either
|
1626 | /// a typical type argument (e.g. `Foo<int>` if `int` is typically how the
|
1627 | /// type is specialized), or specifying the upper bound (e.g. `Foo<Object?>`).
|
1628 | Type get debugTypicalAncestorWidgetClass;
|
1629 |
|
1630 | /// Describes the [RenderObjectWidget] that is typically used to set up the
|
1631 | /// [ParentData] that [applyParentData] will write to.
|
1632 | ///
|
1633 | /// This is only used in error messages to tell users what widget typically
|
1634 | /// wraps this [ParentDataWidget].
|
1635 | ///
|
1636 | /// Returns [debugTypicalAncestorWidgetClass] by default as a String. This can
|
1637 | /// be overridden to describe more than one Type of valid parent.
|
1638 | String get debugTypicalAncestorWidgetDescription => ' $debugTypicalAncestorWidgetClass' ;
|
1639 |
|
1640 | Iterable<DiagnosticsNode> _debugDescribeIncorrectParentDataType({
|
1641 | required ParentData? parentData,
|
1642 | RenderObjectWidget? parentDataCreator,
|
1643 | DiagnosticsNode? ownershipChain,
|
1644 | }) {
|
1645 | assert(T != dynamic);
|
1646 | assert(T != ParentData);
|
1647 |
|
1648 | final String description = 'The ParentDataWidget $this wants to apply ParentData of type $T to a RenderObject' ;
|
1649 | return <DiagnosticsNode>[
|
1650 | if (parentData == null)
|
1651 | ErrorDescription(
|
1652 | ' $description, which has not been set up to receive any ParentData.' ,
|
1653 | )
|
1654 | else
|
1655 | ErrorDescription(
|
1656 | ' $description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.' ,
|
1657 | ),
|
1658 | ErrorHint(
|
1659 | 'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. '
|
1660 | 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetDescription widgets.' ,
|
1661 | ),
|
1662 | if (parentDataCreator != null)
|
1663 | ErrorHint(
|
1664 | 'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.' ,
|
1665 | ),
|
1666 | if (ownershipChain != null)
|
1667 | ErrorDescription(
|
1668 | 'The ownership chain for the RenderObject that received the incompatible parent data was:\n $ownershipChain' ,
|
1669 | ),
|
1670 | ];
|
1671 | }
|
1672 |
|
1673 | /// Write the data from this widget into the given render object's parent data.
|
1674 | ///
|
1675 | /// The framework calls this function whenever it detects that the
|
1676 | /// [RenderObject] associated with the [child] has outdated
|
1677 | /// [RenderObject.parentData]. For example, if the render object was recently
|
1678 | /// inserted into the render tree, the render object's parent data might not
|
1679 | /// match the data in this widget.
|
1680 | ///
|
1681 | /// Subclasses are expected to override this function to copy data from their
|
1682 | /// fields into the [RenderObject.parentData] field of the given render
|
1683 | /// object. The render object's parent is guaranteed to have been created by a
|
1684 | /// widget of type `T`, which usually means that this function can assume that
|
1685 | /// the render object's parent data object inherits from a particular class.
|
1686 | ///
|
1687 | /// If this function modifies data that can change the parent's layout or
|
1688 | /// painting, this function is responsible for calling
|
1689 | /// [RenderObject.markNeedsLayout] or [RenderObject.markNeedsPaint] on the
|
1690 | /// parent, as appropriate.
|
1691 | @protected
|
1692 | void applyParentData(RenderObject renderObject);
|
1693 |
|
1694 | /// Whether the [ParentDataElement.applyWidgetOutOfTurn] method is allowed
|
1695 | /// with this widget.
|
1696 | ///
|
1697 | /// This should only return true if this widget represents a [ParentData]
|
1698 | /// configuration that will have no impact on the layout or paint phase.
|
1699 | ///
|
1700 | /// See also:
|
1701 | ///
|
1702 | /// * [ParentDataElement.applyWidgetOutOfTurn], which verifies this in debug
|
1703 | /// mode.
|
1704 | @protected
|
1705 | bool debugCanApplyOutOfTurn() => false;
|
1706 | }
|
1707 |
|
1708 | /// Base class for widgets that efficiently propagate information down the tree.
|
1709 | ///
|
1710 | /// {@youtube 560 315 https://www.youtube.com/watch?v=og-vJqLzg2c}
|
1711 | ///
|
1712 | /// To obtain the nearest instance of a particular type of inherited widget from
|
1713 | /// a build context, use [BuildContext.dependOnInheritedWidgetOfExactType].
|
1714 | ///
|
1715 | /// Inherited widgets, when referenced in this way, will cause the consumer to
|
1716 | /// rebuild when the inherited widget itself changes state.
|
1717 | ///
|
1718 | /// {@youtube 560 315 https://www.youtube.com/watch?v=Zbm3hjPjQMk}
|
1719 | ///
|
1720 | /// {@tool snippet}
|
1721 | ///
|
1722 | /// The following is a skeleton of an inherited widget called `FrogColor`:
|
1723 | ///
|
1724 | /// ```dart
|
1725 | /// class FrogColor extends InheritedWidget {
|
1726 | /// const FrogColor({
|
1727 | /// super.key,
|
1728 | /// required this.color,
|
1729 | /// required super.child,
|
1730 | /// });
|
1731 | ///
|
1732 | /// final Color color;
|
1733 | ///
|
1734 | /// static FrogColor? maybeOf(BuildContext context) {
|
1735 | /// return context.dependOnInheritedWidgetOfExactType<FrogColor>();
|
1736 | /// }
|
1737 | ///
|
1738 | /// static FrogColor of(BuildContext context) {
|
1739 | /// final FrogColor? result = maybeOf(context);
|
1740 | /// assert(result != null, 'No FrogColor found in context');
|
1741 | /// return result!;
|
1742 | /// }
|
1743 | ///
|
1744 | /// @override
|
1745 | /// bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
|
1746 | /// }
|
1747 | /// ```
|
1748 | /// {@end-tool}
|
1749 | ///
|
1750 | /// ## Implementing the `of` and `maybeOf` methods
|
1751 | ///
|
1752 | /// The convention is to provide two static methods, `of` and `maybeOf`, on the
|
1753 | /// [InheritedWidget] which call
|
1754 | /// [BuildContext.dependOnInheritedWidgetOfExactType]. This allows the class to
|
1755 | /// define its own fallback logic in case there isn't a widget in scope.
|
1756 | ///
|
1757 | /// The `of` method typically returns a non-nullable instance and asserts if the
|
1758 | /// [InheritedWidget] isn't found, and the `maybeOf` method returns a nullable
|
1759 | /// instance, and returns null if the [InheritedWidget] isn't found. The `of`
|
1760 | /// method is typically implemented by calling `maybeOf` internally.
|
1761 | ///
|
1762 | /// Sometimes, the `of` and `maybeOf` methods return some data rather than the
|
1763 | /// inherited widget itself; for example, in this case it could have returned a
|
1764 | /// [Color] instead of the `FrogColor` widget.
|
1765 | ///
|
1766 | /// Occasionally, the inherited widget is an implementation detail of another
|
1767 | /// class, and is therefore private. The `of` and `maybeOf` methods in that case
|
1768 | /// are typically implemented on the public class instead. For example, [Theme]
|
1769 | /// is implemented as a [StatelessWidget] that builds a private inherited
|
1770 | /// widget; [Theme.of] looks for that private inherited widget using
|
1771 | /// [BuildContext.dependOnInheritedWidgetOfExactType] and then returns the
|
1772 | /// [ThemeData] inside it.
|
1773 | ///
|
1774 | /// ## Calling the `of` or `maybeOf` methods
|
1775 | ///
|
1776 | /// When using the `of` or `maybeOf` methods, the `context` must be a descendant
|
1777 | /// of the [InheritedWidget], meaning it must be "below" the [InheritedWidget]
|
1778 | /// in the tree.
|
1779 | ///
|
1780 | /// {@tool snippet}
|
1781 | ///
|
1782 | /// In this example, the `context` used is the one from the [Builder], which is
|
1783 | /// a child of the `FrogColor` widget, so this works.
|
1784 | ///
|
1785 | /// ```dart
|
1786 | /// // continuing from previous example...
|
1787 | /// class MyPage extends StatelessWidget {
|
1788 | /// const MyPage({super.key});
|
1789 | ///
|
1790 | /// @override
|
1791 | /// Widget build(BuildContext context) {
|
1792 | /// return Scaffold(
|
1793 | /// body: FrogColor(
|
1794 | /// color: Colors.green,
|
1795 | /// child: Builder(
|
1796 | /// builder: (BuildContext innerContext) {
|
1797 | /// return Text(
|
1798 | /// 'Hello Frog',
|
1799 | /// style: TextStyle(color: FrogColor.of(innerContext).color),
|
1800 | /// );
|
1801 | /// },
|
1802 | /// ),
|
1803 | /// ),
|
1804 | /// );
|
1805 | /// }
|
1806 | /// }
|
1807 | /// ```
|
1808 | /// {@end-tool}
|
1809 | ///
|
1810 | /// {@tool snippet}
|
1811 | ///
|
1812 | /// In this example, the `context` used is the one from the `MyOtherPage`
|
1813 | /// widget, which is a parent of the `FrogColor` widget, so this does not work,
|
1814 | /// and will assert when `FrogColor.of` is called.
|
1815 | ///
|
1816 | /// ```dart
|
1817 | /// // continuing from previous example...
|
1818 | ///
|
1819 | /// class MyOtherPage extends StatelessWidget {
|
1820 | /// const MyOtherPage({super.key});
|
1821 | ///
|
1822 | /// @override
|
1823 | /// Widget build(BuildContext context) {
|
1824 | /// return Scaffold(
|
1825 | /// body: FrogColor(
|
1826 | /// color: Colors.green,
|
1827 | /// child: Text(
|
1828 | /// 'Hello Frog',
|
1829 | /// style: TextStyle(color: FrogColor.of(context).color),
|
1830 | /// ),
|
1831 | /// ),
|
1832 | /// );
|
1833 | /// }
|
1834 | /// }
|
1835 | /// ```
|
1836 | /// {@end-tool} {@youtube 560 315 https://www.youtube.com/watch?v=1t-8rBCGBYw}
|
1837 | ///
|
1838 | /// See also:
|
1839 | ///
|
1840 | /// * [StatefulWidget] and [State], for widgets that can build differently
|
1841 | /// several times over their lifetime.
|
1842 | /// * [StatelessWidget], for widgets that always build the same way given a
|
1843 | /// particular configuration and ambient state.
|
1844 | /// * [Widget], for an overview of widgets in general.
|
1845 | /// * [InheritedNotifier], an inherited widget whose value can be a
|
1846 | /// [Listenable], and which will notify dependents whenever the value sends
|
1847 | /// notifications.
|
1848 | /// * [InheritedModel], an inherited widget that allows clients to subscribe to
|
1849 | /// changes for subparts of the value.
|
1850 | abstract class InheritedWidget extends ProxyWidget {
|
1851 | /// Abstract const constructor. This constructor enables subclasses to provide
|
1852 | /// const constructors so that they can be used in const expressions.
|
1853 | const InheritedWidget({ super.key, required super.child });
|
1854 |
|
1855 | @override
|
1856 | InheritedElement createElement() => InheritedElement(this);
|
1857 |
|
1858 | /// Whether the framework should notify widgets that inherit from this widget.
|
1859 | ///
|
1860 | /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
|
1861 | /// inherit from this widget but sometimes we do not. For example, if the data
|
1862 | /// held by this widget is the same as the data held by `oldWidget`, then we
|
1863 | /// do not need to rebuild the widgets that inherited the data held by
|
1864 | /// `oldWidget`.
|
1865 | ///
|
1866 | /// The framework distinguishes these cases by calling this function with the
|
1867 | /// widget that previously occupied this location in the tree as an argument.
|
1868 | /// The given widget is guaranteed to have the same [runtimeType] as this
|
1869 | /// object.
|
1870 | @protected
|
1871 | bool updateShouldNotify(covariant InheritedWidget oldWidget);
|
1872 | }
|
1873 |
|
1874 | /// [RenderObjectWidget]s provide the configuration for [RenderObjectElement]s,
|
1875 | /// which wrap [RenderObject]s, which provide the actual rendering of the
|
1876 | /// application.
|
1877 | ///
|
1878 | /// Usually, rather than subclassing [RenderObjectWidget] directly, render
|
1879 | /// object widgets subclass one of:
|
1880 | ///
|
1881 | /// * [LeafRenderObjectWidget], if the widget has no children.
|
1882 | /// * [SingleChildRenderObjectWidget], if the widget has exactly one child.
|
1883 | /// * [MultiChildRenderObjectWidget], if the widget takes a list of children.
|
1884 | /// * [SlottedMultiChildRenderObjectWidget], if the widget organizes its
|
1885 | /// children in different named slots.
|
1886 | ///
|
1887 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
1888 | abstract class RenderObjectWidget extends Widget {
|
1889 | /// Abstract const constructor. This constructor enables subclasses to provide
|
1890 | /// const constructors so that they can be used in const expressions.
|
1891 | const RenderObjectWidget({ super.key });
|
1892 |
|
1893 | /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass.
|
1894 | @override
|
1895 | @factory
|
1896 | RenderObjectElement createElement();
|
1897 |
|
1898 | /// Creates an instance of the [RenderObject] class that this
|
1899 | /// [RenderObjectWidget] represents, using the configuration described by this
|
1900 | /// [RenderObjectWidget].
|
1901 | ///
|
1902 | /// This method should not do anything with the children of the render object.
|
1903 | /// That should instead be handled by the method that overrides
|
1904 | /// [RenderObjectElement.mount] in the object rendered by this object's
|
1905 | /// [createElement] method. See, for example,
|
1906 | /// [SingleChildRenderObjectElement.mount].
|
1907 | @protected
|
1908 | @factory
|
1909 | RenderObject createRenderObject(BuildContext context);
|
1910 |
|
1911 | /// Copies the configuration described by this [RenderObjectWidget] to the
|
1912 | /// given [RenderObject], which will be of the same type as returned by this
|
1913 | /// object's [createRenderObject].
|
1914 | ///
|
1915 | /// This method should not do anything to update the children of the render
|
1916 | /// object. That should instead be handled by the method that overrides
|
1917 | /// [RenderObjectElement.update] in the object rendered by this object's
|
1918 | /// [createElement] method. See, for example,
|
1919 | /// [SingleChildRenderObjectElement.update].
|
1920 | @protected
|
1921 | void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
|
1922 |
|
1923 | /// A render object previously associated with this widget has been removed
|
1924 | /// from the tree. The given [RenderObject] will be of the same type as
|
1925 | /// returned by this object's [createRenderObject].
|
1926 | @protected
|
1927 | void didUnmountRenderObject(covariant RenderObject renderObject) { }
|
1928 | }
|
1929 |
|
1930 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
1931 | /// that have no children.
|
1932 | ///
|
1933 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
1934 | abstract class LeafRenderObjectWidget extends RenderObjectWidget {
|
1935 | /// Abstract const constructor. This constructor enables subclasses to provide
|
1936 | /// const constructors so that they can be used in const expressions.
|
1937 | const LeafRenderObjectWidget({ super.key });
|
1938 |
|
1939 | @override
|
1940 | LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
|
1941 | }
|
1942 |
|
1943 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
1944 | /// that have a single child slot.
|
1945 | ///
|
1946 | /// The render object assigned to this widget should make use of
|
1947 | /// [RenderObjectWithChildMixin] to implement a single-child model. The mixin
|
1948 | /// exposes a [RenderObjectWithChildMixin.child] property that allows retrieving
|
1949 | /// the render object belonging to the [child] widget.
|
1950 | ///
|
1951 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
1952 | abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
|
1953 | /// Abstract const constructor. This constructor enables subclasses to provide
|
1954 | /// const constructors so that they can be used in const expressions.
|
1955 | const SingleChildRenderObjectWidget({ super.key, this.child });
|
1956 |
|
1957 | /// The widget below this widget in the tree.
|
1958 | ///
|
1959 | /// {@macro flutter.widgets.ProxyWidget.child}
|
1960 | final Widget? child;
|
1961 |
|
1962 | @override
|
1963 | SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
|
1964 | }
|
1965 |
|
1966 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
1967 | /// that have a single list of children. (This superclass only provides the
|
1968 | /// storage for that child list, it doesn't actually provide the updating
|
1969 | /// logic.)
|
1970 | ///
|
1971 | /// Subclasses must use a [RenderObject] that mixes in
|
1972 | /// [ContainerRenderObjectMixin], which provides the necessary functionality to
|
1973 | /// visit the children of the container render object (the render object
|
1974 | /// belonging to the [children] widgets). Typically, subclasses will use a
|
1975 | /// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and
|
1976 | /// [RenderBoxContainerDefaultsMixin].
|
1977 | ///
|
1978 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
1979 | ///
|
1980 | /// See also:
|
1981 | ///
|
1982 | /// * [Stack], which uses [MultiChildRenderObjectWidget].
|
1983 | /// * [RenderStack], for an example implementation of the associated render
|
1984 | /// object.
|
1985 | /// * [SlottedMultiChildRenderObjectWidget], which configures a
|
1986 | /// [RenderObject] that instead of having a single list of children organizes
|
1987 | /// its children in named slots.
|
1988 | abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
|
1989 | /// Initializes fields for subclasses.
|
1990 | const MultiChildRenderObjectWidget({ super.key, this.children = const <Widget>[] });
|
1991 |
|
1992 | /// The widgets below this widget in the tree.
|
1993 | ///
|
1994 | /// If this list is going to be mutated, it is usually wise to put a [Key] on
|
1995 | /// each of the child widgets, so that the framework can match old
|
1996 | /// configurations to new configurations and maintain the underlying render
|
1997 | /// objects.
|
1998 | ///
|
1999 | /// Also, a [Widget] in Flutter is immutable, so directly modifying the
|
2000 | /// [children] such as `someMultiChildRenderObjectWidget.children.add(...)` or
|
2001 | /// as the example code below will result in incorrect behaviors. Whenever the
|
2002 | /// children list is modified, a new list object should be provided.
|
2003 | ///
|
2004 | /// ```dart
|
2005 | /// // This code is incorrect.
|
2006 | /// class SomeWidgetState extends State<SomeWidget> {
|
2007 | /// final List<Widget> _children = <Widget>[];
|
2008 | ///
|
2009 | /// void someHandler() {
|
2010 | /// setState(() {
|
2011 | /// _children.add(const ChildWidget());
|
2012 | /// });
|
2013 | /// }
|
2014 | ///
|
2015 | /// @override
|
2016 | /// Widget build(BuildContext context) {
|
2017 | /// // Reusing `List _children` here is problematic.
|
2018 | /// return Row(children: _children);
|
2019 | /// }
|
2020 | /// }
|
2021 | /// ```
|
2022 | ///
|
2023 | /// The following code corrects the problem mentioned above.
|
2024 | ///
|
2025 | /// ```dart
|
2026 | /// class SomeWidgetState extends State<SomeWidget> {
|
2027 | /// final List<Widget> _children = <Widget>[];
|
2028 | ///
|
2029 | /// void someHandler() {
|
2030 | /// setState(() {
|
2031 | /// // The key here allows Flutter to reuse the underlying render
|
2032 | /// // objects even if the children list is recreated.
|
2033 | /// _children.add(ChildWidget(key: UniqueKey()));
|
2034 | /// });
|
2035 | /// }
|
2036 | ///
|
2037 | /// @override
|
2038 | /// Widget build(BuildContext context) {
|
2039 | /// // Always create a new list of children as a Widget is immutable.
|
2040 | /// return Row(children: _children.toList());
|
2041 | /// }
|
2042 | /// }
|
2043 | /// ```
|
2044 | final List<Widget> children;
|
2045 |
|
2046 | @override
|
2047 | MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
|
2048 | }
|
2049 |
|
2050 |
|
2051 | // ELEMENTS
|
2052 |
|
2053 | enum _ElementLifecycle {
|
2054 | initial,
|
2055 | active,
|
2056 | inactive,
|
2057 | defunct,
|
2058 | }
|
2059 |
|
2060 | class _InactiveElements {
|
2061 | bool _locked = false;
|
2062 | final Set<Element> _elements = HashSet<Element>();
|
2063 |
|
2064 | void _unmount(Element element) {
|
2065 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
2066 | assert(() {
|
2067 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
2068 | if (element.widget.key is GlobalKey) {
|
2069 | debugPrint('Discarding $element from inactive elements list.' );
|
2070 | }
|
2071 | }
|
2072 | return true;
|
2073 | }());
|
2074 | element.visitChildren((Element child) {
|
2075 | assert(child._parent == element);
|
2076 | _unmount(child);
|
2077 | });
|
2078 | element.unmount();
|
2079 | assert(element._lifecycleState == _ElementLifecycle.defunct);
|
2080 | }
|
2081 |
|
2082 | void _unmountAll() {
|
2083 | _locked = true;
|
2084 | final List<Element> elements = _elements.toList()..sort(Element._sort);
|
2085 | _elements.clear();
|
2086 | try {
|
2087 | elements.reversed.forEach(_unmount);
|
2088 | } finally {
|
2089 | assert(_elements.isEmpty);
|
2090 | _locked = false;
|
2091 | }
|
2092 | }
|
2093 |
|
2094 | static void _deactivateRecursively(Element element) {
|
2095 | assert(element._lifecycleState == _ElementLifecycle.active);
|
2096 | element.deactivate();
|
2097 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
2098 | element.visitChildren(_deactivateRecursively);
|
2099 | assert(() {
|
2100 | element.debugDeactivated();
|
2101 | return true;
|
2102 | }());
|
2103 | }
|
2104 |
|
2105 | void add(Element element) {
|
2106 | assert(!_locked);
|
2107 | assert(!_elements.contains(element));
|
2108 | assert(element._parent == null);
|
2109 | if (element._lifecycleState == _ElementLifecycle.active) {
|
2110 | _deactivateRecursively(element);
|
2111 | }
|
2112 | _elements.add(element);
|
2113 | }
|
2114 |
|
2115 | void remove(Element element) {
|
2116 | assert(!_locked);
|
2117 | assert(_elements.contains(element));
|
2118 | assert(element._parent == null);
|
2119 | _elements.remove(element);
|
2120 | assert(element._lifecycleState != _ElementLifecycle.active);
|
2121 | }
|
2122 |
|
2123 | bool debugContains(Element element) {
|
2124 | late bool result;
|
2125 | assert(() {
|
2126 | result = _elements.contains(element);
|
2127 | return true;
|
2128 | }());
|
2129 | return result;
|
2130 | }
|
2131 | }
|
2132 |
|
2133 | /// Signature for the callback to [BuildContext.visitChildElements].
|
2134 | ///
|
2135 | /// The argument is the child being visited.
|
2136 | ///
|
2137 | /// It is safe to call `element.visitChildElements` reentrantly within
|
2138 | /// this callback.
|
2139 | typedef ElementVisitor = void Function(Element element);
|
2140 |
|
2141 | /// Signature for the callback to [BuildContext.visitAncestorElements].
|
2142 | ///
|
2143 | /// The argument is the ancestor being visited.
|
2144 | ///
|
2145 | /// Return false to stop the walk.
|
2146 | typedef ConditionalElementVisitor = bool Function(Element element);
|
2147 |
|
2148 | /// A handle to the location of a widget in the widget tree.
|
2149 | ///
|
2150 | /// This class presents a set of methods that can be used from
|
2151 | /// [StatelessWidget.build] methods and from methods on [State] objects.
|
2152 | ///
|
2153 | /// [BuildContext] objects are passed to [WidgetBuilder] functions (such as
|
2154 | /// [StatelessWidget.build]), and are available from the [State.context] member.
|
2155 | /// Some static functions (e.g. [showDialog], [Theme.of], and so forth) also
|
2156 | /// take build contexts so that they can act on behalf of the calling widget, or
|
2157 | /// obtain data specifically for the given context.
|
2158 | ///
|
2159 | /// Each widget has its own [BuildContext], which becomes the parent of the
|
2160 | /// widget returned by the [StatelessWidget.build] or [State.build] function.
|
2161 | /// (And similarly, the parent of any children for [RenderObjectWidget]s.)
|
2162 | ///
|
2163 | /// In particular, this means that within a build method, the build context of
|
2164 | /// the widget of the build method is not the same as the build context of the
|
2165 | /// widgets returned by that build method. This can lead to some tricky cases.
|
2166 | /// For example, [Theme.of(context)] looks for the nearest enclosing [Theme] of
|
2167 | /// the given build context. If a build method for a widget Q includes a [Theme]
|
2168 | /// within its returned widget tree, and attempts to use [Theme.of] passing its
|
2169 | /// own context, the build method for Q will not find that [Theme] object. It
|
2170 | /// will instead find whatever [Theme] was an ancestor to the widget Q. If the
|
2171 | /// build context for a subpart of the returned tree is needed, a [Builder]
|
2172 | /// widget can be used: the build context passed to the [Builder.builder]
|
2173 | /// callback will be that of the [Builder] itself.
|
2174 | ///
|
2175 | /// For example, in the following snippet, the [ScaffoldState.showBottomSheet]
|
2176 | /// method is called on the [Scaffold] widget that the build method itself
|
2177 | /// creates. If a [Builder] had not been used, and instead the `context`
|
2178 | /// argument of the build method itself had been used, no [Scaffold] would have
|
2179 | /// been found, and the [Scaffold.of] function would have returned null.
|
2180 | ///
|
2181 | /// ```dart
|
2182 | /// @override
|
2183 | /// Widget build(BuildContext context) {
|
2184 | /// // here, Scaffold.of(context) returns null
|
2185 | /// return Scaffold(
|
2186 | /// appBar: AppBar(title: const Text('Demo')),
|
2187 | /// body: Builder(
|
2188 | /// builder: (BuildContext context) {
|
2189 | /// return TextButton(
|
2190 | /// child: const Text('BUTTON'),
|
2191 | /// onPressed: () {
|
2192 | /// Scaffold.of(context).showBottomSheet(
|
2193 | /// (BuildContext context) {
|
2194 | /// return Container(
|
2195 | /// alignment: Alignment.center,
|
2196 | /// height: 200,
|
2197 | /// color: Colors.amber,
|
2198 | /// child: Center(
|
2199 | /// child: Column(
|
2200 | /// mainAxisSize: MainAxisSize.min,
|
2201 | /// children: <Widget>[
|
2202 | /// const Text('BottomSheet'),
|
2203 | /// ElevatedButton(
|
2204 | /// child: const Text('Close BottomSheet'),
|
2205 | /// onPressed: () {
|
2206 | /// Navigator.pop(context);
|
2207 | /// },
|
2208 | /// )
|
2209 | /// ],
|
2210 | /// ),
|
2211 | /// ),
|
2212 | /// );
|
2213 | /// },
|
2214 | /// );
|
2215 | /// },
|
2216 | /// );
|
2217 | /// },
|
2218 | /// )
|
2219 | /// );
|
2220 | /// }
|
2221 | /// ```
|
2222 | ///
|
2223 | /// The [BuildContext] for a particular widget can change location over time as
|
2224 | /// the widget is moved around the tree. Because of this, values returned from
|
2225 | /// the methods on this class should not be cached beyond the execution of a
|
2226 | /// single synchronous function.
|
2227 | ///
|
2228 | /// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g}
|
2229 | ///
|
2230 | /// Avoid storing instances of [BuildContext]s because they may become invalid
|
2231 | /// if the widget they are associated with is unmounted from the widget tree.
|
2232 | /// {@template flutter.widgets.BuildContext.asynchronous_gap}
|
2233 | /// If a [BuildContext] is used across an asynchronous gap (i.e. after performing
|
2234 | /// an asynchronous operation), consider checking [mounted] to determine whether
|
2235 | /// the context is still valid before interacting with it:
|
2236 | ///
|
2237 | /// ```dart
|
2238 | /// @override
|
2239 | /// Widget build(BuildContext context) {
|
2240 | /// return OutlinedButton(
|
2241 | /// onPressed: () async {
|
2242 | /// await Future<void>.delayed(const Duration(seconds: 1));
|
2243 | /// if (context.mounted) {
|
2244 | /// Navigator.of(context).pop();
|
2245 | /// }
|
2246 | /// },
|
2247 | /// child: const Text('Delayed pop'),
|
2248 | /// );
|
2249 | /// }
|
2250 | /// ```
|
2251 | /// {@endtemplate}
|
2252 | ///
|
2253 | /// [BuildContext] objects are actually [Element] objects. The [BuildContext]
|
2254 | /// interface is used to discourage direct manipulation of [Element] objects.
|
2255 | abstract class BuildContext {
|
2256 | /// The current configuration of the [Element] that is this [BuildContext].
|
2257 | Widget get widget;
|
2258 |
|
2259 | /// The [BuildOwner] for this context. The [BuildOwner] is in charge of
|
2260 | /// managing the rendering pipeline for this context.
|
2261 | BuildOwner? get owner;
|
2262 |
|
2263 | /// Whether the [Widget] this context is associated with is currently
|
2264 | /// mounted in the widget tree.
|
2265 | ///
|
2266 | /// Accessing the properties of the [BuildContext] or calling any methods on
|
2267 | /// it is only valid while mounted is true. If mounted is false, assertions
|
2268 | /// will trigger.
|
2269 | ///
|
2270 | /// Once unmounted, a given [BuildContext] will never become mounted again.
|
2271 | ///
|
2272 | /// {@macro flutter.widgets.BuildContext.asynchronous_gap}
|
2273 | bool get mounted;
|
2274 |
|
2275 | /// Whether the [widget] is currently updating the widget or render tree.
|
2276 | ///
|
2277 | /// For [StatefulWidget]s and [StatelessWidget]s this flag is true while
|
2278 | /// their respective build methods are executing.
|
2279 | /// [RenderObjectWidget]s set this to true while creating or configuring their
|
2280 | /// associated [RenderObject]s.
|
2281 | /// Other [Widget] types may set this to true for conceptually similar phases
|
2282 | /// of their lifecycle.
|
2283 | ///
|
2284 | /// When this is true, it is safe for [widget] to establish a dependency to an
|
2285 | /// [InheritedWidget] by calling [dependOnInheritedElement] or
|
2286 | /// [dependOnInheritedWidgetOfExactType].
|
2287 | ///
|
2288 | /// Accessing this flag in release mode is not valid.
|
2289 | bool get debugDoingBuild;
|
2290 |
|
2291 | /// The current [RenderObject] for the widget. If the widget is a
|
2292 | /// [RenderObjectWidget], this is the render object that the widget created
|
2293 | /// for itself. Otherwise, it is the render object of the first descendant
|
2294 | /// [RenderObjectWidget].
|
2295 | ///
|
2296 | /// This method will only return a valid result after the build phase is
|
2297 | /// complete. It is therefore not valid to call this from a build method.
|
2298 | /// It should only be called from interaction event handlers (e.g.
|
2299 | /// gesture callbacks) or layout or paint callbacks. It is also not valid to
|
2300 | /// call if [State.mounted] returns false.
|
2301 | ///
|
2302 | /// If the render object is a [RenderBox], which is the common case, then the
|
2303 | /// size of the render object can be obtained from the [size] getter. This is
|
2304 | /// only valid after the layout phase, and should therefore only be examined
|
2305 | /// from paint callbacks or interaction event handlers (e.g. gesture
|
2306 | /// callbacks).
|
2307 | ///
|
2308 | /// For details on the different phases of a frame, see the discussion at
|
2309 | /// [WidgetsBinding.drawFrame].
|
2310 | ///
|
2311 | /// Calling this method is theoretically relatively expensive (O(N) in the
|
2312 | /// depth of the tree), but in practice is usually cheap because the tree
|
2313 | /// usually has many render objects and therefore the distance to the nearest
|
2314 | /// render object is usually short.
|
2315 | RenderObject? findRenderObject();
|
2316 |
|
2317 | /// The size of the [RenderBox] returned by [findRenderObject].
|
2318 | ///
|
2319 | /// This getter will only return a valid result after the layout phase is
|
2320 | /// complete. It is therefore not valid to call this from a build method.
|
2321 | /// It should only be called from paint callbacks or interaction event
|
2322 | /// handlers (e.g. gesture callbacks).
|
2323 | ///
|
2324 | /// For details on the different phases of a frame, see the discussion at
|
2325 | /// [WidgetsBinding.drawFrame].
|
2326 | ///
|
2327 | /// This getter will only return a valid result if [findRenderObject] actually
|
2328 | /// returns a [RenderBox]. If [findRenderObject] returns a render object that
|
2329 | /// is not a subtype of [RenderBox] (e.g., [RenderView]), this getter will
|
2330 | /// throw an exception in debug mode and will return null in release mode.
|
2331 | ///
|
2332 | /// Calling this getter is theoretically relatively expensive (O(N) in the
|
2333 | /// depth of the tree), but in practice is usually cheap because the tree
|
2334 | /// usually has many render objects and therefore the distance to the nearest
|
2335 | /// render object is usually short.
|
2336 | Size? get size;
|
2337 |
|
2338 | /// Registers this build context with [ancestor] such that when
|
2339 | /// [ancestor]'s widget changes this build context is rebuilt.
|
2340 | ///
|
2341 | /// Returns `ancestor.widget`.
|
2342 | ///
|
2343 | /// This method is rarely called directly. Most applications should use
|
2344 | /// [dependOnInheritedWidgetOfExactType], which calls this method after finding
|
2345 | /// the appropriate [InheritedElement] ancestor.
|
2346 | ///
|
2347 | /// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can
|
2348 | /// be called apply to this method as well.
|
2349 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect });
|
2350 |
|
2351 | /// Returns the nearest widget of the given type `T` and creates a dependency
|
2352 | /// on it, or null if no appropriate widget is found.
|
2353 | ///
|
2354 | /// The widget found will be a concrete [InheritedWidget] subclass, and
|
2355 | /// calling [dependOnInheritedWidgetOfExactType] registers this build context
|
2356 | /// with the returned widget. When that widget changes (or a new widget of
|
2357 | /// that type is introduced, or the widget goes away), this build context is
|
2358 | /// rebuilt so that it can obtain new values from that widget.
|
2359 | ///
|
2360 | /// {@template flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType}
|
2361 | /// This is typically called implicitly from `of()` static methods, e.g.
|
2362 | /// [Theme.of].
|
2363 | ///
|
2364 | /// This method should not be called from widget constructors or from
|
2365 | /// [State.initState] methods, because those methods would not get called
|
2366 | /// again if the inherited value were to change. To ensure that the widget
|
2367 | /// correctly updates itself when the inherited value changes, only call this
|
2368 | /// (directly or indirectly) from build methods, layout and paint callbacks,
|
2369 | /// or from [State.didChangeDependencies] (which is called immediately after
|
2370 | /// [State.initState]).
|
2371 | ///
|
2372 | /// This method should not be called from [State.dispose] because the element
|
2373 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
2374 | /// method, save a reference to the ancestor in [State.didChangeDependencies].
|
2375 | /// It is safe to use this method from [State.deactivate], which is called
|
2376 | /// whenever the widget is removed from the tree.
|
2377 | ///
|
2378 | /// It is also possible to call this method from interaction event handlers
|
2379 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as
|
2380 | /// that value is not cached and/or reused later.
|
2381 | ///
|
2382 | /// Calling this method is O(1) with a small constant factor, but will lead to
|
2383 | /// the widget being rebuilt more often.
|
2384 | ///
|
2385 | /// Once a widget registers a dependency on a particular type by calling this
|
2386 | /// method, it will be rebuilt, and [State.didChangeDependencies] will be
|
2387 | /// called, whenever changes occur relating to that widget until the next time
|
2388 | /// the widget or one of its ancestors is moved (for example, because an
|
2389 | /// ancestor is added or removed).
|
2390 | ///
|
2391 | /// The [aspect] parameter is only used when `T` is an
|
2392 | /// [InheritedWidget] subclasses that supports partial updates, like
|
2393 | /// [InheritedModel]. It specifies what "aspect" of the inherited
|
2394 | /// widget this context depends on.
|
2395 | /// {@endtemplate}
|
2396 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
|
2397 |
|
2398 | /// Returns the nearest widget of the given [InheritedWidget] subclass `T` or
|
2399 | /// null if an appropriate ancestor is not found.
|
2400 | ///
|
2401 | /// This method does not introduce a dependency the way that the more typical
|
2402 | /// [dependOnInheritedWidgetOfExactType] does, so this context will not be
|
2403 | /// rebuilt if the [InheritedWidget] changes. This function is meant for those
|
2404 | /// uncommon use cases where a dependency is undesirable.
|
2405 | ///
|
2406 | /// This method should not be called from [State.dispose] because the element
|
2407 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
2408 | /// method, save a reference to the ancestor in [State.didChangeDependencies].
|
2409 | /// It is safe to use this method from [State.deactivate], which is called
|
2410 | /// whenever the widget is removed from the tree.
|
2411 | ///
|
2412 | /// It is also possible to call this method from interaction event handlers
|
2413 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as
|
2414 | /// that value is not cached and/or reused later.
|
2415 | ///
|
2416 | /// Calling this method is O(1) with a small constant factor.
|
2417 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>();
|
2418 |
|
2419 | /// Obtains the element corresponding to the nearest widget of the given type `T`,
|
2420 | /// which must be the type of a concrete [InheritedWidget] subclass.
|
2421 | ///
|
2422 | /// Returns null if no such element is found.
|
2423 | ///
|
2424 | /// {@template flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType}
|
2425 | /// Calling this method is O(1) with a small constant factor.
|
2426 | ///
|
2427 | /// This method does not establish a relationship with the target in the way
|
2428 | /// that [dependOnInheritedWidgetOfExactType] does.
|
2429 | ///
|
2430 | /// This method should not be called from [State.dispose] because the element
|
2431 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
2432 | /// method, save a reference to the ancestor by calling
|
2433 | /// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is
|
2434 | /// safe to use this method from [State.deactivate], which is called whenever
|
2435 | /// the widget is removed from the tree.
|
2436 | /// {@endtemplate}
|
2437 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
|
2438 |
|
2439 | /// Returns the nearest ancestor widget of the given type `T`, which must be the
|
2440 | /// type of a concrete [Widget] subclass.
|
2441 | ///
|
2442 | /// {@template flutter.widgets.BuildContext.findAncestorWidgetOfExactType}
|
2443 | /// In general, [dependOnInheritedWidgetOfExactType] is more useful, since
|
2444 | /// inherited widgets will trigger consumers to rebuild when they change. This
|
2445 | /// method is appropriate when used in interaction event handlers (e.g.
|
2446 | /// gesture callbacks) or for performing one-off tasks such as asserting that
|
2447 | /// you have or don't have a widget of a specific type as an ancestor. The
|
2448 | /// return value of a Widget's build method should not depend on the value
|
2449 | /// returned by this method, because the build context will not rebuild if the
|
2450 | /// return value of this method changes. This could lead to a situation where
|
2451 | /// data used in the build method changes, but the widget is not rebuilt.
|
2452 | ///
|
2453 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
2454 | /// tree). Only call this method if the distance from this widget to the
|
2455 | /// desired ancestor is known to be small and bounded.
|
2456 | ///
|
2457 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
2458 | /// because the widget tree is no longer stable at that time. To refer to
|
2459 | /// an ancestor from one of those methods, save a reference to the ancestor
|
2460 | /// by calling [findAncestorWidgetOfExactType] in [State.didChangeDependencies].
|
2461 | ///
|
2462 | /// Returns null if a widget of the requested type does not appear in the
|
2463 | /// ancestors of this context.
|
2464 | /// {@endtemplate}
|
2465 | T? findAncestorWidgetOfExactType<T extends Widget>();
|
2466 |
|
2467 | /// Returns the [State] object of the nearest ancestor [StatefulWidget] widget
|
2468 | /// that is an instance of the given type `T`.
|
2469 | ///
|
2470 | /// {@template flutter.widgets.BuildContext.findAncestorStateOfType}
|
2471 | /// This should not be used from build methods, because the build context will
|
2472 | /// not be rebuilt if the value that would be returned by this method changes.
|
2473 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
2474 | /// cases. This method is useful for changing the state of an ancestor widget in
|
2475 | /// a one-off manner, for example, to cause an ancestor scrolling list to
|
2476 | /// scroll this build context's widget into view, or to move the focus in
|
2477 | /// response to user interaction.
|
2478 | ///
|
2479 | /// In general, though, consider using a callback that triggers a stateful
|
2480 | /// change in the ancestor rather than using the imperative style implied by
|
2481 | /// this method. This will usually lead to more maintainable and reusable code
|
2482 | /// since it decouples widgets from each other.
|
2483 | ///
|
2484 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
2485 | /// tree). Only call this method if the distance from this widget to the
|
2486 | /// desired ancestor is known to be small and bounded.
|
2487 | ///
|
2488 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
2489 | /// because the widget tree is no longer stable at that time. To refer to
|
2490 | /// an ancestor from one of those methods, save a reference to the ancestor
|
2491 | /// by calling [findAncestorStateOfType] in [State.didChangeDependencies].
|
2492 | /// {@endtemplate}
|
2493 | ///
|
2494 | /// {@tool snippet}
|
2495 | ///
|
2496 | /// ```dart
|
2497 | /// ScrollableState? scrollable = context.findAncestorStateOfType<ScrollableState>();
|
2498 | /// ```
|
2499 | /// {@end-tool}
|
2500 | T? findAncestorStateOfType<T extends State>();
|
2501 |
|
2502 | /// Returns the [State] object of the furthest ancestor [StatefulWidget] widget
|
2503 | /// that is an instance of the given type `T`.
|
2504 | ///
|
2505 | /// {@template flutter.widgets.BuildContext.findRootAncestorStateOfType}
|
2506 | /// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent
|
2507 | /// ancestors until there are none of the type instance of `T` remaining.
|
2508 | /// Then returns the last one found.
|
2509 | ///
|
2510 | /// This operation is O(N) as well though N is the entire widget tree rather than
|
2511 | /// a subtree.
|
2512 | /// {@endtemplate}
|
2513 | T? findRootAncestorStateOfType<T extends State>();
|
2514 |
|
2515 | /// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget
|
2516 | /// that is an instance of the given type `T`.
|
2517 | ///
|
2518 | /// {@template flutter.widgets.BuildContext.findAncestorRenderObjectOfType}
|
2519 | /// This should not be used from build methods, because the build context will
|
2520 | /// not be rebuilt if the value that would be returned by this method changes.
|
2521 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
2522 | /// cases. This method is useful only in esoteric cases where a widget needs
|
2523 | /// to cause an ancestor to change its layout or paint behavior. For example,
|
2524 | /// it is used by [Material] so that [InkWell] widgets can trigger the ink
|
2525 | /// splash on the [Material]'s actual render object.
|
2526 | ///
|
2527 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
2528 | /// tree). Only call this method if the distance from this widget to the
|
2529 | /// desired ancestor is known to be small and bounded.
|
2530 | ///
|
2531 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
2532 | /// because the widget tree is no longer stable at that time. To refer to
|
2533 | /// an ancestor from one of those methods, save a reference to the ancestor
|
2534 | /// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies].
|
2535 | /// {@endtemplate}
|
2536 | T? findAncestorRenderObjectOfType<T extends RenderObject>();
|
2537 |
|
2538 | /// Walks the ancestor chain, starting with the parent of this build context's
|
2539 | /// widget, invoking the argument for each ancestor.
|
2540 | ///
|
2541 | /// {@template flutter.widgets.BuildContext.visitAncestorElements}
|
2542 | /// The callback is given a reference to the ancestor widget's corresponding
|
2543 | /// [Element] object. The walk stops when it reaches the root widget or when
|
2544 | /// the callback returns false. The callback must not return null.
|
2545 | ///
|
2546 | /// This is useful for inspecting the widget tree.
|
2547 | ///
|
2548 | /// Calling this method is relatively expensive (O(N) in the depth of the tree).
|
2549 | ///
|
2550 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
2551 | /// because the element tree is no longer stable at that time. To refer to
|
2552 | /// an ancestor from one of those methods, save a reference to the ancestor
|
2553 | /// by calling [visitAncestorElements] in [State.didChangeDependencies].
|
2554 | /// {@endtemplate}
|
2555 | void visitAncestorElements(ConditionalElementVisitor visitor);
|
2556 |
|
2557 | /// Walks the children of this widget.
|
2558 | ///
|
2559 | /// {@template flutter.widgets.BuildContext.visitChildElements}
|
2560 | /// This is useful for applying changes to children after they are built
|
2561 | /// without waiting for the next frame, especially if the children are known,
|
2562 | /// and especially if there is exactly one child (as is always the case for
|
2563 | /// [StatefulWidget]s or [StatelessWidget]s).
|
2564 | ///
|
2565 | /// Calling this method is very cheap for build contexts that correspond to
|
2566 | /// [StatefulWidget]s or [StatelessWidget]s (O(1), since there's only one
|
2567 | /// child).
|
2568 | ///
|
2569 | /// Calling this method is potentially expensive for build contexts that
|
2570 | /// correspond to [RenderObjectWidget]s (O(N) in the number of children).
|
2571 | ///
|
2572 | /// Calling this method recursively is extremely expensive (O(N) in the number
|
2573 | /// of descendants), and should be avoided if possible. Generally it is
|
2574 | /// significantly cheaper to use an [InheritedWidget] and have the descendants
|
2575 | /// pull data down, than it is to use [visitChildElements] recursively to push
|
2576 | /// data down to them.
|
2577 | /// {@endtemplate}
|
2578 | void visitChildElements(ElementVisitor visitor);
|
2579 |
|
2580 | /// Start bubbling this notification at the given build context.
|
2581 | ///
|
2582 | /// The notification will be delivered to any [NotificationListener] widgets
|
2583 | /// with the appropriate type parameters that are ancestors of the given
|
2584 | /// [BuildContext].
|
2585 | void dispatchNotification(Notification notification);
|
2586 |
|
2587 | /// Returns a description of the [Element] associated with the current build context.
|
2588 | ///
|
2589 | /// The `name` is typically something like "The element being rebuilt was".
|
2590 | ///
|
2591 | /// See also:
|
2592 | ///
|
2593 | /// * [Element.describeElements], which can be used to describe a list of elements.
|
2594 | DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});
|
2595 |
|
2596 | /// Returns a description of the [Widget] associated with the current build context.
|
2597 | ///
|
2598 | /// The `name` is typically something like "The widget being rebuilt was".
|
2599 | DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});
|
2600 |
|
2601 | /// Adds a description of a specific type of widget missing from the current
|
2602 | /// build context's ancestry tree.
|
2603 | ///
|
2604 | /// You can find an example of using this method in [debugCheckHasMaterial].
|
2605 | List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType });
|
2606 |
|
2607 | /// Adds a description of the ownership chain from a specific [Element]
|
2608 | /// to the error report.
|
2609 | ///
|
2610 | /// The ownership chain is useful for debugging the source of an element.
|
2611 | DiagnosticsNode describeOwnershipChain(String name);
|
2612 | }
|
2613 |
|
2614 | /// A class that determines the scope of a [BuildOwner.buildScope] operation.
|
2615 | ///
|
2616 | /// The [BuildOwner.buildScope] method rebuilds all dirty [Element]s who share
|
2617 | /// the same [Element.buildScope] as its `context` argument, and skips those
|
2618 | /// with a different [Element.buildScope].
|
2619 | ///
|
2620 | /// [Element]s by default have the same `buildScope` as their parents. Special
|
2621 | /// [Element]s may override [Element.buildScope] to create an isolated build scope
|
2622 | /// for its descendants. The [LayoutBuilder] widget, for example, establishes its
|
2623 | /// own [BuildScope] such that no descendant [Element]s may rebuild prematurely
|
2624 | /// until the incoming constraints are known.
|
2625 | final class BuildScope {
|
2626 | /// Creates a [BuildScope] with an optional [scheduleRebuild] callback.
|
2627 | BuildScope({ this.scheduleRebuild });
|
2628 |
|
2629 | // Whether `scheduleRebuild` is called.
|
2630 | bool _buildScheduled = false;
|
2631 | // Whether [BuildOwner.buildScope] is actively running in this [BuildScope].
|
2632 | bool _building = false;
|
2633 |
|
2634 | /// An optional [VoidCallback] that will be called when [Element]s in this
|
2635 | /// [BuildScope] are marked as dirty for the first time.
|
2636 | ///
|
2637 | /// This callback usually signifies that the [BuildOwner.buildScope] method
|
2638 | /// must be called at a later time in this frame to rebuild dirty elements in
|
2639 | /// this [BuildScope]. It will **not** be called if this scope is actively being
|
2640 | /// built by [BuildOwner.buildScope], since the [BuildScope] will be clean when
|
2641 | /// [BuildOwner.buildScope] returns.
|
2642 | final VoidCallback? scheduleRebuild;
|
2643 |
|
2644 | /// Whether [_dirtyElements] need to be sorted again as a result of more
|
2645 | /// elements becoming dirty during the build.
|
2646 | ///
|
2647 | /// This is necessary to preserve the sort order defined by [Element._sort].
|
2648 | ///
|
2649 | /// This field is set to null when [BuildOwner.buildScope] is not actively
|
2650 | /// rebuilding the widget tree.
|
2651 | bool? _dirtyElementsNeedsResorting;
|
2652 | final List<Element> _dirtyElements = <Element>[];
|
2653 |
|
2654 | @pragma('dart2js:tryInline' )
|
2655 | @pragma('vm:prefer-inline' )
|
2656 | @pragma('wasm:prefer-inline' )
|
2657 | void _scheduleBuildFor(Element element) {
|
2658 | assert(identical(element.buildScope, this));
|
2659 | if (!element._inDirtyList) {
|
2660 | _dirtyElements.add(element);
|
2661 | element._inDirtyList = true;
|
2662 | }
|
2663 | if (!_buildScheduled && !_building) {
|
2664 | _buildScheduled = true;
|
2665 | scheduleRebuild?.call();
|
2666 | }
|
2667 | if (_dirtyElementsNeedsResorting != null) {
|
2668 | _dirtyElementsNeedsResorting = true;
|
2669 | }
|
2670 | }
|
2671 |
|
2672 | @pragma('dart2js:tryInline' )
|
2673 | @pragma('vm:prefer-inline' )
|
2674 | @pragma('wasm:prefer-inline' )
|
2675 | @pragma('vm:notify-debugger-on-exception' )
|
2676 | void _tryRebuild(Element element) {
|
2677 | assert(element._inDirtyList);
|
2678 | assert(identical(element.buildScope, this));
|
2679 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(element.widget);
|
2680 | if (isTimelineTracked) {
|
2681 | Map<String, String>? debugTimelineArguments;
|
2682 | assert(() {
|
2683 | if (kDebugMode && debugEnhanceBuildTimelineArguments) {
|
2684 | debugTimelineArguments = element.widget.toDiagnosticsNode().toTimelineArguments();
|
2685 | }
|
2686 | return true;
|
2687 | }());
|
2688 | FlutterTimeline.startSync(
|
2689 | ' ${element.widget.runtimeType}' ,
|
2690 | arguments: debugTimelineArguments,
|
2691 | );
|
2692 | }
|
2693 | try {
|
2694 | element.rebuild();
|
2695 | } catch (e, stack) {
|
2696 | _reportException(
|
2697 | ErrorDescription('while rebuilding dirty elements' ),
|
2698 | e,
|
2699 | stack,
|
2700 | informationCollector: () => <DiagnosticsNode>[
|
2701 | if (kDebugMode)
|
2702 | DiagnosticsDebugCreator(DebugCreator(element)),
|
2703 | element.describeElement('The element being rebuilt at the time was' )
|
2704 | ],
|
2705 | );
|
2706 | }
|
2707 | if (isTimelineTracked) {
|
2708 | FlutterTimeline.finishSync();
|
2709 | }
|
2710 | }
|
2711 |
|
2712 | bool _debugAssertElementInScope(Element element, Element debugBuildRoot) {
|
2713 | final bool isInScope = element._debugIsDescsendantOf(debugBuildRoot)
|
2714 | || !element.debugIsActive;
|
2715 | if (isInScope) {
|
2716 | return true;
|
2717 | }
|
2718 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
2719 | ErrorSummary('Tried to build dirty widget in the wrong build scope.' ),
|
2720 | ErrorDescription(
|
2721 | 'A widget which was marked as dirty and is still active was scheduled to be built, '
|
2722 | 'but the current build scope unexpectedly does not contain that widget.' ,
|
2723 | ),
|
2724 | ErrorHint(
|
2725 | 'Sometimes this is detected when an element is removed from the widget tree, but the '
|
2726 | 'element somehow did not get marked as inactive. In that case, it might be caused by '
|
2727 | 'an ancestor element failing to implement visitChildren correctly, thus preventing '
|
2728 | 'some or all of its descendants from being correctly deactivated.' ,
|
2729 | ),
|
2730 | DiagnosticsProperty<Element>(
|
2731 | 'The root of the build scope was' ,
|
2732 | debugBuildRoot,
|
2733 | style: DiagnosticsTreeStyle.errorProperty,
|
2734 | ),
|
2735 | DiagnosticsProperty<Element>(
|
2736 | 'The offending element (which does not appear to be a descendant of the root of the build scope) was' ,
|
2737 | element,
|
2738 | style: DiagnosticsTreeStyle.errorProperty,
|
2739 | ),
|
2740 | ]);
|
2741 | }
|
2742 |
|
2743 | @pragma('vm:notify-debugger-on-exception' )
|
2744 | void _flushDirtyElements({ required Element debugBuildRoot }) {
|
2745 | assert(_dirtyElementsNeedsResorting == null, '_flushDirtyElements must be non-reentrant' );
|
2746 | _dirtyElements.sort(Element._sort);
|
2747 | _dirtyElementsNeedsResorting = false;
|
2748 | try {
|
2749 | for (int index = 0; index < _dirtyElements.length; index = _dirtyElementIndexAfter(index)) {
|
2750 | final Element element = _dirtyElements[index];
|
2751 | if (identical(element.buildScope, this)) {
|
2752 | assert(_debugAssertElementInScope(element, debugBuildRoot));
|
2753 | _tryRebuild(element);
|
2754 | }
|
2755 | }
|
2756 | assert(() {
|
2757 | final Iterable<Element> missedElements = _dirtyElements.where((Element element) => element.debugIsActive && element.dirty && identical(element.buildScope, this));
|
2758 | if (missedElements.isNotEmpty) {
|
2759 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
2760 | ErrorSummary('buildScope missed some dirty elements.' ),
|
2761 | ErrorHint('This probably indicates that the dirty list should have been resorted but was not.' ),
|
2762 | DiagnosticsProperty<Element>(
|
2763 | 'The context argument of the buildScope call was' ,
|
2764 | debugBuildRoot,
|
2765 | style: DiagnosticsTreeStyle.errorProperty,
|
2766 | ),
|
2767 | Element.describeElements('The list of missed elements at the end of the buildScope call was' , missedElements),
|
2768 | ]);
|
2769 | }
|
2770 | return true;
|
2771 | }());
|
2772 | } finally {
|
2773 | for (final Element element in _dirtyElements) {
|
2774 | if (identical(element.buildScope, this)) {
|
2775 | element._inDirtyList = false;
|
2776 | }
|
2777 | }
|
2778 | _dirtyElements.clear();
|
2779 | _dirtyElementsNeedsResorting = null;
|
2780 | _buildScheduled = false;
|
2781 | }
|
2782 | }
|
2783 |
|
2784 | @pragma('dart2js:tryInline' )
|
2785 | @pragma('vm:prefer-inline' )
|
2786 | @pragma('wasm:prefer-inline' )
|
2787 | int _dirtyElementIndexAfter(int index) {
|
2788 | if (!_dirtyElementsNeedsResorting!) {
|
2789 | return index + 1;
|
2790 | }
|
2791 | index += 1;
|
2792 | _dirtyElements.sort(Element._sort);
|
2793 | _dirtyElementsNeedsResorting = false;
|
2794 | while (index > 0 && _dirtyElements[index - 1].dirty) {
|
2795 | // It is possible for previously dirty but inactive widgets to move right in the list.
|
2796 | // We therefore have to move the index left in the list to account for this.
|
2797 | // We don't know how many could have moved. However, we do know that the only possible
|
2798 | // change to the list is that nodes that were previously to the left of the index have
|
2799 | // now moved to be to the right of the right-most cleaned node, and we do know that
|
2800 | // all the clean nodes were to the left of the index. So we move the index left
|
2801 | // until just after the right-most clean node.
|
2802 | index -= 1;
|
2803 | }
|
2804 | assert(() {
|
2805 | for (int i = index - 1; i >= 0; i -= 1) {
|
2806 | final Element element = _dirtyElements[i];
|
2807 | assert(!element.dirty || element._lifecycleState != _ElementLifecycle.active);
|
2808 | }
|
2809 | return true;
|
2810 | }());
|
2811 | return index;
|
2812 | }
|
2813 | }
|
2814 |
|
2815 | /// Manager class for the widgets framework.
|
2816 | ///
|
2817 | /// This class tracks which widgets need rebuilding, and handles other tasks
|
2818 | /// that apply to widget trees as a whole, such as managing the inactive element
|
2819 | /// list for the tree and triggering the "reassemble" command when necessary
|
2820 | /// during hot reload when debugging.
|
2821 | ///
|
2822 | /// The main build owner is typically owned by the [WidgetsBinding], and is
|
2823 | /// driven from the operating system along with the rest of the
|
2824 | /// build/layout/paint pipeline.
|
2825 | ///
|
2826 | /// Additional build owners can be built to manage off-screen widget trees.
|
2827 | ///
|
2828 | /// To assign a build owner to a tree, use the
|
2829 | /// [RootElementMixin.assignOwner] method on the root element of the
|
2830 | /// widget tree.
|
2831 | ///
|
2832 | /// {@tool dartpad}
|
2833 | /// This example shows how to build an off-screen widget tree used to measure
|
2834 | /// the layout size of the rendered tree. For some use cases, the simpler
|
2835 | /// [Offstage] widget may be a better alternative to this approach.
|
2836 | ///
|
2837 | /// ** See code in examples/api/lib/widgets/framework/build_owner.0.dart **
|
2838 | /// {@end-tool}
|
2839 | class BuildOwner {
|
2840 | /// Creates an object that manages widgets.
|
2841 | ///
|
2842 | /// If the `focusManager` argument is not specified or is null, this will
|
2843 | /// construct a new [FocusManager] and register its global input handlers
|
2844 | /// via [FocusManager.registerGlobalHandlers], which will modify static
|
2845 | /// state. Callers wishing to avoid altering this state can explicitly pass
|
2846 | /// a focus manager here.
|
2847 | BuildOwner({ this.onBuildScheduled, FocusManager? focusManager }) :
|
2848 | focusManager = focusManager ?? (FocusManager()..registerGlobalHandlers());
|
2849 |
|
2850 | /// Called on each build pass when the first buildable element is marked
|
2851 | /// dirty.
|
2852 | VoidCallback? onBuildScheduled;
|
2853 |
|
2854 | final _InactiveElements _inactiveElements = _InactiveElements();
|
2855 |
|
2856 | bool _scheduledFlushDirtyElements = false;
|
2857 |
|
2858 | /// The object in charge of the focus tree.
|
2859 | ///
|
2860 | /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain
|
2861 | /// the [FocusScopeNode] for a given [BuildContext].
|
2862 | ///
|
2863 | /// See [FocusManager] for more details.
|
2864 | ///
|
2865 | /// This field will default to a [FocusManager] that has registered its
|
2866 | /// global input handlers via [FocusManager.registerGlobalHandlers]. Callers
|
2867 | /// wishing to avoid registering those handlers (and modifying the associated
|
2868 | /// static state) can explicitly pass a focus manager to the [BuildOwner.new]
|
2869 | /// constructor.
|
2870 | FocusManager focusManager;
|
2871 |
|
2872 | /// Adds an element to the dirty elements list so that it will be rebuilt
|
2873 | /// when [WidgetsBinding.drawFrame] calls [buildScope].
|
2874 | void scheduleBuildFor(Element element) {
|
2875 | assert(element.owner == this);
|
2876 | assert(element._parentBuildScope != null);
|
2877 | assert(() {
|
2878 | if (debugPrintScheduleBuildForStacks) {
|
2879 | debugPrintStack(label: 'scheduleBuildFor() called for $element${element.buildScope._dirtyElements.contains(element) ? " (ALREADY IN LIST)" : "" }' );
|
2880 | }
|
2881 | if (!element.dirty) {
|
2882 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
2883 | ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.' ),
|
2884 | element.describeElement('The method was called for the following element' ),
|
2885 | ErrorDescription(
|
2886 | 'This element is not current marked as dirty. Make sure to set the dirty flag before '
|
2887 | 'calling scheduleBuildFor().' ,
|
2888 | ),
|
2889 | ErrorHint(
|
2890 | 'If you did not attempt to call scheduleBuildFor() yourself, then this probably '
|
2891 | 'indicates a bug in the widgets framework. Please report it:\n'
|
2892 | ' https://github.com/flutter/flutter/issues/new?template=2_bug.yml',
|
2893 | ),
|
2894 | ]);
|
2895 | }
|
2896 | return true;
|
2897 | }());
|
2898 | final BuildScope buildScope = element.buildScope;
|
2899 | assert(() {
|
2900 | if (debugPrintScheduleBuildForStacks && element._inDirtyList) {
|
2901 | debugPrintStack(
|
2902 | label: 'BuildOwner.scheduleBuildFor() called; '
|
2903 | '_dirtyElementsNeedsResorting was ${buildScope._dirtyElementsNeedsResorting} (now true); '
|
2904 | 'The dirty list for the current build scope is: ${buildScope._dirtyElements}' ,
|
2905 | );
|
2906 | }
|
2907 | if (!_debugBuilding && element._inDirtyList) {
|
2908 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
2909 | ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.' ),
|
2910 | ErrorHint(
|
2911 | 'The BuildOwner.scheduleBuildFor() method called on an Element '
|
2912 | 'that is already in the dirty list.' ,
|
2913 | ),
|
2914 | element.describeElement('the dirty Element was' ),
|
2915 | ]);
|
2916 | }
|
2917 | return true;
|
2918 | }());
|
2919 | if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
|
2920 | _scheduledFlushDirtyElements = true;
|
2921 | onBuildScheduled!();
|
2922 | }
|
2923 | buildScope._scheduleBuildFor(element);
|
2924 | assert(() {
|
2925 | if (debugPrintScheduleBuildForStacks) {
|
2926 | debugPrint("...the build scope's dirty list is now: $buildScope._dirtyElements" );
|
2927 | }
|
2928 | return true;
|
2929 | }());
|
2930 | }
|
2931 |
|
2932 | int _debugStateLockLevel = 0;
|
2933 | bool get _debugStateLocked => _debugStateLockLevel > 0;
|
2934 |
|
2935 | /// Whether this widget tree is in the build phase.
|
2936 | ///
|
2937 | /// Only valid when asserts are enabled.
|
2938 | bool get debugBuilding => _debugBuilding;
|
2939 | bool _debugBuilding = false;
|
2940 | Element? _debugCurrentBuildTarget;
|
2941 |
|
2942 | /// Establishes a scope in which calls to [State.setState] are forbidden, and
|
2943 | /// calls the given `callback`.
|
2944 | ///
|
2945 | /// This mechanism is used to ensure that, for instance, [State.dispose] does
|
2946 | /// not call [State.setState].
|
2947 | void lockState(VoidCallback callback) {
|
2948 | assert(_debugStateLockLevel >= 0);
|
2949 | assert(() {
|
2950 | _debugStateLockLevel += 1;
|
2951 | return true;
|
2952 | }());
|
2953 | try {
|
2954 | callback();
|
2955 | } finally {
|
2956 | assert(() {
|
2957 | _debugStateLockLevel -= 1;
|
2958 | return true;
|
2959 | }());
|
2960 | }
|
2961 | assert(_debugStateLockLevel >= 0);
|
2962 | }
|
2963 |
|
2964 | /// Establishes a scope for updating the widget tree, and calls the given
|
2965 | /// `callback`, if any. Then, builds all the elements that were marked as
|
2966 | /// dirty using [scheduleBuildFor], in depth order.
|
2967 | ///
|
2968 | /// This mechanism prevents build methods from transitively requiring other
|
2969 | /// build methods to run, potentially causing infinite loops.
|
2970 | ///
|
2971 | /// The dirty list is processed after `callback` returns, building all the
|
2972 | /// elements that were marked as dirty using [scheduleBuildFor], in depth
|
2973 | /// order. If elements are marked as dirty while this method is running, they
|
2974 | /// must be deeper than the `context` node, and deeper than any
|
2975 | /// previously-built node in this pass.
|
2976 | ///
|
2977 | /// To flush the current dirty list without performing any other work, this
|
2978 | /// function can be called with no callback. This is what the framework does
|
2979 | /// each frame, in [WidgetsBinding.drawFrame].
|
2980 | ///
|
2981 | /// Only one [buildScope] can be active at a time.
|
2982 | ///
|
2983 | /// A [buildScope] implies a [lockState] scope as well.
|
2984 | ///
|
2985 | /// To print a console message every time this method is called, set
|
2986 | /// [debugPrintBuildScope] to true. This is useful when debugging problems
|
2987 | /// involving widgets not getting marked dirty, or getting marked dirty too
|
2988 | /// often.
|
2989 | @pragma('vm:notify-debugger-on-exception' )
|
2990 | void buildScope(Element context, [ VoidCallback? callback ]) {
|
2991 | final BuildScope buildScope = context.buildScope;
|
2992 | if (callback == null && buildScope._dirtyElements.isEmpty) {
|
2993 | return;
|
2994 | }
|
2995 | assert(_debugStateLockLevel >= 0);
|
2996 | assert(!_debugBuilding);
|
2997 | assert(() {
|
2998 | if (debugPrintBuildScope) {
|
2999 | debugPrint(
|
3000 | 'buildScope called with context $context; '
|
3001 | "its build scope's dirty list is: ${buildScope._dirtyElements}" ,
|
3002 | );
|
3003 | }
|
3004 | _debugStateLockLevel += 1;
|
3005 | _debugBuilding = true;
|
3006 | return true;
|
3007 | }());
|
3008 | if (!kReleaseMode) {
|
3009 | Map<String, String>? debugTimelineArguments;
|
3010 | assert(() {
|
3011 | if (debugEnhanceBuildTimelineArguments) {
|
3012 | debugTimelineArguments = <String, String>{
|
3013 | 'build scope dirty count' : ' ${buildScope._dirtyElements.length}' ,
|
3014 | 'build scope dirty list' : ' ${buildScope._dirtyElements}' ,
|
3015 | 'lock level' : ' $_debugStateLockLevel' ,
|
3016 | 'scope context' : ' $context' ,
|
3017 | };
|
3018 | }
|
3019 | return true;
|
3020 | }());
|
3021 | FlutterTimeline.startSync(
|
3022 | 'BUILD' ,
|
3023 | arguments: debugTimelineArguments
|
3024 | );
|
3025 | }
|
3026 | try {
|
3027 | _scheduledFlushDirtyElements = true;
|
3028 | buildScope._building = true;
|
3029 | if (callback != null) {
|
3030 | assert(_debugStateLocked);
|
3031 | Element? debugPreviousBuildTarget;
|
3032 | assert(() {
|
3033 | debugPreviousBuildTarget = _debugCurrentBuildTarget;
|
3034 | _debugCurrentBuildTarget = context;
|
3035 | return true;
|
3036 | }());
|
3037 | try {
|
3038 | callback();
|
3039 | } finally {
|
3040 | assert(() {
|
3041 | assert(_debugCurrentBuildTarget == context);
|
3042 | _debugCurrentBuildTarget = debugPreviousBuildTarget;
|
3043 | _debugElementWasRebuilt(context);
|
3044 | return true;
|
3045 | }());
|
3046 | }
|
3047 | }
|
3048 | buildScope._flushDirtyElements(debugBuildRoot: context);
|
3049 | } finally {
|
3050 | buildScope._building = false;
|
3051 | _scheduledFlushDirtyElements = false;
|
3052 | if (!kReleaseMode) {
|
3053 | FlutterTimeline.finishSync();
|
3054 | }
|
3055 | assert(_debugBuilding);
|
3056 | assert(() {
|
3057 | _debugBuilding = false;
|
3058 | _debugStateLockLevel -= 1;
|
3059 | if (debugPrintBuildScope) {
|
3060 | debugPrint('buildScope finished' );
|
3061 | }
|
3062 | return true;
|
3063 | }());
|
3064 | }
|
3065 | assert(_debugStateLockLevel >= 0);
|
3066 | }
|
3067 |
|
3068 | Map<Element, Set<GlobalKey>>? _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans;
|
3069 |
|
3070 | void _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans(Element node, GlobalKey key) {
|
3071 | final Map<Element, Set<GlobalKey>> map = _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans ??= HashMap<Element, Set<GlobalKey>>();
|
3072 | final Set<GlobalKey> keys = map.putIfAbsent(node, () => HashSet<GlobalKey>());
|
3073 | keys.add(key);
|
3074 | }
|
3075 |
|
3076 | void _debugElementWasRebuilt(Element node) {
|
3077 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.remove(node);
|
3078 | }
|
3079 |
|
3080 | final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{};
|
3081 |
|
3082 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can
|
3083 | // eliminate unused fields, but not their initializers.
|
3084 | @_debugOnly
|
3085 | final Set<Element>? _debugIllFatedElements = kDebugMode ? HashSet<Element>() : null;
|
3086 |
|
3087 | // This map keeps track which child reserves the global key with the parent.
|
3088 | // Parent, child -> global key.
|
3089 | // This provides us a way to remove old reservation while parent rebuilds the
|
3090 | // child in the same slot.
|
3091 | //
|
3092 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can
|
3093 | // eliminate unused fields, but not their initializers.
|
3094 | @_debugOnly
|
3095 | final Map<Element, Map<Element, GlobalKey>>? _debugGlobalKeyReservations = kDebugMode ? <Element, Map<Element, GlobalKey>>{} : null;
|
3096 |
|
3097 | /// The number of [GlobalKey] instances that are currently associated with
|
3098 | /// [Element]s that have been built by this build owner.
|
3099 | int get globalKeyCount => _globalKeyRegistry.length;
|
3100 |
|
3101 | void _debugRemoveGlobalKeyReservationFor(Element parent, Element child) {
|
3102 | assert(() {
|
3103 | _debugGlobalKeyReservations?[parent]?.remove(child);
|
3104 | return true;
|
3105 | }());
|
3106 | }
|
3107 |
|
3108 | void _registerGlobalKey(GlobalKey key, Element element) {
|
3109 | assert(() {
|
3110 | if (_globalKeyRegistry.containsKey(key)) {
|
3111 | final Element oldElement = _globalKeyRegistry[key]!;
|
3112 | assert(element.widget.runtimeType != oldElement.widget.runtimeType);
|
3113 | _debugIllFatedElements?.add(oldElement);
|
3114 | }
|
3115 | return true;
|
3116 | }());
|
3117 | _globalKeyRegistry[key] = element;
|
3118 | }
|
3119 |
|
3120 | void _unregisterGlobalKey(GlobalKey key, Element element) {
|
3121 | assert(() {
|
3122 | if (_globalKeyRegistry.containsKey(key) && _globalKeyRegistry[key] != element) {
|
3123 | final Element oldElement = _globalKeyRegistry[key]!;
|
3124 | assert(element.widget.runtimeType != oldElement.widget.runtimeType);
|
3125 | }
|
3126 | return true;
|
3127 | }());
|
3128 | if (_globalKeyRegistry[key] == element) {
|
3129 | _globalKeyRegistry.remove(key);
|
3130 | }
|
3131 | }
|
3132 |
|
3133 | void _debugReserveGlobalKeyFor(Element parent, Element child, GlobalKey key) {
|
3134 | assert(() {
|
3135 | _debugGlobalKeyReservations?[parent] ??= <Element, GlobalKey>{};
|
3136 | _debugGlobalKeyReservations?[parent]![child] = key;
|
3137 | return true;
|
3138 | }());
|
3139 | }
|
3140 |
|
3141 | void _debugVerifyGlobalKeyReservation() {
|
3142 | assert(() {
|
3143 | final Map<GlobalKey, Element> keyToParent = <GlobalKey, Element>{};
|
3144 | _debugGlobalKeyReservations?.forEach((Element parent, Map<Element, GlobalKey> childToKey) {
|
3145 | // We ignore parent that are unmounted or detached.
|
3146 | if (parent._lifecycleState == _ElementLifecycle.defunct || parent.renderObject?.attached == false) {
|
3147 | return;
|
3148 | }
|
3149 | childToKey.forEach((Element child, GlobalKey key) {
|
3150 | // If parent = null, the node is deactivated by its parent and is
|
3151 | // not re-attached to other part of the tree. We should ignore this
|
3152 | // node.
|
3153 | if (child._parent == null) {
|
3154 | return;
|
3155 | }
|
3156 | // It is possible the same key registers to the same parent twice
|
3157 | // with different children. That is illegal, but it is not in the
|
3158 | // scope of this check. Such error will be detected in
|
3159 | // _debugVerifyIllFatedPopulation or
|
3160 | // _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans.
|
3161 | if (keyToParent.containsKey(key) && keyToParent[key] != parent) {
|
3162 | // We have duplication reservations for the same global key.
|
3163 | final Element older = keyToParent[key]!;
|
3164 | final Element newer = parent;
|
3165 | final FlutterError error;
|
3166 | if (older.toString() != newer.toString()) {
|
3167 | error = FlutterError.fromParts(<DiagnosticsNode>[
|
3168 | ErrorSummary('Multiple widgets used the same GlobalKey.' ),
|
3169 | ErrorDescription(
|
3170 | 'The key $key was used by multiple widgets. The parents of those widgets were:\n'
|
3171 | '- $older\n'
|
3172 | '- $newer\n'
|
3173 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
3174 | ),
|
3175 | ]);
|
3176 | } else {
|
3177 | error = FlutterError.fromParts(<DiagnosticsNode>[
|
3178 | ErrorSummary('Multiple widgets used the same GlobalKey.' ),
|
3179 | ErrorDescription(
|
3180 | 'The key $key was used by multiple widgets. The parents of those widgets were '
|
3181 | 'different widgets that both had the following description:\n'
|
3182 | ' $parent\n'
|
3183 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
3184 | ),
|
3185 | ]);
|
3186 | }
|
3187 | // Fix the tree by removing the duplicated child from one of its
|
3188 | // parents to resolve the duplicated key issue. This allows us to
|
3189 | // tear down the tree during testing without producing additional
|
3190 | // misleading exceptions.
|
3191 | if (child._parent != older) {
|
3192 | older.visitChildren((Element currentChild) {
|
3193 | if (currentChild == child) {
|
3194 | older.forgetChild(child);
|
3195 | }
|
3196 | });
|
3197 | }
|
3198 | if (child._parent != newer) {
|
3199 | newer.visitChildren((Element currentChild) {
|
3200 | if (currentChild == child) {
|
3201 | newer.forgetChild(child);
|
3202 | }
|
3203 | });
|
3204 | }
|
3205 | throw error;
|
3206 | } else {
|
3207 | keyToParent[key] = parent;
|
3208 | }
|
3209 | });
|
3210 | });
|
3211 | _debugGlobalKeyReservations?.clear();
|
3212 | return true;
|
3213 | }());
|
3214 | }
|
3215 |
|
3216 | void _debugVerifyIllFatedPopulation() {
|
3217 | assert(() {
|
3218 | Map<GlobalKey, Set<Element>>? duplicates;
|
3219 | for (final Element element in _debugIllFatedElements ?? const <Element>{}) {
|
3220 | if (element._lifecycleState != _ElementLifecycle.defunct) {
|
3221 | assert(element.widget.key != null);
|
3222 | final GlobalKey key = element.widget.key! as GlobalKey;
|
3223 | assert(_globalKeyRegistry.containsKey(key));
|
3224 | duplicates ??= <GlobalKey, Set<Element>>{};
|
3225 | // Uses ordered set to produce consistent error message.
|
3226 | final Set<Element> elements = duplicates.putIfAbsent(key, () => <Element>{});
|
3227 | elements.add(element);
|
3228 | elements.add(_globalKeyRegistry[key]!);
|
3229 | }
|
3230 | }
|
3231 | _debugIllFatedElements?.clear();
|
3232 | if (duplicates != null) {
|
3233 | final List<DiagnosticsNode> information = <DiagnosticsNode>[];
|
3234 | information.add(ErrorSummary('Multiple widgets used the same GlobalKey.' ));
|
3235 | for (final GlobalKey key in duplicates.keys) {
|
3236 | final Set<Element> elements = duplicates[key]!;
|
3237 | // TODO(jacobr): this will omit the '- ' before each widget name and
|
3238 | // use the more standard whitespace style instead. Please let me know
|
3239 | // if the '- ' style is a feature we want to maintain and we can add
|
3240 | // another tree style that supports it. I also see '* ' in some places
|
3241 | // so it would be nice to unify and normalize.
|
3242 | information.add(Element.describeElements('The key $key was used by ${elements.length} widgets' , elements));
|
3243 | }
|
3244 | information.add(ErrorDescription('A GlobalKey can only be specified on one widget at a time in the widget tree.' ));
|
3245 | throw FlutterError.fromParts(information);
|
3246 | }
|
3247 | return true;
|
3248 | }());
|
3249 | }
|
3250 |
|
3251 | /// Complete the element build pass by unmounting any elements that are no
|
3252 | /// longer active.
|
3253 | ///
|
3254 | /// This is called by [WidgetsBinding.drawFrame].
|
3255 | ///
|
3256 | /// In debug mode, this also runs some sanity checks, for example checking for
|
3257 | /// duplicate global keys.
|
3258 | @pragma('vm:notify-debugger-on-exception' )
|
3259 | void finalizeTree() {
|
3260 | if (!kReleaseMode) {
|
3261 | FlutterTimeline.startSync('FINALIZE TREE' );
|
3262 | }
|
3263 | try {
|
3264 | lockState(_inactiveElements._unmountAll); // this unregisters the GlobalKeys
|
3265 | assert(() {
|
3266 | try {
|
3267 | _debugVerifyGlobalKeyReservation();
|
3268 | _debugVerifyIllFatedPopulation();
|
3269 | if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.isNotEmpty ?? false) {
|
3270 | final Set<GlobalKey> keys = HashSet<GlobalKey>();
|
3271 | for (final Element element in _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys) {
|
3272 | if (element._lifecycleState != _ElementLifecycle.defunct) {
|
3273 | keys.addAll(_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans![element]!);
|
3274 | }
|
3275 | }
|
3276 | if (keys.isNotEmpty) {
|
3277 | final Map<String, int> keyStringCount = HashMap<String, int>();
|
3278 | for (final String key in keys.map<String>((GlobalKey key) => key.toString())) {
|
3279 | if (keyStringCount.containsKey(key)) {
|
3280 | keyStringCount.update(key, (int value) => value + 1);
|
3281 | } else {
|
3282 | keyStringCount[key] = 1;
|
3283 | }
|
3284 | }
|
3285 | final List<String> keyLabels = <String>[
|
3286 | for (final MapEntry<String, int>(:String key, value: int count) in keyStringCount.entries)
|
3287 | if (count == 1) key
|
3288 | else ' $key ( $count different affected keys had this toString representation)' ,
|
3289 | ];
|
3290 | final Iterable<Element> elements = _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys;
|
3291 | final Map<String, int> elementStringCount = HashMap<String, int>();
|
3292 | for (final String element in elements.map<String>((Element element) => element.toString())) {
|
3293 | if (elementStringCount.containsKey(element)) {
|
3294 | elementStringCount.update(element, (int value) => value + 1);
|
3295 | } else {
|
3296 | elementStringCount[element] = 1;
|
3297 | }
|
3298 | }
|
3299 | final List<String> elementLabels = <String>[
|
3300 | for (final MapEntry<String, int>(key: String element, value: int count) in elementStringCount.entries)
|
3301 | if (count == 1) element
|
3302 | else ' $element ( $count different affected elements had this toString representation)' ,
|
3303 | ];
|
3304 | assert(keyLabels.isNotEmpty);
|
3305 | final String the = keys.length == 1 ? ' the' : '' ;
|
3306 | final String s = keys.length == 1 ? '' : 's' ;
|
3307 | final String were = keys.length == 1 ? 'was' : 'were' ;
|
3308 | final String their = keys.length == 1 ? 'its' : 'their' ;
|
3309 | final String respective = elementLabels.length == 1 ? '' : ' respective' ;
|
3310 | final String those = keys.length == 1 ? 'that' : 'those' ;
|
3311 | final String s2 = elementLabels.length == 1 ? '' : 's' ;
|
3312 | final String those2 = elementLabels.length == 1 ? 'that' : 'those' ;
|
3313 | final String they = elementLabels.length == 1 ? 'it' : 'they' ;
|
3314 | final String think = elementLabels.length == 1 ? 'thinks' : 'think' ;
|
3315 | final String are = elementLabels.length == 1 ? 'is' : 'are' ;
|
3316 | // TODO(jacobr): make this error more structured to better expose which widgets had problems.
|
3317 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
3318 | ErrorSummary('Duplicate GlobalKey $s detected in widget tree.' ),
|
3319 | // TODO(jacobr): refactor this code so the elements are clickable
|
3320 | // in GUI debug tools.
|
3321 | ErrorDescription(
|
3322 | 'The following GlobalKey $s $were specified multiple times in the widget tree. This will lead to '
|
3323 | 'parts of the widget tree being truncated unexpectedly, because the second time a key is seen, '
|
3324 | 'the previous instance is moved to the new location. The key $s $were:\n'
|
3325 | '- ${keyLabels.join("\n " )}\n'
|
3326 | 'This was determined by noticing that after $the widget $s with the above global key $s $were moved '
|
3327 | 'out of $their$respective previous parent $s2, $those2 previous parent $s2 never updated during this frame, meaning '
|
3328 | 'that $they either did not update at all or updated before the widget $s $were moved, in either case '
|
3329 | 'implying that $they still $think that $they should have a child with $those global key $s.\n'
|
3330 | 'The specific parent $s2 that did not update after having one or more children forcibly removed '
|
3331 | 'due to GlobalKey reparenting $are:\n'
|
3332 | '- ${elementLabels.join("\n " )}'
|
3333 | '\nA GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
3334 | ),
|
3335 | ]);
|
3336 | }
|
3337 | }
|
3338 | } finally {
|
3339 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear();
|
3340 | }
|
3341 | return true;
|
3342 | }());
|
3343 | } catch (e, stack) {
|
3344 | // Catching the exception directly to avoid activating the ErrorWidget.
|
3345 | // Since the tree is in a broken state, adding the ErrorWidget would
|
3346 | // cause more exceptions.
|
3347 | _reportException(ErrorSummary('while finalizing the widget tree' ), e, stack);
|
3348 | } finally {
|
3349 | if (!kReleaseMode) {
|
3350 | FlutterTimeline.finishSync();
|
3351 | }
|
3352 | }
|
3353 | }
|
3354 |
|
3355 | /// Cause the entire subtree rooted at the given [Element] to be entirely
|
3356 | /// rebuilt. This is used by development tools when the application code has
|
3357 | /// changed and is being hot-reloaded, to cause the widget tree to pick up any
|
3358 | /// changed implementations.
|
3359 | ///
|
3360 | /// This is expensive and should not be called except during development.
|
3361 | void reassemble(Element root) {
|
3362 | if (!kReleaseMode) {
|
3363 | FlutterTimeline.startSync('Preparing Hot Reload (widgets)' );
|
3364 | }
|
3365 | try {
|
3366 | assert(root._parent == null);
|
3367 | assert(root.owner == this);
|
3368 | root.reassemble();
|
3369 | } finally {
|
3370 | if (!kReleaseMode) {
|
3371 | FlutterTimeline.finishSync();
|
3372 | }
|
3373 | }
|
3374 | }
|
3375 | }
|
3376 |
|
3377 | /// Mixin this class to allow receiving [Notification] objects dispatched by
|
3378 | /// child elements.
|
3379 | ///
|
3380 | /// See also:
|
3381 | /// * [NotificationListener], for a widget that allows consuming notifications.
|
3382 | mixin NotifiableElementMixin on Element {
|
3383 | /// Called when a notification of the appropriate type arrives at this
|
3384 | /// location in the tree.
|
3385 | ///
|
3386 | /// Return true to cancel the notification bubbling. Return false to
|
3387 | /// allow the notification to continue to be dispatched to further ancestors.
|
3388 | bool onNotification(Notification notification);
|
3389 |
|
3390 | @override
|
3391 | void attachNotificationTree() {
|
3392 | _notificationTree = _NotificationNode(_parent?._notificationTree, this);
|
3393 | }
|
3394 | }
|
3395 |
|
3396 | class _NotificationNode {
|
3397 | _NotificationNode(this.parent, this.current);
|
3398 |
|
3399 | NotifiableElementMixin? current;
|
3400 | _NotificationNode? parent;
|
3401 |
|
3402 | void dispatchNotification(Notification notification) {
|
3403 | if (current?.onNotification(notification) ?? true) {
|
3404 | return;
|
3405 | }
|
3406 | parent?.dispatchNotification(notification);
|
3407 | }
|
3408 | }
|
3409 |
|
3410 | bool _isProfileBuildsEnabledFor(Widget widget) {
|
3411 | return debugProfileBuildsEnabled ||
|
3412 | (debugProfileBuildsEnabledUserWidgets &&
|
3413 | debugIsWidgetLocalCreation(widget));
|
3414 | }
|
3415 |
|
3416 | /// An instantiation of a [Widget] at a particular location in the tree.
|
3417 | ///
|
3418 | /// Widgets describe how to configure a subtree but the same widget can be used
|
3419 | /// to configure multiple subtrees simultaneously because widgets are immutable.
|
3420 | /// An [Element] represents the use of a widget to configure a specific location
|
3421 | /// in the tree. Over time, the widget associated with a given element can
|
3422 | /// change, for example, if the parent widget rebuilds and creates a new widget
|
3423 | /// for this location.
|
3424 | ///
|
3425 | /// Elements form a tree. Most elements have a unique child, but some widgets
|
3426 | /// (e.g., subclasses of [RenderObjectElement]) can have multiple children.
|
3427 | ///
|
3428 | /// Elements have the following lifecycle:
|
3429 | ///
|
3430 | /// * The framework creates an element by calling [Widget.createElement] on the
|
3431 | /// widget that will be used as the element's initial configuration.
|
3432 | /// * The framework calls [mount] to add the newly created element to the tree
|
3433 | /// at a given slot in a given parent. The [mount] method is responsible for
|
3434 | /// inflating any child widgets and calling [attachRenderObject] as
|
3435 | /// necessary to attach any associated render objects to the render tree.
|
3436 | /// * At this point, the element is considered "active" and might appear on
|
3437 | /// screen.
|
3438 | /// * At some point, the parent might decide to change the widget used to
|
3439 | /// configure this element, for example because the parent rebuilt with new
|
3440 | /// state. When this happens, the framework will call [update] with the new
|
3441 | /// widget. The new widget will always have the same [runtimeType] and key as
|
3442 | /// old widget. If the parent wishes to change the [runtimeType] or key of
|
3443 | /// the widget at this location in the tree, it can do so by unmounting this
|
3444 | /// element and inflating the new widget at this location.
|
3445 | /// * At some point, an ancestor might decide to remove this element (or an
|
3446 | /// intermediate ancestor) from the tree, which the ancestor does by calling
|
3447 | /// [deactivateChild] on itself. Deactivating the intermediate ancestor will
|
3448 | /// remove that element's render object from the render tree and add this
|
3449 | /// element to the [owner]'s list of inactive elements, causing the framework
|
3450 | /// to call [deactivate] on this element.
|
3451 | /// * At this point, the element is considered "inactive" and will not appear
|
3452 | /// on screen. An element can remain in the inactive state only until
|
3453 | /// the end of the current animation frame. At the end of the animation
|
3454 | /// frame, any elements that are still inactive will be unmounted.
|
3455 | /// * If the element gets reincorporated into the tree (e.g., because it or one
|
3456 | /// of its ancestors has a global key that is reused), the framework will
|
3457 | /// remove the element from the [owner]'s list of inactive elements, call
|
3458 | /// [activate] on the element, and reattach the element's render object to
|
3459 | /// the render tree. (At this point, the element is again considered "active"
|
3460 | /// and might appear on screen.)
|
3461 | /// * If the element does not get reincorporated into the tree by the end of
|
3462 | /// the current animation frame, the framework will call [unmount] on the
|
3463 | /// element.
|
3464 | /// * At this point, the element is considered "defunct" and will not be
|
3465 | /// incorporated into the tree in the future.
|
3466 | abstract class Element extends DiagnosticableTree implements BuildContext {
|
3467 | /// Creates an element that uses the given widget as its configuration.
|
3468 | ///
|
3469 | /// Typically called by an override of [Widget.createElement].
|
3470 | Element(Widget widget)
|
3471 | : _widget = widget {
|
3472 | if (kFlutterMemoryAllocationsEnabled) {
|
3473 | FlutterMemoryAllocations.instance.dispatchObjectCreated(
|
3474 | library: _flutterWidgetsLibrary,
|
3475 | className: ' $Element' ,
|
3476 | object: this,
|
3477 | );
|
3478 | }
|
3479 | }
|
3480 |
|
3481 | Element? _parent;
|
3482 | _NotificationNode? _notificationTree;
|
3483 |
|
3484 | /// Compare two widgets for equality.
|
3485 | ///
|
3486 | /// When a widget is rebuilt with another that compares equal according
|
3487 | /// to `operator ==`, it is assumed that the update is redundant and the
|
3488 | /// work to update that branch of the tree is skipped.
|
3489 | ///
|
3490 | /// It is generally discouraged to override `operator ==` on any widget that
|
3491 | /// has children, since a correct implementation would have to defer to the
|
3492 | /// children's equality operator also, and that is an O(N²) operation: each
|
3493 | /// child would need to itself walk all its children, each step of the tree.
|
3494 | ///
|
3495 | /// It is sometimes reasonable for a leaf widget (one with no children) to
|
3496 | /// implement this method, if rebuilding the widget is known to be much more
|
3497 | /// expensive than checking the widgets' parameters for equality and if the
|
3498 | /// widget is expected to often be rebuilt with identical parameters.
|
3499 | ///
|
3500 | /// In general, however, it is more efficient to cache the widgets used
|
3501 | /// in a build method if it is known that they will not change.
|
3502 | @nonVirtual
|
3503 | @override
|
3504 | // ignore: avoid_equals_and_hash_code_on_mutable_classes, hash_and_equals
|
3505 | bool operator ==(Object other) => identical(this, other);
|
3506 |
|
3507 | /// Information set by parent to define where this child fits in its parent's
|
3508 | /// child list.
|
3509 | ///
|
3510 | /// A child widget's slot is determined when the parent's [updateChild] method
|
3511 | /// is called to inflate the child widget. See [RenderObjectElement] for more
|
3512 | /// details on slots.
|
3513 | Object? get slot => _slot;
|
3514 | Object? _slot;
|
3515 |
|
3516 | /// An integer that is guaranteed to be greater than the parent's, if any.
|
3517 | /// The element at the root of the tree must have a depth greater than 0.
|
3518 | int get depth {
|
3519 | assert(() {
|
3520 | if (_lifecycleState == _ElementLifecycle.initial) {
|
3521 | throw FlutterError('Depth is only available when element has been mounted.' );
|
3522 | }
|
3523 | return true;
|
3524 | }());
|
3525 | return _depth;
|
3526 | }
|
3527 | late int _depth;
|
3528 |
|
3529 | /// Returns result < 0 when [a] < [b], result == 0 when [a] == [b], result > 0
|
3530 | /// when [a] > [b].
|
3531 | static int _sort(Element a, Element b) {
|
3532 | final int diff = a.depth - b.depth;
|
3533 | // If depths are not equal, return the difference.
|
3534 | if (diff != 0) {
|
3535 | return diff;
|
3536 | }
|
3537 | // If the `dirty` values are not equal, sort with non-dirty elements being
|
3538 | // less than dirty elements.
|
3539 | final bool isBDirty = b.dirty;
|
3540 | if (a.dirty != isBDirty) {
|
3541 | return isBDirty ? -1 : 1;
|
3542 | }
|
3543 | // Otherwise, `depth`s and `dirty`s are equal.
|
3544 | return 0;
|
3545 | }
|
3546 |
|
3547 | // Return a numeric encoding of the specific `Element` concrete subtype.
|
3548 | // This is used in `Element.updateChild` to determine if a hot reload modified the
|
3549 | // superclass of a mounted element's configuration. The encoding of each `Element`
|
3550 | // must match the corresponding `Widget` encoding in `Widget._debugConcreteSubtype`.
|
3551 | static int _debugConcreteSubtype(Element element) {
|
3552 | return element is StatefulElement ? 1 :
|
3553 | element is StatelessElement ? 2 :
|
3554 | 0;
|
3555 | }
|
3556 |
|
3557 | /// The configuration for this element.
|
3558 | ///
|
3559 | /// Avoid overriding this field on [Element] subtypes to provide a more
|
3560 | /// specific widget type (i.e. [StatelessElement] and [StatelessWidget]).
|
3561 | /// Instead, cast at any call sites where the more specific type is required.
|
3562 | /// This avoids significant cast overhead on the getter which is accessed
|
3563 | /// throughout the framework internals during the build phase - and for which
|
3564 | /// the more specific type information is not used.
|
3565 | @override
|
3566 | Widget get widget => _widget!;
|
3567 | Widget? _widget;
|
3568 |
|
3569 | @override
|
3570 | bool get mounted => _widget != null;
|
3571 |
|
3572 | /// Returns true if the Element is defunct.
|
3573 | ///
|
3574 | /// This getter always returns false in profile and release builds.
|
3575 | /// See the lifecycle documentation for [Element] for additional information.
|
3576 | bool get debugIsDefunct {
|
3577 | bool isDefunct = false;
|
3578 | assert(() {
|
3579 | isDefunct = _lifecycleState == _ElementLifecycle.defunct;
|
3580 | return true;
|
3581 | }());
|
3582 | return isDefunct;
|
3583 | }
|
3584 |
|
3585 | /// Returns true if the Element is active.
|
3586 | ///
|
3587 | /// This getter always returns false in profile and release builds.
|
3588 | /// See the lifecycle documentation for [Element] for additional information.
|
3589 | bool get debugIsActive {
|
3590 | bool isActive = false;
|
3591 | assert(() {
|
3592 | isActive = _lifecycleState == _ElementLifecycle.active;
|
3593 | return true;
|
3594 | }());
|
3595 | return isActive;
|
3596 | }
|
3597 |
|
3598 | /// The object that manages the lifecycle of this element.
|
3599 | @override
|
3600 | BuildOwner? get owner => _owner;
|
3601 | BuildOwner? _owner;
|
3602 |
|
3603 | /// A [BuildScope] whose dirty [Element]s can only be rebuilt by
|
3604 | /// [BuildOwner.buildScope] calls whose `context` argument is an [Element]
|
3605 | /// within this [BuildScope].
|
3606 | ///
|
3607 | /// The getter typically is only safe to access when this [Element] is [mounted].
|
3608 | ///
|
3609 | /// The default implementation returns the parent [Element]'s [buildScope],
|
3610 | /// as in most cases an [Element] is ready to rebuild as soon as its ancestors
|
3611 | /// are no longer dirty. One notable exception is [LayoutBuilder]'s
|
3612 | /// descendants, which must not rebuild until the incoming constraints become
|
3613 | /// available. [LayoutBuilder]'s [Element] overrides [buildScope] to make none
|
3614 | /// of its descendants can rebuild until the incoming constraints are known.
|
3615 | ///
|
3616 | /// If you choose to override this getter to establish your own [BuildScope],
|
3617 | /// to flush the dirty [Element]s in the [BuildScope] you need to manually call
|
3618 | /// [BuildOwner.buildScope] with the root [Element] of your [BuildScope] when
|
3619 | /// appropriate, as the Flutter framework does not try to register or manage
|
3620 | /// custom [BuildScope]s.
|
3621 | ///
|
3622 | /// Always return the same [BuildScope] instance if you override this getter.
|
3623 | /// Changing the value returned by this getter at runtime is not
|
3624 | /// supported.
|
3625 | ///
|
3626 | /// The [updateChild] method ignores [buildScope]: if the parent [Element]
|
3627 | /// calls [updateChild] on a child with a different [BuildScope], the child may
|
3628 | /// still rebuild.
|
3629 | ///
|
3630 | /// See also:
|
3631 | ///
|
3632 | /// * [LayoutBuilder], a widget that establishes a custom [BuildScope].
|
3633 | BuildScope get buildScope => _parentBuildScope!;
|
3634 | // The cached value of the parent Element's build scope. The cache is updated
|
3635 | // when this Element mounts or reparents.
|
3636 | BuildScope? _parentBuildScope;
|
3637 |
|
3638 | /// {@template flutter.widgets.Element.reassemble}
|
3639 | /// Called whenever the application is reassembled during debugging, for
|
3640 | /// example during hot reload.
|
3641 | ///
|
3642 | /// This method should rerun any initialization logic that depends on global
|
3643 | /// state, for example, image loading from asset bundles (since the asset
|
3644 | /// bundle may have changed).
|
3645 | ///
|
3646 | /// This function will only be called during development. In release builds,
|
3647 | /// the `ext.flutter.reassemble` hook is not available, and so this code will
|
3648 | /// never execute.
|
3649 | ///
|
3650 | /// Implementers should not rely on any ordering for hot reload source update,
|
3651 | /// reassemble, and build methods after a hot reload has been initiated. It is
|
3652 | /// possible that a [Timer] (e.g. an [Animation]) or a debugging session
|
3653 | /// attached to the isolate could trigger a build with reloaded code _before_
|
3654 | /// reassemble is called. Code that expects preconditions to be set by
|
3655 | /// reassemble after a hot reload must be resilient to being called out of
|
3656 | /// order, e.g. by fizzling instead of throwing. That said, once reassemble is
|
3657 | /// called, build will be called after it at least once.
|
3658 | /// {@endtemplate}
|
3659 | ///
|
3660 | /// See also:
|
3661 | ///
|
3662 | /// * [State.reassemble]
|
3663 | /// * [BindingBase.reassembleApplication]
|
3664 | /// * [Image], which uses this to reload images.
|
3665 | @mustCallSuper
|
3666 | @protected
|
3667 | void reassemble() {
|
3668 | markNeedsBuild();
|
3669 | visitChildren((Element child) {
|
3670 | child.reassemble();
|
3671 | });
|
3672 | }
|
3673 |
|
3674 | bool _debugIsDescsendantOf(Element target) {
|
3675 | Element? element = this;
|
3676 | while (element != null && element.depth > target.depth) {
|
3677 | element = element._parent;
|
3678 | }
|
3679 | return element == target;
|
3680 | }
|
3681 |
|
3682 | /// The render object at (or below) this location in the tree.
|
3683 | ///
|
3684 | /// If this object is a [RenderObjectElement], the render object is the one at
|
3685 | /// this location in the tree. Otherwise, this getter will walk down the tree
|
3686 | /// until it finds a [RenderObjectElement].
|
3687 | ///
|
3688 | /// Some locations in the tree are not backed by a render object. In those
|
3689 | /// cases, this getter returns null. This can happen, if the element is
|
3690 | /// located outside of a [View] since only the element subtree rooted in a
|
3691 | /// view has a render tree associated with it.
|
3692 | RenderObject? get renderObject {
|
3693 | Element? current = this;
|
3694 | while (current != null) {
|
3695 | if (current._lifecycleState == _ElementLifecycle.defunct) {
|
3696 | break;
|
3697 | } else if (current is RenderObjectElement) {
|
3698 | return current.renderObject;
|
3699 | } else {
|
3700 | current = current.renderObjectAttachingChild;
|
3701 | }
|
3702 | }
|
3703 | return null;
|
3704 | }
|
3705 |
|
3706 | /// Returns the child of this [Element] that will insert a [RenderObject] into
|
3707 | /// an ancestor of this Element to construct the render tree.
|
3708 | ///
|
3709 | /// Returns null if this Element doesn't have any children who need to attach
|
3710 | /// a [RenderObject] to an ancestor of this [Element]. A [RenderObjectElement]
|
3711 | /// will therefore return null because its children insert their
|
3712 | /// [RenderObject]s into the [RenderObjectElement] itself and not into an
|
3713 | /// ancestor of the [RenderObjectElement].
|
3714 | ///
|
3715 | /// Furthermore, this may return null for [Element]s that hoist their own
|
3716 | /// independent render tree and do not extend the ancestor render tree.
|
3717 | @protected
|
3718 | Element? get renderObjectAttachingChild {
|
3719 | Element? next;
|
3720 | visitChildren((Element child) {
|
3721 | assert(next == null); // This verifies that there's only one child.
|
3722 | next = child;
|
3723 | });
|
3724 | return next;
|
3725 | }
|
3726 |
|
3727 | @override
|
3728 | List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType }) {
|
3729 | final List<DiagnosticsNode> information = <DiagnosticsNode>[];
|
3730 | final List<Element> ancestors = <Element>[];
|
3731 | visitAncestorElements((Element element) {
|
3732 | ancestors.add(element);
|
3733 | return true;
|
3734 | });
|
3735 |
|
3736 | information.add(DiagnosticsProperty<Element>(
|
3737 | 'The specific widget that could not find a $expectedAncestorType ancestor was' ,
|
3738 | this,
|
3739 | style: DiagnosticsTreeStyle.errorProperty,
|
3740 | ));
|
3741 |
|
3742 | if (ancestors.isNotEmpty) {
|
3743 | information.add(describeElements('The ancestors of this widget were' , ancestors));
|
3744 | } else {
|
3745 | information.add(ErrorDescription(
|
3746 | 'This widget is the root of the tree, so it has no '
|
3747 | 'ancestors, let alone a " $expectedAncestorType" ancestor.' ,
|
3748 | ));
|
3749 | }
|
3750 | return information;
|
3751 | }
|
3752 |
|
3753 | /// Returns a list of [Element]s from the current build context to the error report.
|
3754 | static DiagnosticsNode describeElements(String name, Iterable<Element> elements) {
|
3755 | return DiagnosticsBlock(
|
3756 | name: name,
|
3757 | children: elements.map<DiagnosticsNode>((Element element) => DiagnosticsProperty<Element>('' , element)).toList(),
|
3758 | allowTruncate: true,
|
3759 | );
|
3760 | }
|
3761 |
|
3762 | @override
|
3763 | DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}) {
|
3764 | return DiagnosticsProperty<Element>(name, this, style: style);
|
3765 | }
|
3766 |
|
3767 | @override
|
3768 | DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}) {
|
3769 | return DiagnosticsProperty<Element>(name, this, style: style);
|
3770 | }
|
3771 |
|
3772 | @override
|
3773 | DiagnosticsNode describeOwnershipChain(String name) {
|
3774 | // TODO(jacobr): make this structured so clients can support clicks on
|
3775 | // individual entries. For example, is this an iterable with arrows as
|
3776 | // separators?
|
3777 | return StringProperty(name, debugGetCreatorChain(10));
|
3778 | }
|
3779 |
|
3780 | // This is used to verify that Element objects move through life in an
|
3781 | // orderly fashion.
|
3782 | _ElementLifecycle _lifecycleState = _ElementLifecycle.initial;
|
3783 |
|
3784 | /// Calls the argument for each child. Must be overridden by subclasses that
|
3785 | /// support having children.
|
3786 | ///
|
3787 | /// There is no guaranteed order in which the children will be visited, though
|
3788 | /// it should be consistent over time.
|
3789 | ///
|
3790 | /// Calling this during build is dangerous: the child list might still be
|
3791 | /// being updated at that point, so the children might not be constructed yet,
|
3792 | /// or might be old children that are going to be replaced. This method should
|
3793 | /// only be called if it is provable that the children are available.
|
3794 | void visitChildren(ElementVisitor visitor) { }
|
3795 |
|
3796 | /// Calls the argument for each child considered onstage.
|
3797 | ///
|
3798 | /// Classes like [Offstage] and [Overlay] override this method to hide their
|
3799 | /// children.
|
3800 | ///
|
3801 | /// Being onstage affects the element's discoverability during testing when
|
3802 | /// you use Flutter's [Finder] objects. For example, when you instruct the
|
3803 | /// test framework to tap on a widget, by default the finder will look for
|
3804 | /// onstage elements and ignore the offstage ones.
|
3805 | ///
|
3806 | /// The default implementation defers to [visitChildren] and therefore treats
|
3807 | /// the element as onstage.
|
3808 | ///
|
3809 | /// See also:
|
3810 | ///
|
3811 | /// * [Offstage] widget that hides its children.
|
3812 | /// * [Finder] that skips offstage widgets by default.
|
3813 | /// * [RenderObject.visitChildrenForSemantics], in contrast to this method,
|
3814 | /// designed specifically for excluding parts of the UI from the semantics
|
3815 | /// tree.
|
3816 | void debugVisitOnstageChildren(ElementVisitor visitor) => visitChildren(visitor);
|
3817 |
|
3818 | /// Wrapper around [visitChildren] for [BuildContext].
|
3819 | @override
|
3820 | void visitChildElements(ElementVisitor visitor) {
|
3821 | assert(() {
|
3822 | if (owner == null || !owner!._debugStateLocked) {
|
3823 | return true;
|
3824 | }
|
3825 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
3826 | ErrorSummary('visitChildElements() called during build.' ),
|
3827 | ErrorDescription(
|
3828 | "The BuildContext.visitChildElements() method can't be called during "
|
3829 | 'build because the child list is still being updated at that point, '
|
3830 | 'so the children might not be constructed yet, or might be old children '
|
3831 | 'that are going to be replaced.' ,
|
3832 | ),
|
3833 | ]);
|
3834 | }());
|
3835 | visitChildren(visitor);
|
3836 | }
|
3837 |
|
3838 | /// Update the given child with the given new configuration.
|
3839 | ///
|
3840 | /// This method is the core of the widgets system. It is called each time we
|
3841 | /// are to add, update, or remove a child based on an updated configuration.
|
3842 | ///
|
3843 | /// The `newSlot` argument specifies the new value for this element's [slot].
|
3844 | ///
|
3845 | /// If the `child` is null, and the `newWidget` is not null, then we have a new
|
3846 | /// child for which we need to create an [Element], configured with `newWidget`.
|
3847 | ///
|
3848 | /// If the `newWidget` is null, and the `child` is not null, then we need to
|
3849 | /// remove it because it no longer has a configuration.
|
3850 | ///
|
3851 | /// If neither are null, then we need to update the `child`'s configuration to
|
3852 | /// be the new configuration given by `newWidget`. If `newWidget` can be given
|
3853 | /// to the existing child (as determined by [Widget.canUpdate]), then it is so
|
3854 | /// given. Otherwise, the old child needs to be disposed and a new child
|
3855 | /// created for the new configuration.
|
3856 | ///
|
3857 | /// If both are null, then we don't have a child and won't have a child, so we
|
3858 | /// do nothing.
|
3859 | ///
|
3860 | /// The [updateChild] method returns the new child, if it had to create one,
|
3861 | /// or the child that was passed in, if it just had to update the child, or
|
3862 | /// null, if it removed the child and did not replace it.
|
3863 | ///
|
3864 | /// The following table summarizes the above:
|
3865 | ///
|
3866 | /// | | **newWidget == null** | **newWidget != null** |
|
3867 | /// | :-----------------: | :--------------------- | :---------------------- |
|
3868 | /// | **child == null** | Returns null. | Returns new [Element]. |
|
3869 | /// | **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
|
3870 | ///
|
3871 | /// The `newSlot` argument is used only if `newWidget` is not null. If `child`
|
3872 | /// is null (or if the old child cannot be updated), then the `newSlot` is
|
3873 | /// given to the new [Element] that is created for the child, via
|
3874 | /// [inflateWidget]. If `child` is not null (and the old child _can_ be
|
3875 | /// updated), then the `newSlot` is given to [updateSlotForChild] to update
|
3876 | /// its slot, in case it has moved around since it was last built.
|
3877 | ///
|
3878 | /// See the [RenderObjectElement] documentation for more information on slots.
|
3879 | @protected
|
3880 | @pragma('dart2js:tryInline' )
|
3881 | @pragma('vm:prefer-inline' )
|
3882 | @pragma('wasm:prefer-inline' )
|
3883 | Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
|
3884 | if (newWidget == null) {
|
3885 | if (child != null) {
|
3886 | deactivateChild(child);
|
3887 | }
|
3888 | return null;
|
3889 | }
|
3890 |
|
3891 | final Element newChild;
|
3892 | if (child != null) {
|
3893 | bool hasSameSuperclass = true;
|
3894 | // When the type of a widget is changed between Stateful and Stateless via
|
3895 | // hot reload, the element tree will end up in a partially invalid state.
|
3896 | // That is, if the widget was a StatefulWidget and is now a StatelessWidget,
|
3897 | // then the element tree currently contains a StatefulElement that is incorrectly
|
3898 | // referencing a StatelessWidget (and likewise with StatelessElement).
|
3899 | //
|
3900 | // To avoid crashing due to type errors, we need to gently guide the invalid
|
3901 | // element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
|
3902 | // returns false which prevents us from trying to update the existing element
|
3903 | // incorrectly.
|
3904 | //
|
3905 | // For the case where the widget becomes Stateful, we also need to avoid
|
3906 | // accessing `StatelessElement.widget` as the cast on the getter will
|
3907 | // cause a type error to be thrown. Here we avoid that by short-circuiting
|
3908 | // the `Widget.canUpdate` check once `hasSameSuperclass` is false.
|
3909 | assert(() {
|
3910 | final int oldElementClass = Element._debugConcreteSubtype(child);
|
3911 | final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
|
3912 | hasSameSuperclass = oldElementClass == newWidgetClass;
|
3913 | return true;
|
3914 | }());
|
3915 | if (hasSameSuperclass && child.widget == newWidget) {
|
3916 | // We don't insert a timeline event here, because otherwise it's
|
3917 | // confusing that widgets that "don't update" (because they didn't
|
3918 | // change) get "charged" on the timeline.
|
3919 | if (child.slot != newSlot) {
|
3920 | updateSlotForChild(child, newSlot);
|
3921 | }
|
3922 | newChild = child;
|
3923 | } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
|
3924 | if (child.slot != newSlot) {
|
3925 | updateSlotForChild(child, newSlot);
|
3926 | }
|
3927 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
|
3928 | if (isTimelineTracked) {
|
3929 | Map<String, String>? debugTimelineArguments;
|
3930 | assert(() {
|
3931 | if (kDebugMode && debugEnhanceBuildTimelineArguments) {
|
3932 | debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments();
|
3933 | }
|
3934 | return true;
|
3935 | }());
|
3936 | FlutterTimeline.startSync(
|
3937 | ' ${newWidget.runtimeType}' ,
|
3938 | arguments: debugTimelineArguments,
|
3939 | );
|
3940 | }
|
3941 | child.update(newWidget);
|
3942 | if (isTimelineTracked) {
|
3943 | FlutterTimeline.finishSync();
|
3944 | }
|
3945 | assert(child.widget == newWidget);
|
3946 | assert(() {
|
3947 | child.owner!._debugElementWasRebuilt(child);
|
3948 | return true;
|
3949 | }());
|
3950 | newChild = child;
|
3951 | } else {
|
3952 | deactivateChild(child);
|
3953 | assert(child._parent == null);
|
3954 | // The [debugProfileBuildsEnabled] code for this branch is inside
|
3955 | // [inflateWidget], since some [Element]s call [inflateWidget] directly
|
3956 | // instead of going through [updateChild].
|
3957 | newChild = inflateWidget(newWidget, newSlot);
|
3958 | }
|
3959 | } else {
|
3960 | // The [debugProfileBuildsEnabled] code for this branch is inside
|
3961 | // [inflateWidget], since some [Element]s call [inflateWidget] directly
|
3962 | // instead of going through [updateChild].
|
3963 | newChild = inflateWidget(newWidget, newSlot);
|
3964 | }
|
3965 |
|
3966 | assert(() {
|
3967 | if (child != null) {
|
3968 | _debugRemoveGlobalKeyReservation(child);
|
3969 | }
|
3970 | final Key? key = newWidget.key;
|
3971 | if (key is GlobalKey) {
|
3972 | assert(owner != null);
|
3973 | owner!._debugReserveGlobalKeyFor(this, newChild, key);
|
3974 | }
|
3975 | return true;
|
3976 | }());
|
3977 |
|
3978 | return newChild;
|
3979 | }
|
3980 |
|
3981 | /// Updates the children of this element to use new widgets.
|
3982 | ///
|
3983 | /// Attempts to update the given old children list using the given new
|
3984 | /// widgets, removing obsolete elements and introducing new ones as necessary,
|
3985 | /// and then returns the new child list.
|
3986 | ///
|
3987 | /// During this function the `oldChildren` list must not be modified. If the
|
3988 | /// caller wishes to remove elements from `oldChildren` reentrantly while
|
3989 | /// this function is on the stack, the caller can supply a `forgottenChildren`
|
3990 | /// argument, which can be modified while this function is on the stack.
|
3991 | /// Whenever this function reads from `oldChildren`, this function first
|
3992 | /// checks whether the child is in `forgottenChildren`. If it is, the function
|
3993 | /// acts as if the child was not in `oldChildren`.
|
3994 | ///
|
3995 | /// This function is a convenience wrapper around [updateChild], which updates
|
3996 | /// each individual child. If `slots` is non-null, the value for the `newSlot`
|
3997 | /// argument of [updateChild] is retrieved from that list using the index that
|
3998 | /// the currently processed `child` corresponds to in the `newWidgets` list
|
3999 | /// (`newWidgets` and `slots` must have the same length). If `slots` is null,
|
4000 | /// an [IndexedSlot<Element>] is used as the value for the `newSlot` argument.
|
4001 | /// In that case, [IndexedSlot.index] is set to the index that the currently
|
4002 | /// processed `child` corresponds to in the `newWidgets` list and
|
4003 | /// [IndexedSlot.value] is set to the [Element] of the previous widget in that
|
4004 | /// list (or null if it is the first child).
|
4005 | ///
|
4006 | /// When the [slot] value of an [Element] changes, its
|
4007 | /// associated [renderObject] needs to move to a new position in the child
|
4008 | /// list of its parents. If that [RenderObject] organizes its children in a
|
4009 | /// linked list (as is done by the [ContainerRenderObjectMixin]) this can
|
4010 | /// be implemented by re-inserting the child [RenderObject] into the
|
4011 | /// list after the [RenderObject] associated with the [Element] provided as
|
4012 | /// [IndexedSlot.value] in the [slot] object.
|
4013 | ///
|
4014 | /// Using the previous sibling as a [slot] is not enough, though, because
|
4015 | /// child [RenderObject]s are only moved around when the [slot] of their
|
4016 | /// associated [RenderObjectElement]s is updated. When the order of child
|
4017 | /// [Element]s is changed, some elements in the list may move to a new index
|
4018 | /// but still have the same previous sibling. For example, when
|
4019 | /// `[e1, e2, e3, e4]` is changed to `[e1, e3, e4, e2]` the element e4
|
4020 | /// continues to have e3 as a previous sibling even though its index in the list
|
4021 | /// has changed and its [RenderObject] needs to move to come before e2's
|
4022 | /// [RenderObject]. In order to trigger this move, a new [slot] value needs to
|
4023 | /// be assigned to its [Element] whenever its index in its
|
4024 | /// parent's child list changes. Using an [IndexedSlot<Element>] achieves
|
4025 | /// exactly that and also ensures that the underlying parent [RenderObject]
|
4026 | /// knows where a child needs to move to in a linked list by providing its new
|
4027 | /// previous sibling.
|
4028 | @protected
|
4029 | List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element>? forgottenChildren, List<Object?>? slots }) {
|
4030 | assert(slots == null || newWidgets.length == slots.length);
|
4031 |
|
4032 | Element? replaceWithNullIfForgotten(Element child) {
|
4033 | return (forgottenChildren?.contains(child) ?? false) ? null : child;
|
4034 | }
|
4035 |
|
4036 | Object? slotFor(int newChildIndex, Element? previousChild) {
|
4037 | return slots != null
|
4038 | ? slots[newChildIndex]
|
4039 | : IndexedSlot<Element?>(newChildIndex, previousChild);
|
4040 | }
|
4041 |
|
4042 | // This attempts to diff the new child list (newWidgets) with
|
4043 | // the old child list (oldChildren), and produce a new list of elements to
|
4044 | // be the new list of child elements of this element. The called of this
|
4045 | // method is expected to update this render object accordingly.
|
4046 |
|
4047 | // The cases it tries to optimize for are:
|
4048 | // - the old list is empty
|
4049 | // - the lists are identical
|
4050 | // - there is an insertion or removal of one or more widgets in
|
4051 | // only one place in the list
|
4052 | // If a widget with a key is in both lists, it will be synced.
|
4053 | // Widgets without keys might be synced but there is no guarantee.
|
4054 |
|
4055 | // The general approach is to sync the entire new list backwards, as follows:
|
4056 | // 1. Walk the lists from the top, syncing nodes, until you no longer have
|
4057 | // matching nodes.
|
4058 | // 2. Walk the lists from the bottom, without syncing nodes, until you no
|
4059 | // longer have matching nodes. We'll sync these nodes at the end. We
|
4060 | // don't sync them now because we want to sync all the nodes in order
|
4061 | // from beginning to end.
|
4062 | // At this point we narrowed the old and new lists to the point
|
4063 | // where the nodes no longer match.
|
4064 | // 3. Walk the narrowed part of the old list to get the list of
|
4065 | // keys and sync null with non-keyed items.
|
4066 | // 4. Walk the narrowed part of the new list forwards:
|
4067 | // * Sync non-keyed items with null
|
4068 | // * Sync keyed items with the source if it exists, else with null.
|
4069 | // 5. Walk the bottom of the list again, syncing the nodes.
|
4070 | // 6. Sync null with any items in the list of keys that are still
|
4071 | // mounted.
|
4072 |
|
4073 | int newChildrenTop = 0;
|
4074 | int oldChildrenTop = 0;
|
4075 | int newChildrenBottom = newWidgets.length - 1;
|
4076 | int oldChildrenBottom = oldChildren.length - 1;
|
4077 |
|
4078 | final List<Element> newChildren = List<Element>.filled(newWidgets.length, _NullElement.instance);
|
4079 |
|
4080 | Element? previousChild;
|
4081 |
|
4082 | // Update the top of the list.
|
4083 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
|
4084 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
|
4085 | final Widget newWidget = newWidgets[newChildrenTop];
|
4086 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
|
4087 | if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget)) {
|
4088 | break;
|
4089 | }
|
4090 | final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
|
4091 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
4092 | newChildren[newChildrenTop] = newChild;
|
4093 | previousChild = newChild;
|
4094 | newChildrenTop += 1;
|
4095 | oldChildrenTop += 1;
|
4096 | }
|
4097 |
|
4098 | // Scan the bottom of the list.
|
4099 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
|
4100 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
|
4101 | final Widget newWidget = newWidgets[newChildrenBottom];
|
4102 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
|
4103 | if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget)) {
|
4104 | break;
|
4105 | }
|
4106 | oldChildrenBottom -= 1;
|
4107 | newChildrenBottom -= 1;
|
4108 | }
|
4109 |
|
4110 | // Scan the old children in the middle of the list.
|
4111 | final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
|
4112 | Map<Key, Element>? oldKeyedChildren;
|
4113 | if (haveOldChildren) {
|
4114 | oldKeyedChildren = <Key, Element>{};
|
4115 | while (oldChildrenTop <= oldChildrenBottom) {
|
4116 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
|
4117 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
|
4118 | if (oldChild != null) {
|
4119 | if (oldChild.widget.key != null) {
|
4120 | oldKeyedChildren[oldChild.widget.key!] = oldChild;
|
4121 | } else {
|
4122 | deactivateChild(oldChild);
|
4123 | }
|
4124 | }
|
4125 | oldChildrenTop += 1;
|
4126 | }
|
4127 | }
|
4128 |
|
4129 | // Update the middle of the list.
|
4130 | while (newChildrenTop <= newChildrenBottom) {
|
4131 | Element? oldChild;
|
4132 | final Widget newWidget = newWidgets[newChildrenTop];
|
4133 | if (haveOldChildren) {
|
4134 | final Key? key = newWidget.key;
|
4135 | if (key != null) {
|
4136 | oldChild = oldKeyedChildren![key];
|
4137 | if (oldChild != null) {
|
4138 | if (Widget.canUpdate(oldChild.widget, newWidget)) {
|
4139 | // we found a match!
|
4140 | // remove it from oldKeyedChildren so we don't unsync it later
|
4141 | oldKeyedChildren.remove(key);
|
4142 | } else {
|
4143 | // Not a match, let's pretend we didn't see it for now.
|
4144 | oldChild = null;
|
4145 | }
|
4146 | }
|
4147 | }
|
4148 | }
|
4149 | assert(oldChild == null || Widget.canUpdate(oldChild.widget, newWidget));
|
4150 | final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
|
4151 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
4152 | assert(oldChild == newChild || oldChild == null || oldChild._lifecycleState != _ElementLifecycle.active);
|
4153 | newChildren[newChildrenTop] = newChild;
|
4154 | previousChild = newChild;
|
4155 | newChildrenTop += 1;
|
4156 | }
|
4157 |
|
4158 | // We've scanned the whole list.
|
4159 | assert(oldChildrenTop == oldChildrenBottom + 1);
|
4160 | assert(newChildrenTop == newChildrenBottom + 1);
|
4161 | assert(newWidgets.length - newChildrenTop == oldChildren.length - oldChildrenTop);
|
4162 | newChildrenBottom = newWidgets.length - 1;
|
4163 | oldChildrenBottom = oldChildren.length - 1;
|
4164 |
|
4165 | // Update the bottom of the list.
|
4166 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
|
4167 | final Element oldChild = oldChildren[oldChildrenTop];
|
4168 | assert(replaceWithNullIfForgotten(oldChild) != null);
|
4169 | assert(oldChild._lifecycleState == _ElementLifecycle.active);
|
4170 | final Widget newWidget = newWidgets[newChildrenTop];
|
4171 | assert(Widget.canUpdate(oldChild.widget, newWidget));
|
4172 | final Element newChild = updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!;
|
4173 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
4174 | assert(oldChild == newChild || oldChild._lifecycleState != _ElementLifecycle.active);
|
4175 | newChildren[newChildrenTop] = newChild;
|
4176 | previousChild = newChild;
|
4177 | newChildrenTop += 1;
|
4178 | oldChildrenTop += 1;
|
4179 | }
|
4180 |
|
4181 | // Clean up any of the remaining middle nodes from the old list.
|
4182 | if (haveOldChildren && oldKeyedChildren!.isNotEmpty) {
|
4183 | for (final Element oldChild in oldKeyedChildren.values) {
|
4184 | if (forgottenChildren == null || !forgottenChildren.contains(oldChild)) {
|
4185 | deactivateChild(oldChild);
|
4186 | }
|
4187 | }
|
4188 | }
|
4189 | assert(newChildren.every((Element element) => element is! _NullElement));
|
4190 | return newChildren;
|
4191 | }
|
4192 |
|
4193 | /// Add this element to the tree in the given slot of the given parent.
|
4194 | ///
|
4195 | /// The framework calls this function when a newly created element is added to
|
4196 | /// the tree for the first time. Use this method to initialize state that
|
4197 | /// depends on having a parent. State that is independent of the parent can
|
4198 | /// more easily be initialized in the constructor.
|
4199 | ///
|
4200 | /// This method transitions the element from the "initial" lifecycle state to
|
4201 | /// the "active" lifecycle state.
|
4202 | ///
|
4203 | /// Subclasses that override this method are likely to want to also override
|
4204 | /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
|
4205 | /// [RenderObjectElement.moveRenderObjectChild], and
|
4206 | /// [RenderObjectElement.removeRenderObjectChild].
|
4207 | ///
|
4208 | /// Implementations of this method should start with a call to the inherited
|
4209 | /// method, as in `super.mount(parent, newSlot)`.
|
4210 | @mustCallSuper
|
4211 | void mount(Element? parent, Object? newSlot) {
|
4212 | assert(
|
4213 | _lifecycleState == _ElementLifecycle.initial,
|
4214 | 'This element is no longer in its initial state ( ${_lifecycleState.name})' ,
|
4215 | );
|
4216 | assert(
|
4217 | _parent == null,
|
4218 | "This element already has a parent ( $_parent) and it shouldn't have one yet." ,
|
4219 | );
|
4220 | assert(
|
4221 | parent == null || parent._lifecycleState == _ElementLifecycle.active,
|
4222 | 'Parent ( $parent) should be null or in the active state ( ${parent._lifecycleState.name})' ,
|
4223 | );
|
4224 | assert(
|
4225 | slot == null,
|
4226 | "This element already has a slot ( $slot) and it shouldn't" ,
|
4227 | );
|
4228 | _parent = parent;
|
4229 | _slot = newSlot;
|
4230 | _lifecycleState = _ElementLifecycle.active;
|
4231 | _depth = 1 + (_parent?.depth ?? 0);
|
4232 | if (parent != null) {
|
4233 | // Only assign ownership if the parent is non-null. If parent is null
|
4234 | // (the root node), the owner should have already been assigned.
|
4235 | // See RootRenderObjectElement.assignOwner().
|
4236 | _owner = parent.owner;
|
4237 | _parentBuildScope = parent.buildScope;
|
4238 | }
|
4239 | assert(owner != null);
|
4240 | final Key? key = widget.key;
|
4241 | if (key is GlobalKey) {
|
4242 | owner!._registerGlobalKey(key, this);
|
4243 | }
|
4244 | _updateInheritance();
|
4245 | attachNotificationTree();
|
4246 | }
|
4247 |
|
4248 | void _debugRemoveGlobalKeyReservation(Element child) {
|
4249 | assert(owner != null);
|
4250 | owner!._debugRemoveGlobalKeyReservationFor(this, child);
|
4251 | }
|
4252 |
|
4253 | /// Change the widget used to configure this element.
|
4254 | ///
|
4255 | /// The framework calls this function when the parent wishes to use a
|
4256 | /// different widget to configure this element. The new widget is guaranteed
|
4257 | /// to have the same [runtimeType] as the old widget.
|
4258 | ///
|
4259 | /// This function is called only during the "active" lifecycle state.
|
4260 | @mustCallSuper
|
4261 | void update(covariant Widget newWidget) {
|
4262 | // This code is hot when hot reloading, so we try to
|
4263 | // only call _AssertionError._evaluateAssertion once.
|
4264 | assert(
|
4265 | _lifecycleState == _ElementLifecycle.active
|
4266 | && newWidget != widget
|
4267 | && Widget.canUpdate(widget, newWidget),
|
4268 | );
|
4269 | // This Element was told to update and we can now release all the global key
|
4270 | // reservations of forgotten children. We cannot do this earlier because the
|
4271 | // forgotten children still represent global key duplications if the element
|
4272 | // never updates (the forgotten children are not removed from the tree
|
4273 | // until the call to update happens)
|
4274 | assert(() {
|
4275 | _debugForgottenChildrenWithGlobalKey?.forEach(_debugRemoveGlobalKeyReservation);
|
4276 | _debugForgottenChildrenWithGlobalKey?.clear();
|
4277 | return true;
|
4278 | }());
|
4279 | _widget = newWidget;
|
4280 | }
|
4281 |
|
4282 | /// Change the slot that the given child occupies in its parent.
|
4283 | ///
|
4284 | /// Called by [MultiChildRenderObjectElement], and other [RenderObjectElement]
|
4285 | /// subclasses that have multiple children, when child moves from one position
|
4286 | /// to another in this element's child list.
|
4287 | @protected
|
4288 | void updateSlotForChild(Element child, Object? newSlot) {
|
4289 | assert(_lifecycleState == _ElementLifecycle.active);
|
4290 | assert(child._parent == this);
|
4291 | void visit(Element element) {
|
4292 | element.updateSlot(newSlot);
|
4293 | final Element? descendant = element.renderObjectAttachingChild;
|
4294 | if (descendant != null) {
|
4295 | visit(descendant);
|
4296 | }
|
4297 | }
|
4298 | visit(child);
|
4299 | }
|
4300 |
|
4301 | /// Called by [updateSlotForChild] when the framework needs to change the slot
|
4302 | /// that this [Element] occupies in its ancestor.
|
4303 | @protected
|
4304 | @mustCallSuper
|
4305 | void updateSlot(Object? newSlot) {
|
4306 | assert(_lifecycleState == _ElementLifecycle.active);
|
4307 | assert(_parent != null);
|
4308 | assert(_parent!._lifecycleState == _ElementLifecycle.active);
|
4309 | _slot = newSlot;
|
4310 | }
|
4311 |
|
4312 | void _updateDepth(int parentDepth) {
|
4313 | final int expectedDepth = parentDepth + 1;
|
4314 | if (_depth < expectedDepth) {
|
4315 | _depth = expectedDepth;
|
4316 | visitChildren((Element child) {
|
4317 | child._updateDepth(expectedDepth);
|
4318 | });
|
4319 | }
|
4320 | }
|
4321 |
|
4322 | void _updateBuildScopeRecursively() {
|
4323 | if (identical(buildScope, _parent?.buildScope)) {
|
4324 | return;
|
4325 | }
|
4326 | // Unset the _inDirtyList flag so this Element can be added to the dirty list
|
4327 | // of the new build scope if it's dirty.
|
4328 | _inDirtyList = false;
|
4329 | _parentBuildScope = _parent?.buildScope;
|
4330 | visitChildren((Element child) {
|
4331 | child._updateBuildScopeRecursively();
|
4332 | });
|
4333 | }
|
4334 |
|
4335 | /// Remove [renderObject] from the render tree.
|
4336 | ///
|
4337 | /// The default implementation of this function calls
|
4338 | /// [detachRenderObject] recursively on each child. The
|
4339 | /// [RenderObjectElement.detachRenderObject] override does the actual work of
|
4340 | /// removing [renderObject] from the render tree.
|
4341 | ///
|
4342 | /// This is called by [deactivateChild].
|
4343 | void detachRenderObject() {
|
4344 | visitChildren((Element child) {
|
4345 | child.detachRenderObject();
|
4346 | });
|
4347 | _slot = null;
|
4348 | }
|
4349 |
|
4350 | /// Add [renderObject] to the render tree at the location specified by `newSlot`.
|
4351 | ///
|
4352 | /// The default implementation of this function calls
|
4353 | /// [attachRenderObject] recursively on each child. The
|
4354 | /// [RenderObjectElement.attachRenderObject] override does the actual work of
|
4355 | /// adding [renderObject] to the render tree.
|
4356 | ///
|
4357 | /// The `newSlot` argument specifies the new value for this element's [slot].
|
4358 | void attachRenderObject(Object? newSlot) {
|
4359 | assert(slot == null);
|
4360 | visitChildren((Element child) {
|
4361 | child.attachRenderObject(newSlot);
|
4362 | });
|
4363 | _slot = newSlot;
|
4364 | }
|
4365 |
|
4366 | Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) {
|
4367 | // The "inactivity" of the element being retaken here may be forward-looking: if
|
4368 | // we are taking an element with a GlobalKey from an element that currently has
|
4369 | // it as a child, then we know that element will soon no longer have that
|
4370 | // element as a child. The only way that assumption could be false is if the
|
4371 | // global key is being duplicated, and we'll try to track that using the
|
4372 | // _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans call below.
|
4373 | final Element? element = key._currentElement;
|
4374 | if (element == null) {
|
4375 | return null;
|
4376 | }
|
4377 | if (!Widget.canUpdate(element.widget, newWidget)) {
|
4378 | return null;
|
4379 | }
|
4380 | assert(() {
|
4381 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
4382 | debugPrint('Attempting to take $element from ${element._parent ?? "inactive elements list" } to put in $this.' );
|
4383 | }
|
4384 | return true;
|
4385 | }());
|
4386 | final Element? parent = element._parent;
|
4387 | if (parent != null) {
|
4388 | assert(() {
|
4389 | if (parent == this) {
|
4390 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4391 | ErrorSummary("A GlobalKey was used multiple times inside one widget's child list." ),
|
4392 | DiagnosticsProperty<GlobalKey>('The offending GlobalKey was' , key),
|
4393 | parent.describeElement('The parent of the widgets with that key was' ),
|
4394 | element.describeElement('The first child to get instantiated with that key became' ),
|
4395 | DiagnosticsProperty<Widget>('The second child that was to be instantiated with that key was' , widget, style: DiagnosticsTreeStyle.errorProperty),
|
4396 | ErrorDescription('A GlobalKey can only be specified on one widget at a time in the widget tree.' ),
|
4397 | ]);
|
4398 | }
|
4399 | parent.owner!._debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans(
|
4400 | parent,
|
4401 | key,
|
4402 | );
|
4403 | return true;
|
4404 | }());
|
4405 | parent.forgetChild(element);
|
4406 | parent.deactivateChild(element);
|
4407 | }
|
4408 | assert(element._parent == null);
|
4409 | owner!._inactiveElements.remove(element);
|
4410 | return element;
|
4411 | }
|
4412 |
|
4413 | /// Create an element for the given widget and add it as a child of this
|
4414 | /// element in the given slot.
|
4415 | ///
|
4416 | /// This method is typically called by [updateChild] but can be called
|
4417 | /// directly by subclasses that need finer-grained control over creating
|
4418 | /// elements.
|
4419 | ///
|
4420 | /// If the given widget has a global key and an element already exists that
|
4421 | /// has a widget with that global key, this function will reuse that element
|
4422 | /// (potentially grafting it from another location in the tree or reactivating
|
4423 | /// it from the list of inactive elements) rather than creating a new element.
|
4424 | ///
|
4425 | /// The `newSlot` argument specifies the new value for this element's [slot].
|
4426 | ///
|
4427 | /// The element returned by this function will already have been mounted and
|
4428 | /// will be in the "active" lifecycle state.
|
4429 | @protected
|
4430 | @pragma('dart2js:tryInline' )
|
4431 | @pragma('vm:prefer-inline' )
|
4432 | @pragma('wasm:prefer-inline' )
|
4433 | Element inflateWidget(Widget newWidget, Object? newSlot) {
|
4434 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
|
4435 | if (isTimelineTracked) {
|
4436 | Map<String, String>? debugTimelineArguments;
|
4437 | assert(() {
|
4438 | if (kDebugMode && debugEnhanceBuildTimelineArguments) {
|
4439 | debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments();
|
4440 | }
|
4441 | return true;
|
4442 | }());
|
4443 | FlutterTimeline.startSync(
|
4444 | ' ${newWidget.runtimeType}' ,
|
4445 | arguments: debugTimelineArguments,
|
4446 | );
|
4447 | }
|
4448 |
|
4449 | try {
|
4450 | final Key? key = newWidget.key;
|
4451 | if (key is GlobalKey) {
|
4452 | final Element? newChild = _retakeInactiveElement(key, newWidget);
|
4453 | if (newChild != null) {
|
4454 | assert(newChild._parent == null);
|
4455 | assert(() {
|
4456 | _debugCheckForCycles(newChild);
|
4457 | return true;
|
4458 | }());
|
4459 | try {
|
4460 | newChild._activateWithParent(this, newSlot);
|
4461 | } catch (_) {
|
4462 | // Attempt to do some clean-up if activation fails to leave tree in a reasonable state.
|
4463 | try {
|
4464 | deactivateChild(newChild);
|
4465 | } catch (_) {
|
4466 | // Clean-up failed. Only surface original exception.
|
4467 | }
|
4468 | rethrow;
|
4469 | }
|
4470 | final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
|
4471 | assert(newChild == updatedChild);
|
4472 | return updatedChild!;
|
4473 | }
|
4474 | }
|
4475 | final Element newChild = newWidget.createElement();
|
4476 | assert(() {
|
4477 | _debugCheckForCycles(newChild);
|
4478 | return true;
|
4479 | }());
|
4480 | newChild.mount(this, newSlot);
|
4481 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
4482 |
|
4483 | return newChild;
|
4484 | } finally {
|
4485 | if (isTimelineTracked) {
|
4486 | FlutterTimeline.finishSync();
|
4487 | }
|
4488 | }
|
4489 | }
|
4490 |
|
4491 | void _debugCheckForCycles(Element newChild) {
|
4492 | assert(newChild._parent == null);
|
4493 | assert(() {
|
4494 | Element node = this;
|
4495 | while (node._parent != null) {
|
4496 | node = node._parent!;
|
4497 | }
|
4498 | assert(node != newChild); // indicates we are about to create a cycle
|
4499 | return true;
|
4500 | }());
|
4501 | }
|
4502 |
|
4503 | /// Move the given element to the list of inactive elements and detach its
|
4504 | /// render object from the render tree.
|
4505 | ///
|
4506 | /// This method stops the given element from being a child of this element by
|
4507 | /// detaching its render object from the render tree and moving the element to
|
4508 | /// the list of inactive elements.
|
4509 | ///
|
4510 | /// This method (indirectly) calls [deactivate] on the child.
|
4511 | ///
|
4512 | /// The caller is responsible for removing the child from its child model.
|
4513 | /// Typically [deactivateChild] is called by the element itself while it is
|
4514 | /// updating its child model; however, during [GlobalKey] reparenting, the new
|
4515 | /// parent proactively calls the old parent's [deactivateChild], first using
|
4516 | /// [forgetChild] to cause the old parent to update its child model.
|
4517 | @protected
|
4518 | void deactivateChild(Element child) {
|
4519 | assert(child._parent == this);
|
4520 | child._parent = null;
|
4521 | child.detachRenderObject();
|
4522 | owner!._inactiveElements.add(child); // this eventually calls child.deactivate()
|
4523 | assert(() {
|
4524 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
4525 | if (child.widget.key is GlobalKey) {
|
4526 | debugPrint('Deactivated $child (keyed child of $this)' );
|
4527 | }
|
4528 | }
|
4529 | return true;
|
4530 | }());
|
4531 | }
|
4532 |
|
4533 | // The children that have been forgotten by forgetChild. This will be used in
|
4534 | // [update] to remove the global key reservations of forgotten children.
|
4535 | //
|
4536 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can
|
4537 | // eliminate unused fields, but not their initializers.
|
4538 | @_debugOnly
|
4539 | final Set<Element>? _debugForgottenChildrenWithGlobalKey = kDebugMode ? HashSet<Element>() : null;
|
4540 |
|
4541 | /// Remove the given child from the element's child list, in preparation for
|
4542 | /// the child being reused elsewhere in the element tree.
|
4543 | ///
|
4544 | /// This updates the child model such that, e.g., [visitChildren] does not
|
4545 | /// walk that child anymore.
|
4546 | ///
|
4547 | /// The element will still have a valid parent when this is called, and the
|
4548 | /// child's [Element.slot] value will be valid in the context of that parent.
|
4549 | /// After this is called, [deactivateChild] is called to sever the link to
|
4550 | /// this object.
|
4551 | ///
|
4552 | /// The [update] is responsible for updating or creating the new child that
|
4553 | /// will replace this [child].
|
4554 | @protected
|
4555 | @mustCallSuper
|
4556 | void forgetChild(Element child) {
|
4557 | // This method is called on the old parent when the given child (with a
|
4558 | // global key) is given a new parent. We cannot remove the global key
|
4559 | // reservation directly in this method because the forgotten child is not
|
4560 | // removed from the tree until this Element is updated in [update]. If
|
4561 | // [update] is never called, the forgotten child still represents a global
|
4562 | // key duplication that we need to catch.
|
4563 | assert(() {
|
4564 | if (child.widget.key is GlobalKey) {
|
4565 | _debugForgottenChildrenWithGlobalKey?.add(child);
|
4566 | }
|
4567 | return true;
|
4568 | }());
|
4569 | }
|
4570 |
|
4571 | void _activateWithParent(Element parent, Object? newSlot) {
|
4572 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
4573 | _parent = parent;
|
4574 | _owner = parent.owner;
|
4575 | assert(() {
|
4576 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
4577 | debugPrint('Reactivating $this (now child of $_parent).' );
|
4578 | }
|
4579 | return true;
|
4580 | }());
|
4581 | _updateDepth(_parent!.depth);
|
4582 | _updateBuildScopeRecursively();
|
4583 | _activateRecursively(this);
|
4584 | attachRenderObject(newSlot);
|
4585 | assert(_lifecycleState == _ElementLifecycle.active);
|
4586 | }
|
4587 |
|
4588 | static void _activateRecursively(Element element) {
|
4589 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
4590 | element.activate();
|
4591 | assert(element._lifecycleState == _ElementLifecycle.active);
|
4592 | element.visitChildren(_activateRecursively);
|
4593 | }
|
4594 |
|
4595 | /// Transition from the "inactive" to the "active" lifecycle state.
|
4596 | ///
|
4597 | /// The framework calls this method when a previously deactivated element has
|
4598 | /// been reincorporated into the tree. The framework does not call this method
|
4599 | /// the first time an element becomes active (i.e., from the "initial"
|
4600 | /// lifecycle state). Instead, the framework calls [mount] in that situation.
|
4601 | ///
|
4602 | /// See the lifecycle documentation for [Element] for additional information.
|
4603 | ///
|
4604 | /// Implementations of this method should start with a call to the inherited
|
4605 | /// method, as in `super.activate()`.
|
4606 | @mustCallSuper
|
4607 | void activate() {
|
4608 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
4609 | assert(owner != null);
|
4610 | final bool hadDependencies = (_dependencies?.isNotEmpty ?? false) || _hadUnsatisfiedDependencies;
|
4611 | _lifecycleState = _ElementLifecycle.active;
|
4612 | // We unregistered our dependencies in deactivate, but never cleared the list.
|
4613 | // Since we're going to be reused, let's clear our list now.
|
4614 | _dependencies?.clear();
|
4615 | _hadUnsatisfiedDependencies = false;
|
4616 | _updateInheritance();
|
4617 | attachNotificationTree();
|
4618 | if (_dirty) {
|
4619 | owner!.scheduleBuildFor(this);
|
4620 | }
|
4621 | if (hadDependencies) {
|
4622 | didChangeDependencies();
|
4623 | }
|
4624 | }
|
4625 |
|
4626 | /// Transition from the "active" to the "inactive" lifecycle state.
|
4627 | ///
|
4628 | /// The framework calls this method when a previously active element is moved
|
4629 | /// to the list of inactive elements. While in the inactive state, the element
|
4630 | /// will not appear on screen. The element can remain in the inactive state
|
4631 | /// only until the end of the current animation frame. At the end of the
|
4632 | /// animation frame, if the element has not be reactivated, the framework will
|
4633 | /// unmount the element.
|
4634 | ///
|
4635 | /// This is (indirectly) called by [deactivateChild].
|
4636 | ///
|
4637 | /// See the lifecycle documentation for [Element] for additional information.
|
4638 | ///
|
4639 | /// Implementations of this method should end with a call to the inherited
|
4640 | /// method, as in `super.deactivate()`.
|
4641 | @mustCallSuper
|
4642 | void deactivate() {
|
4643 | assert(_lifecycleState == _ElementLifecycle.active);
|
4644 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
|
4645 | if (_dependencies?.isNotEmpty ?? false) {
|
4646 | for (final InheritedElement dependency in _dependencies!) {
|
4647 | dependency.removeDependent(this);
|
4648 | }
|
4649 | // For expediency, we don't actually clear the list here, even though it's
|
4650 | // no longer representative of what we are registered with. If we never
|
4651 | // get re-used, it doesn't matter. If we do, then we'll clear the list in
|
4652 | // activate(). The benefit of this is that it allows Element's activate()
|
4653 | // implementation to decide whether to rebuild based on whether we had
|
4654 | // dependencies here.
|
4655 | }
|
4656 | _inheritedElements = null;
|
4657 | _lifecycleState = _ElementLifecycle.inactive;
|
4658 | }
|
4659 |
|
4660 | /// Called, in debug mode, after children have been deactivated (see [deactivate]).
|
4661 | ///
|
4662 | /// This method is not called in release builds.
|
4663 | @mustCallSuper
|
4664 | void debugDeactivated() {
|
4665 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
4666 | }
|
4667 |
|
4668 | /// Transition from the "inactive" to the "defunct" lifecycle state.
|
4669 | ///
|
4670 | /// Called when the framework determines that an inactive element will never
|
4671 | /// be reactivated. At the end of each animation frame, the framework calls
|
4672 | /// [unmount] on any remaining inactive elements, preventing inactive elements
|
4673 | /// from remaining inactive for longer than a single animation frame.
|
4674 | ///
|
4675 | /// After this function is called, the element will not be incorporated into
|
4676 | /// the tree again.
|
4677 | ///
|
4678 | /// Any resources this element holds should be released at this point. For
|
4679 | /// example, [RenderObjectElement.unmount] calls [RenderObject.dispose] and
|
4680 | /// nulls out its reference to the render object.
|
4681 | ///
|
4682 | /// See the lifecycle documentation for [Element] for additional information.
|
4683 | ///
|
4684 | /// Implementations of this method should end with a call to the inherited
|
4685 | /// method, as in `super.unmount()`.
|
4686 | @mustCallSuper
|
4687 | void unmount() {
|
4688 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
4689 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
|
4690 | assert(owner != null);
|
4691 | if (kFlutterMemoryAllocationsEnabled) {
|
4692 | FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
4693 | }
|
4694 | // Use the private property to avoid a CastError during hot reload.
|
4695 | final Key? key = _widget?.key;
|
4696 | if (key is GlobalKey) {
|
4697 | owner!._unregisterGlobalKey(key, this);
|
4698 | }
|
4699 | // Release resources to reduce the severity of memory leaks caused by
|
4700 | // defunct, but accidentally retained Elements.
|
4701 | _widget = null;
|
4702 | _dependencies = null;
|
4703 | _lifecycleState = _ElementLifecycle.defunct;
|
4704 | }
|
4705 |
|
4706 | /// Whether the child in the provided `slot` (or one of its descendants) must
|
4707 | /// insert a [RenderObject] into its ancestor [RenderObjectElement] by calling
|
4708 | /// [RenderObjectElement.insertRenderObjectChild] on it.
|
4709 | ///
|
4710 | /// This method is used to define non-rendering zones in the element tree (see
|
4711 | /// [WidgetsBinding] for an explanation of rendering and non-rendering zones):
|
4712 | ///
|
4713 | /// Most branches of the [Element] tree are expected to eventually insert a
|
4714 | /// [RenderObject] into their [RenderObjectElement] ancestor to construct the
|
4715 | /// render tree. However, there is a notable exception: an [Element] may
|
4716 | /// expect that the occupant of a certain child slot creates a new independent
|
4717 | /// render tree and therefore is not allowed to insert a render object into
|
4718 | /// the existing render tree. Those elements must return false from this
|
4719 | /// method for the slot in question to signal to the child in that slot that
|
4720 | /// it must not call [RenderObjectElement.insertRenderObjectChild] on its
|
4721 | /// ancestor.
|
4722 | ///
|
4723 | /// As an example, the element backing the [ViewAnchor] returns false from
|
4724 | /// this method for the [ViewAnchor.view] slot to enforce that it is occupied
|
4725 | /// by e.g. a [View] widget, which will ultimately bootstrap a separate
|
4726 | /// render tree for that view. Another example is the [ViewCollection] widget,
|
4727 | /// which returns false for all its slots for the same reason.
|
4728 | ///
|
4729 | /// Overriding this method is not common, as elements behaving in the way
|
4730 | /// described above are rare.
|
4731 | bool debugExpectsRenderObjectForSlot(Object? slot) => true;
|
4732 |
|
4733 | @override
|
4734 | RenderObject? findRenderObject() {
|
4735 | assert(() {
|
4736 | if (_lifecycleState != _ElementLifecycle.active) {
|
4737 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4738 | ErrorSummary('Cannot get renderObject of inactive element.' ),
|
4739 | ErrorDescription(
|
4740 | 'In order for an element to have a valid renderObject, it must be '
|
4741 | 'active, which means it is part of the tree.\n'
|
4742 | 'Instead, this element is in the $_lifecycleState state.\n'
|
4743 | 'If you called this method from a State object, consider guarding '
|
4744 | 'it with State.mounted.' ,
|
4745 | ),
|
4746 | describeElement('The findRenderObject() method was called for the following element' ),
|
4747 | ]);
|
4748 | }
|
4749 | return true;
|
4750 | }());
|
4751 | return renderObject;
|
4752 | }
|
4753 |
|
4754 | @override
|
4755 | Size? get size {
|
4756 | assert(() {
|
4757 | if (_lifecycleState != _ElementLifecycle.active) {
|
4758 | // TODO(jacobr): is this a good separation into contract and violation?
|
4759 | // I have added a line of white space.
|
4760 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4761 | ErrorSummary('Cannot get size of inactive element.' ),
|
4762 | ErrorDescription(
|
4763 | 'In order for an element to have a valid size, the element must be '
|
4764 | 'active, which means it is part of the tree.\n'
|
4765 | 'Instead, this element is in the $_lifecycleState state.' ,
|
4766 | ),
|
4767 | describeElement('The size getter was called for the following element' ),
|
4768 | ]);
|
4769 | }
|
4770 | if (owner!._debugBuilding) {
|
4771 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4772 | ErrorSummary('Cannot get size during build.' ),
|
4773 | ErrorDescription(
|
4774 | 'The size of this render object has not yet been determined because '
|
4775 | 'the framework is still in the process of building widgets, which '
|
4776 | 'means the render tree for this frame has not yet been determined. '
|
4777 | 'The size getter should only be called from paint callbacks or '
|
4778 | 'interaction event handlers (e.g. gesture callbacks).' ,
|
4779 | ),
|
4780 | ErrorSpacer(),
|
4781 | ErrorHint(
|
4782 | 'If you need some sizing information during build to decide which '
|
4783 | 'widgets to build, consider using a LayoutBuilder widget, which can '
|
4784 | 'tell you the layout constraints at a given location in the tree. See '
|
4785 | '<https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html> '
|
4786 | 'for more details.' ,
|
4787 | ),
|
4788 | ErrorSpacer(),
|
4789 | describeElement('The size getter was called for the following element' ),
|
4790 | ]);
|
4791 | }
|
4792 | return true;
|
4793 | }());
|
4794 | final RenderObject? renderObject = findRenderObject();
|
4795 | assert(() {
|
4796 | if (renderObject == null) {
|
4797 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4798 | ErrorSummary('Cannot get size without a render object.' ),
|
4799 | ErrorHint(
|
4800 | 'In order for an element to have a valid size, the element must have '
|
4801 | 'an associated render object. This element does not have an associated '
|
4802 | 'render object, which typically means that the size getter was called '
|
4803 | 'too early in the pipeline (e.g., during the build phase) before the '
|
4804 | 'framework has created the render tree.' ,
|
4805 | ),
|
4806 | describeElement('The size getter was called for the following element' ),
|
4807 | ]);
|
4808 | }
|
4809 | if (renderObject is RenderSliver) {
|
4810 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4811 | ErrorSummary('Cannot get size from a RenderSliver.' ),
|
4812 | ErrorHint(
|
4813 | 'The render object associated with this element is a '
|
4814 | ' ${renderObject.runtimeType}, which is a subtype of RenderSliver. '
|
4815 | 'Slivers do not have a size per se. They have a more elaborate '
|
4816 | 'geometry description, which can be accessed by calling '
|
4817 | 'findRenderObject and then using the "geometry" getter on the '
|
4818 | 'resulting object.' ,
|
4819 | ),
|
4820 | describeElement('The size getter was called for the following element' ),
|
4821 | renderObject.describeForError('The associated render sliver was' ),
|
4822 | ]);
|
4823 | }
|
4824 | if (renderObject is! RenderBox) {
|
4825 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4826 | ErrorSummary('Cannot get size from a render object that is not a RenderBox.' ),
|
4827 | ErrorHint(
|
4828 | 'Instead of being a subtype of RenderBox, the render object associated '
|
4829 | 'with this element is a ${renderObject.runtimeType}. If this type of '
|
4830 | 'render object does have a size, consider calling findRenderObject '
|
4831 | 'and extracting its size manually.' ,
|
4832 | ),
|
4833 | describeElement('The size getter was called for the following element' ),
|
4834 | renderObject.describeForError('The associated render object was' ),
|
4835 | ]);
|
4836 | }
|
4837 | final RenderBox box = renderObject;
|
4838 | if (!box.hasSize) {
|
4839 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4840 | ErrorSummary('Cannot get size from a render object that has not been through layout.' ),
|
4841 | ErrorHint(
|
4842 | 'The size of this render object has not yet been determined because '
|
4843 | 'this render object has not yet been through layout, which typically '
|
4844 | 'means that the size getter was called too early in the pipeline '
|
4845 | '(e.g., during the build phase) before the framework has determined '
|
4846 | 'the size and position of the render objects during layout.' ,
|
4847 | ),
|
4848 | describeElement('The size getter was called for the following element' ),
|
4849 | box.describeForError('The render object from which the size was to be obtained was' ),
|
4850 | ]);
|
4851 | }
|
4852 | if (box.debugNeedsLayout) {
|
4853 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4854 | ErrorSummary('Cannot get size from a render object that has been marked dirty for layout.' ),
|
4855 | ErrorHint(
|
4856 | 'The size of this render object is ambiguous because this render object has '
|
4857 | 'been modified since it was last laid out, which typically means that the size '
|
4858 | 'getter was called too early in the pipeline (e.g., during the build phase) '
|
4859 | 'before the framework has determined the size and position of the render '
|
4860 | 'objects during layout.' ,
|
4861 | ),
|
4862 | describeElement('The size getter was called for the following element' ),
|
4863 | box.describeForError('The render object from which the size was to be obtained was' ),
|
4864 | ErrorHint(
|
4865 | 'Consider using debugPrintMarkNeedsLayoutStacks to determine why the render '
|
4866 | 'object in question is dirty, if you did not expect this.' ,
|
4867 | ),
|
4868 | ]);
|
4869 | }
|
4870 | return true;
|
4871 | }());
|
4872 | if (renderObject is RenderBox) {
|
4873 | return renderObject.size;
|
4874 | }
|
4875 | return null;
|
4876 | }
|
4877 |
|
4878 | PersistentHashMap<Type, InheritedElement>? _inheritedElements;
|
4879 | Set<InheritedElement>? _dependencies;
|
4880 | bool _hadUnsatisfiedDependencies = false;
|
4881 |
|
4882 | bool _debugCheckStateIsActiveForAncestorLookup() {
|
4883 | assert(() {
|
4884 | if (_lifecycleState != _ElementLifecycle.active) {
|
4885 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
4886 | ErrorSummary("Looking up a deactivated widget's ancestor is unsafe." ),
|
4887 | ErrorDescription(
|
4888 | "At this point the state of the widget's element tree is no longer "
|
4889 | 'stable.' ,
|
4890 | ),
|
4891 | ErrorHint(
|
4892 | "To safely refer to a widget's ancestor in its dispose() method, "
|
4893 | 'save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() '
|
4894 | "in the widget's didChangeDependencies() method." ,
|
4895 | ),
|
4896 | ]);
|
4897 | }
|
4898 | return true;
|
4899 | }());
|
4900 | return true;
|
4901 | }
|
4902 |
|
4903 | /// Returns `true` if [dependOnInheritedElement] was previously called with [ancestor].
|
4904 | @protected
|
4905 | bool doesDependOnInheritedElement(InheritedElement ancestor) {
|
4906 | return _dependencies?.contains(ancestor) ?? false;
|
4907 | }
|
4908 |
|
4909 | @override
|
4910 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
|
4911 | _dependencies ??= HashSet<InheritedElement>();
|
4912 | _dependencies!.add(ancestor);
|
4913 | ancestor.updateDependencies(this, aspect);
|
4914 | return ancestor.widget as InheritedWidget;
|
4915 | }
|
4916 |
|
4917 | @override
|
4918 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
|
4919 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4920 | final InheritedElement? ancestor = _inheritedElements?[T];
|
4921 | if (ancestor != null) {
|
4922 | return dependOnInheritedElement(ancestor, aspect: aspect) as T;
|
4923 | }
|
4924 | _hadUnsatisfiedDependencies = true;
|
4925 | return null;
|
4926 | }
|
4927 |
|
4928 | @override
|
4929 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>() {
|
4930 | return getElementForInheritedWidgetOfExactType<T>()?.widget as T?;
|
4931 | }
|
4932 |
|
4933 | @override
|
4934 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
|
4935 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4936 | return _inheritedElements?[T];
|
4937 | }
|
4938 |
|
4939 | /// Called in [Element.mount] and [Element.activate] to register this element in
|
4940 | /// the notification tree.
|
4941 | ///
|
4942 | /// This method is only exposed so that [NotifiableElementMixin] can be implemented.
|
4943 | /// Subclasses of [Element] that wish to respond to notifications should mix that
|
4944 | /// in instead.
|
4945 | ///
|
4946 | /// See also:
|
4947 | /// * [NotificationListener], a widget that allows listening to notifications.
|
4948 | @protected
|
4949 | void attachNotificationTree() {
|
4950 | _notificationTree = _parent?._notificationTree;
|
4951 | }
|
4952 |
|
4953 | void _updateInheritance() {
|
4954 | assert(_lifecycleState == _ElementLifecycle.active);
|
4955 | _inheritedElements = _parent?._inheritedElements;
|
4956 | }
|
4957 |
|
4958 | @override
|
4959 | T? findAncestorWidgetOfExactType<T extends Widget>() {
|
4960 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4961 | Element? ancestor = _parent;
|
4962 | while (ancestor != null && ancestor.widget.runtimeType != T) {
|
4963 | ancestor = ancestor._parent;
|
4964 | }
|
4965 | return ancestor?.widget as T?;
|
4966 | }
|
4967 |
|
4968 | @override
|
4969 | T? findAncestorStateOfType<T extends State<StatefulWidget>>() {
|
4970 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4971 | Element? ancestor = _parent;
|
4972 | while (ancestor != null) {
|
4973 | if (ancestor is StatefulElement && ancestor.state is T) {
|
4974 | break;
|
4975 | }
|
4976 | ancestor = ancestor._parent;
|
4977 | }
|
4978 | final StatefulElement? statefulAncestor = ancestor as StatefulElement?;
|
4979 | return statefulAncestor?.state as T?;
|
4980 | }
|
4981 |
|
4982 | @override
|
4983 | T? findRootAncestorStateOfType<T extends State<StatefulWidget>>() {
|
4984 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4985 | Element? ancestor = _parent;
|
4986 | StatefulElement? statefulAncestor;
|
4987 | while (ancestor != null) {
|
4988 | if (ancestor is StatefulElement && ancestor.state is T) {
|
4989 | statefulAncestor = ancestor;
|
4990 | }
|
4991 | ancestor = ancestor._parent;
|
4992 | }
|
4993 | return statefulAncestor?.state as T?;
|
4994 | }
|
4995 |
|
4996 | @override
|
4997 | T? findAncestorRenderObjectOfType<T extends RenderObject>() {
|
4998 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
4999 | Element? ancestor = _parent;
|
5000 | while (ancestor != null) {
|
5001 | if (ancestor is RenderObjectElement && ancestor.renderObject is T) {
|
5002 | return ancestor.renderObject as T;
|
5003 | }
|
5004 | ancestor = ancestor._parent;
|
5005 | }
|
5006 | return null;
|
5007 | }
|
5008 |
|
5009 | @override
|
5010 | void visitAncestorElements(ConditionalElementVisitor visitor) {
|
5011 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
5012 | Element? ancestor = _parent;
|
5013 | while (ancestor != null && visitor(ancestor)) {
|
5014 | ancestor = ancestor._parent;
|
5015 | }
|
5016 | }
|
5017 |
|
5018 | /// Called when a dependency of this element changes.
|
5019 | ///
|
5020 | /// The [dependOnInheritedWidgetOfExactType] registers this element as depending on
|
5021 | /// inherited information of the given type. When the information of that type
|
5022 | /// changes at this location in the tree (e.g., because the [InheritedElement]
|
5023 | /// updated to a new [InheritedWidget] and
|
5024 | /// [InheritedWidget.updateShouldNotify] returned true), the framework calls
|
5025 | /// this function to notify this element of the change.
|
5026 | @mustCallSuper
|
5027 | void didChangeDependencies() {
|
5028 | assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
|
5029 | assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies' ));
|
5030 | markNeedsBuild();
|
5031 | }
|
5032 |
|
5033 | bool _debugCheckOwnerBuildTargetExists(String methodName) {
|
5034 | assert(() {
|
5035 | if (owner!._debugCurrentBuildTarget == null) {
|
5036 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
5037 | ErrorSummary(
|
5038 | ' $methodName for ${widget.runtimeType} was called at an '
|
5039 | 'inappropriate time.' ,
|
5040 | ),
|
5041 | ErrorDescription('It may only be called while the widgets are being built.' ),
|
5042 | ErrorHint(
|
5043 | 'A possible cause of this error is when $methodName is called during '
|
5044 | 'one of:\n'
|
5045 | ' * network I/O event\n'
|
5046 | ' * file I/O event\n'
|
5047 | ' * timer\n'
|
5048 | ' * microtask (caused by Future.then, async/await, scheduleMicrotask)' ,
|
5049 | ),
|
5050 | ]);
|
5051 | }
|
5052 | return true;
|
5053 | }());
|
5054 | return true;
|
5055 | }
|
5056 |
|
5057 | /// Returns a description of what caused this element to be created.
|
5058 | ///
|
5059 | /// Useful for debugging the source of an element.
|
5060 | String debugGetCreatorChain(int limit) {
|
5061 | final List<String> chain = <String>[];
|
5062 | Element? node = this;
|
5063 | while (chain.length < limit && node != null) {
|
5064 | chain.add(node.toStringShort());
|
5065 | node = node._parent;
|
5066 | }
|
5067 | if (node != null) {
|
5068 | chain.add('\u22EF' );
|
5069 | }
|
5070 | return chain.join(' \u2190 ' );
|
5071 | }
|
5072 |
|
5073 | /// Returns the parent chain from this element back to the root of the tree.
|
5074 | ///
|
5075 | /// Useful for debug display of a tree of Elements with only nodes in the path
|
5076 | /// from the root to this Element expanded.
|
5077 | List<Element> debugGetDiagnosticChain() {
|
5078 | final List<Element> chain = <Element>[this];
|
5079 | Element? node = _parent;
|
5080 | while (node != null) {
|
5081 | chain.add(node);
|
5082 | node = node._parent;
|
5083 | }
|
5084 | return chain;
|
5085 | }
|
5086 |
|
5087 | @override
|
5088 | void dispatchNotification(Notification notification) {
|
5089 | _notificationTree?.dispatchNotification(notification);
|
5090 | }
|
5091 |
|
5092 | /// A short, textual description of this element.
|
5093 | @override
|
5094 | String toStringShort() => _widget?.toStringShort() ?? ' ${describeIdentity(this)}(DEFUNCT)' ;
|
5095 |
|
5096 | @override
|
5097 | DiagnosticsNode toDiagnosticsNode({ String? name, DiagnosticsTreeStyle? style }) {
|
5098 | return _ElementDiagnosticableTreeNode(
|
5099 | name: name,
|
5100 | value: this,
|
5101 | style: style,
|
5102 | );
|
5103 | }
|
5104 |
|
5105 | @override
|
5106 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
5107 | super.debugFillProperties(properties);
|
5108 | properties.defaultDiagnosticsTreeStyle= DiagnosticsTreeStyle.dense;
|
5109 | if (_lifecycleState != _ElementLifecycle.initial) {
|
5110 | properties.add(ObjectFlagProperty<int>('depth' , depth, ifNull: 'no depth' ));
|
5111 | }
|
5112 | properties.add(ObjectFlagProperty<Widget>('widget' , _widget, ifNull: 'no widget' ));
|
5113 | properties.add(DiagnosticsProperty<Key>('key' , _widget?.key, showName: false, defaultValue: null, level: DiagnosticLevel.hidden));
|
5114 | _widget?.debugFillProperties(properties);
|
5115 | properties.add(FlagProperty('dirty' , value: dirty, ifTrue: 'dirty' ));
|
5116 | final Set<InheritedElement>? deps = _dependencies;
|
5117 | if (deps != null && deps.isNotEmpty) {
|
5118 | final List<InheritedElement> sortedDependencies = deps.toList()
|
5119 | ..sort((InheritedElement a, InheritedElement b) =>
|
5120 | a.toStringShort().compareTo(b.toStringShort()));
|
5121 | final List<DiagnosticsNode> diagnosticsDependencies = sortedDependencies
|
5122 | .map((InheritedElement element) => element.widget.toDiagnosticsNode(style: DiagnosticsTreeStyle.sparse))
|
5123 | .toList();
|
5124 | properties.add(DiagnosticsProperty<Set<InheritedElement>>('dependencies' , deps, description: diagnosticsDependencies.toString()));
|
5125 | }
|
5126 | }
|
5127 |
|
5128 | @override
|
5129 | List<DiagnosticsNode> debugDescribeChildren() {
|
5130 | final List<DiagnosticsNode> children = <DiagnosticsNode>[];
|
5131 | visitChildren((Element child) {
|
5132 | children.add(child.toDiagnosticsNode());
|
5133 | });
|
5134 | return children;
|
5135 | }
|
5136 |
|
5137 | /// Returns true if the element has been marked as needing rebuilding.
|
5138 | ///
|
5139 | /// The flag is true when the element is first created and after
|
5140 | /// [markNeedsBuild] has been called. The flag is typically reset to false in
|
5141 | /// the [performRebuild] implementation, but certain elements (that of the
|
5142 | /// [LayoutBuilder] widget, for example) may choose to override [markNeedsBuild]
|
5143 | /// such that it does not set the [dirty] flag to `true` when called.
|
5144 | bool get dirty => _dirty;
|
5145 | bool _dirty = true;
|
5146 |
|
5147 | // Whether this is in _buildScope._dirtyElements. This is used to know whether
|
5148 | // we should be adding the element back into the list when it's reactivated.
|
5149 | bool _inDirtyList = false;
|
5150 |
|
5151 | // Whether we've already built or not. Set in [rebuild].
|
5152 | bool _debugBuiltOnce = false;
|
5153 |
|
5154 | /// Marks the element as dirty and adds it to the global list of widgets to
|
5155 | /// rebuild in the next frame.
|
5156 | ///
|
5157 | /// Since it is inefficient to build an element twice in one frame,
|
5158 | /// applications and widgets should be structured so as to only mark
|
5159 | /// widgets dirty during event handlers before the frame begins, not during
|
5160 | /// the build itself.
|
5161 | void markNeedsBuild() {
|
5162 | assert(_lifecycleState != _ElementLifecycle.defunct);
|
5163 | if (_lifecycleState != _ElementLifecycle.active) {
|
5164 | return;
|
5165 | }
|
5166 | assert(owner != null);
|
5167 | assert(_lifecycleState == _ElementLifecycle.active);
|
5168 | assert(() {
|
5169 | if (owner!._debugBuilding) {
|
5170 | assert(owner!._debugCurrentBuildTarget != null);
|
5171 | assert(owner!._debugStateLocked);
|
5172 | if (_debugIsDescsendantOf(owner!._debugCurrentBuildTarget!)) {
|
5173 | return true;
|
5174 | }
|
5175 | final List<DiagnosticsNode> information = <DiagnosticsNode>[
|
5176 | ErrorSummary('setState() or markNeedsBuild() called during build.' ),
|
5177 | ErrorDescription(
|
5178 | 'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
|
5179 | 'is already in the process of building widgets. A widget can be marked as '
|
5180 | 'needing to be built during the build phase only if one of its ancestors '
|
5181 | 'is currently building. This exception is allowed because the framework '
|
5182 | 'builds parent widgets before children, which means a dirty descendant '
|
5183 | 'will always be built. Otherwise, the framework might not visit this '
|
5184 | 'widget during this build phase.' ,
|
5185 | ),
|
5186 | describeElement('The widget on which setState() or markNeedsBuild() was called was' ),
|
5187 | ];
|
5188 | if (owner!._debugCurrentBuildTarget != null) {
|
5189 | information.add(owner!._debugCurrentBuildTarget!.describeWidget('The widget which was currently being built when the offending call was made was' ));
|
5190 | }
|
5191 | throw FlutterError.fromParts(information);
|
5192 | } else if (owner!._debugStateLocked) {
|
5193 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
5194 | ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.' ),
|
5195 | ErrorDescription(
|
5196 | 'This ${widget.runtimeType} widget cannot be marked as needing to build '
|
5197 | 'because the framework is locked.' ,
|
5198 | ),
|
5199 | describeElement('The widget on which setState() or markNeedsBuild() was called was' ),
|
5200 | ]);
|
5201 | }
|
5202 | return true;
|
5203 | }());
|
5204 | if (dirty) {
|
5205 | return;
|
5206 | }
|
5207 | _dirty = true;
|
5208 | owner!.scheduleBuildFor(this);
|
5209 | }
|
5210 |
|
5211 | /// Cause the widget to update itself. In debug builds, also verify various
|
5212 | /// invariants.
|
5213 | ///
|
5214 | /// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
|
5215 | /// called to mark this element dirty, by [mount] when the element is first
|
5216 | /// built, and by [update] when the widget has changed.
|
5217 | ///
|
5218 | /// The method will only rebuild if [dirty] is true. To rebuild regardless
|
5219 | /// of the [dirty] flag, set `force` to true. Forcing a rebuild is convenient
|
5220 | /// from [update], during which [dirty] is false.
|
5221 | ///
|
5222 | /// ## When rebuilds happen
|
5223 | ///
|
5224 | /// ### Terminology
|
5225 | ///
|
5226 | /// [Widget]s represent the configuration of [Element]s. Each [Element] has a
|
5227 | /// widget, specified in [Element.widget]. The term "widget" is often used
|
5228 | /// when strictly speaking "element" would be more correct.
|
5229 | ///
|
5230 | /// While an [Element] has a current [Widget], over time, that widget may be
|
5231 | /// replaced by others. For example, the element backing a [ColoredBox] may
|
5232 | /// first have as its widget a [ColoredBox] whose [ColoredBox.color] is blue,
|
5233 | /// then later be given a new [ColoredBox] whose color is green.
|
5234 | ///
|
5235 | /// At any particular time, multiple [Element]s in the same tree may have the
|
5236 | /// same [Widget]. For example, the same [ColoredBox] with the green color may
|
5237 | /// be used in multiple places in the widget tree at the same time, each being
|
5238 | /// backed by a different [Element].
|
5239 | ///
|
5240 | /// ### Marking an element dirty
|
5241 | ///
|
5242 | /// An [Element] can be marked dirty between frames. This can happen for various
|
5243 | /// reasons, including the following:
|
5244 | ///
|
5245 | /// * The [State] of a [StatefulWidget] can cause its [Element] to be marked
|
5246 | /// dirty by calling the [State.setState] method.
|
5247 | ///
|
5248 | /// * When an [InheritedWidget] changes, descendants that have previously
|
5249 | /// subscribed to it will be marked dirty.
|
5250 | ///
|
5251 | /// * During a hot reload, every element is marked dirty (using [Element.reassemble]).
|
5252 | ///
|
5253 | /// ### Rebuilding
|
5254 | ///
|
5255 | /// Dirty elements are rebuilt during the next frame. Precisely how this is
|
5256 | /// done depends on the kind of element. A [StatelessElement] rebuilds by
|
5257 | /// using its widget's [StatelessWidget.build] method. A [StatefulElement]
|
5258 | /// rebuilds by using its widget's state's [State.build] method. A
|
5259 | /// [RenderObjectElement] rebuilds by updating its [RenderObject].
|
5260 | ///
|
5261 | /// In many cases, the end result of rebuilding is a single child widget
|
5262 | /// or (for [MultiChildRenderObjectElement]s) a list of children widgets.
|
5263 | ///
|
5264 | /// These child widgets are used to update the [widget] property of the
|
5265 | /// element's child (or children) elements. The new [Widget] is considered to
|
5266 | /// correspond to an existing [Element] if it has the same [Type] and [Key].
|
5267 | /// (In the case of [MultiChildRenderObjectElement]s, some effort is put into
|
5268 | /// tracking widgets even when they change order; see
|
5269 | /// [RenderObjectElement.updateChildren].)
|
5270 | ///
|
5271 | /// If there was no corresponding previous child, this results in a new
|
5272 | /// [Element] being created (using [Widget.createElement]); that element is
|
5273 | /// then itself built, recursively.
|
5274 | ///
|
5275 | /// If there was a child previously but the build did not provide a
|
5276 | /// corresponding child to update it, then the old child is discarded (or, in
|
5277 | /// cases involving [GlobalKey] reparenting, reused elsewhere in the element
|
5278 | /// tree).
|
5279 | ///
|
5280 | /// The most common case, however, is that there was a corresponding previous
|
5281 | /// child. This is handled by asking the child [Element] to update itself
|
5282 | /// using the new child [Widget]. In the case of [StatefulElement]s, this
|
5283 | /// is what triggers [State.didUpdateWidget].
|
5284 | ///
|
5285 | /// ### Not rebuilding
|
5286 | ///
|
5287 | /// Before an [Element] is told to update itself with a new [Widget], the old
|
5288 | /// and new objects are compared using `operator ==`.
|
5289 | ///
|
5290 | /// In general, this is equivalent to doing a comparison using [identical] to
|
5291 | /// see if the two objects are in fact the exact same instance. If they are,
|
5292 | /// and if the element is not already marked dirty for other reasons, then the
|
5293 | /// element skips updating itself as it can determine with certainty that
|
5294 | /// there would be no value in updating itself or its descendants.
|
5295 | ///
|
5296 | /// It is strongly advised to avoid overriding `operator ==` on [Widget]
|
5297 | /// objects. While doing so seems like it could improve performance, in
|
5298 | /// practice, for non-leaf widgets, it results in O(N²) behavior. This is
|
5299 | /// because by necessity the comparison would have to include comparing child
|
5300 | /// widgets, and if those child widgets also implement `operator ==`, it
|
5301 | /// ultimately results in a complete walk of the widget tree... which is then
|
5302 | /// repeated at each level of the tree. In practice, just rebuilding is
|
5303 | /// cheaper. (Additionally, if _any_ subclass of [Widget] used in an
|
5304 | /// application implements `operator ==`, then the compiler cannot inline the
|
5305 | /// comparison anywhere, because it has to treat the call as virtual just in
|
5306 | /// case the instance happens to be one that has an overridden operator.)
|
5307 | ///
|
5308 | /// Instead, the best way to avoid unnecessary rebuilds is to cache the
|
5309 | /// widgets that are returned from [State.build], so that each frame the same
|
5310 | /// widgets are used until such time as they change. Several mechanisms exist
|
5311 | /// to encourage this: `const` widgets, for example, are a form of automatic
|
5312 | /// caching (if a widget is constructed using the `const` keyword, the same
|
5313 | /// instance is returned each time it is constructed with the same arguments).
|
5314 | ///
|
5315 | /// Another example is the [AnimatedBuilder.child] property, which allows the
|
5316 | /// non-animating parts of a subtree to remain static even as the
|
5317 | /// [AnimatedBuilder.builder] callback recreates the other components.
|
5318 | @pragma('dart2js:tryInline' )
|
5319 | @pragma('vm:prefer-inline' )
|
5320 | @pragma('wasm:prefer-inline' )
|
5321 | void rebuild({bool force = false}) {
|
5322 | assert(_lifecycleState != _ElementLifecycle.initial);
|
5323 | if (_lifecycleState != _ElementLifecycle.active || (!_dirty && !force)) {
|
5324 | return;
|
5325 | }
|
5326 | assert(() {
|
5327 | debugOnRebuildDirtyWidget?.call(this, _debugBuiltOnce);
|
5328 | if (debugPrintRebuildDirtyWidgets) {
|
5329 | if (!_debugBuiltOnce) {
|
5330 | debugPrint('Building $this' );
|
5331 | _debugBuiltOnce = true;
|
5332 | } else {
|
5333 | debugPrint('Rebuilding $this' );
|
5334 | }
|
5335 | }
|
5336 | return true;
|
5337 | }());
|
5338 | assert(_lifecycleState == _ElementLifecycle.active);
|
5339 | assert(owner!._debugStateLocked);
|
5340 | Element? debugPreviousBuildTarget;
|
5341 | assert(() {
|
5342 | debugPreviousBuildTarget = owner!._debugCurrentBuildTarget;
|
5343 | owner!._debugCurrentBuildTarget = this;
|
5344 | return true;
|
5345 | }());
|
5346 | try {
|
5347 | performRebuild();
|
5348 | } finally {
|
5349 | assert(() {
|
5350 | owner!._debugElementWasRebuilt(this);
|
5351 | assert(owner!._debugCurrentBuildTarget == this);
|
5352 | owner!._debugCurrentBuildTarget = debugPreviousBuildTarget;
|
5353 | return true;
|
5354 | }());
|
5355 | }
|
5356 | assert(!_dirty);
|
5357 | }
|
5358 |
|
5359 | /// Cause the widget to update itself.
|
5360 | ///
|
5361 | /// Called by [rebuild] after the appropriate checks have been made.
|
5362 | ///
|
5363 | /// The base implementation only clears the [dirty] flag.
|
5364 | @protected
|
5365 | @mustCallSuper
|
5366 | void performRebuild() {
|
5367 | _dirty = false;
|
5368 | }
|
5369 | }
|
5370 |
|
5371 | class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode {
|
5372 | _ElementDiagnosticableTreeNode({
|
5373 | super.name,
|
5374 | required Element super.value,
|
5375 | required super.style,
|
5376 | this.stateful = false,
|
5377 | });
|
5378 |
|
5379 | final bool stateful;
|
5380 |
|
5381 | @override
|
5382 | Map<String, Object?> toJsonMap(DiagnosticsSerializationDelegate delegate) {
|
5383 | final Map<String, Object?> json = super.toJsonMap(delegate);
|
5384 | final Element element = value as Element;
|
5385 | if (!element.debugIsDefunct) {
|
5386 | json['widgetRuntimeType' ] = element.widget.runtimeType.toString();
|
5387 | }
|
5388 | json['stateful' ] = stateful;
|
5389 | return json;
|
5390 | }
|
5391 | }
|
5392 |
|
5393 | /// Signature for the constructor that is called when an error occurs while
|
5394 | /// building a widget.
|
5395 | ///
|
5396 | /// The argument provides information regarding the cause of the error.
|
5397 | ///
|
5398 | /// See also:
|
5399 | ///
|
5400 | /// * [ErrorWidget.builder], which can be set to override the default
|
5401 | /// [ErrorWidget] builder.
|
5402 | /// * [FlutterError.reportError], which is typically called with the same
|
5403 | /// [FlutterErrorDetails] object immediately prior to [ErrorWidget.builder]
|
5404 | /// being called.
|
5405 | typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details);
|
5406 |
|
5407 | /// A widget that renders an exception's message.
|
5408 | ///
|
5409 | /// This widget is used when a build method fails, to help with determining
|
5410 | /// where the problem lies. Exceptions are also logged to the console, which you
|
5411 | /// can read using `flutter logs`. The console will also include additional
|
5412 | /// information such as the stack trace for the exception.
|
5413 | ///
|
5414 | /// It is possible to override this widget.
|
5415 | ///
|
5416 | /// {@tool dartpad}
|
5417 | /// This example shows how to override the standard error widget builder in release
|
5418 | /// mode, but use the standard one in debug mode.
|
5419 | ///
|
5420 | /// The error occurs when you click the "Error Prone" button.
|
5421 | ///
|
5422 | /// ** See code in examples/api/lib/widgets/framework/error_widget.0.dart **
|
5423 | /// {@end-tool}
|
5424 | ///
|
5425 | /// See also:
|
5426 | ///
|
5427 | /// * [FlutterError.onError], which can be set to a method that exits the
|
5428 | /// application if that is preferable to showing an error message.
|
5429 | /// * <https://docs.flutter.dev/testing/errors>, more information about error
|
5430 | /// handling in Flutter.
|
5431 | class ErrorWidget extends LeafRenderObjectWidget {
|
5432 | /// Creates a widget that displays the given exception.
|
5433 | ///
|
5434 | /// The message will be the stringification of the given exception, unless
|
5435 | /// computing that value itself throws an exception, in which case it will
|
5436 | /// be the string "Error".
|
5437 | ///
|
5438 | /// If this object is inspected from an IDE or the devtools, and the original
|
5439 | /// exception is a [FlutterError] object, the original exception itself will
|
5440 | /// be shown in the inspection output.
|
5441 | ErrorWidget(Object exception)
|
5442 | : message = _stringify(exception),
|
5443 | _flutterError = exception is FlutterError ? exception : null,
|
5444 | super(key: UniqueKey());
|
5445 |
|
5446 | /// Creates a widget that displays the given error message.
|
5447 | ///
|
5448 | /// An explicit [FlutterError] can be provided to be reported to inspection
|
5449 | /// tools. It need not match the message.
|
5450 | ErrorWidget.withDetails({ this.message = '' , FlutterError? error })
|
5451 | : _flutterError = error,
|
5452 | super(key: UniqueKey());
|
5453 |
|
5454 | /// The configurable factory for [ErrorWidget].
|
5455 | ///
|
5456 | /// When an error occurs while building a widget, the broken widget is
|
5457 | /// replaced by the widget returned by this function. By default, an
|
5458 | /// [ErrorWidget] is returned.
|
5459 | ///
|
5460 | /// The system is typically in an unstable state when this function is called.
|
5461 | /// An exception has just been thrown in the middle of build (and possibly
|
5462 | /// layout), so surrounding widgets and render objects may be in a rather
|
5463 | /// fragile state. The framework itself (especially the [BuildOwner]) may also
|
5464 | /// be confused, and additional exceptions are quite likely to be thrown.
|
5465 | ///
|
5466 | /// Because of this, it is highly recommended that the widget returned from
|
5467 | /// this function perform the least amount of work possible. A
|
5468 | /// [LeafRenderObjectWidget] is the best choice, especially one that
|
5469 | /// corresponds to a [RenderBox] that can handle the most absurd of incoming
|
5470 | /// constraints. The default constructor maps to a [RenderErrorBox].
|
5471 | ///
|
5472 | /// The default behavior is to show the exception's message in debug mode,
|
5473 | /// and to show nothing but a gray background in release builds.
|
5474 | ///
|
5475 | /// See also:
|
5476 | ///
|
5477 | /// * [FlutterError.onError], which is typically called with the same
|
5478 | /// [FlutterErrorDetails] object immediately prior to this callback being
|
5479 | /// invoked, and which can also be configured to control how errors are
|
5480 | /// reported.
|
5481 | /// * <https://docs.flutter.dev/testing/errors>, more information about error
|
5482 | /// handling in Flutter.
|
5483 | static ErrorWidgetBuilder builder = _defaultErrorWidgetBuilder;
|
5484 |
|
5485 | static Widget _defaultErrorWidgetBuilder(FlutterErrorDetails details) {
|
5486 | String message = '' ;
|
5487 | assert(() {
|
5488 | message = ' ${_stringify(details.exception)}\nSee also: https://docs.flutter.dev/testing/errors';
|
5489 | return true;
|
5490 | }());
|
5491 | final Object exception = details.exception;
|
5492 | return ErrorWidget.withDetails(message: message, error: exception is FlutterError ? exception : null);
|
5493 | }
|
5494 |
|
5495 | static String _stringify(Object? exception) {
|
5496 | try {
|
5497 | return exception.toString();
|
5498 | } catch (error) {
|
5499 | // If we get here, it means things have really gone off the rails, and we're better
|
5500 | // off just returning a simple string and letting the developer find out what the
|
5501 | // root cause of all their problems are by looking at the console logs.
|
5502 | }
|
5503 | return 'Error' ;
|
5504 | }
|
5505 |
|
5506 | /// The message to display.
|
5507 | final String message;
|
5508 | final FlutterError? _flutterError;
|
5509 |
|
5510 | @override
|
5511 | RenderBox createRenderObject(BuildContext context) => RenderErrorBox(message);
|
5512 |
|
5513 | @override
|
---|