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