1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | /// @docImport 'dart:ui'; |
6 | /// |
7 | /// @docImport 'package:flutter/animation.dart'; |
8 | /// @docImport 'package:flutter/material.dart'; |
9 | /// @docImport 'package:flutter/widgets.dart'; |
10 | /// @docImport 'package:flutter_test/flutter_test.dart'; |
11 | library; |
12 | |
13 | import 'dart:async'; |
14 | import 'dart:collection'; |
15 | |
16 | import 'package:flutter/foundation.dart'; |
17 | import 'package:flutter/rendering.dart'; |
18 | |
19 | import 'binding.dart'; |
20 | import 'debug.dart'; |
21 | import 'focus_manager.dart'; |
22 | import 'inherited_model.dart'; |
23 | import 'notification_listener.dart'; |
24 | import 'widget_inspector.dart'; |
25 | |
26 | export 'package:flutter/foundation.dart' |
27 | show |
28 | factory, |
29 | immutable, |
30 | mustCallSuper, |
31 | optionalTypeArgs, |
32 | protected, |
33 | required, |
34 | visibleForTesting; |
35 | export 'package:flutter/foundation.dart' |
36 | show ErrorDescription, ErrorHint, ErrorSummary, FlutterError, debugPrint, debugPrintStack; |
37 | export 'package:flutter/foundation.dart' show DiagnosticLevel, DiagnosticsNode; |
38 | export 'package:flutter/foundation.dart' show Key, LocalKey, ValueKey; |
39 | export 'package:flutter/foundation.dart' show ValueChanged, ValueGetter, ValueSetter, VoidCallback; |
40 | export 'package:flutter/rendering.dart' |
41 | show RenderBox, RenderObject, debugDumpLayerTree, debugDumpRenderTree; |
42 | |
43 | // Examples can assume: |
44 | // late BuildContext context; |
45 | // void setState(VoidCallback fn) { } |
46 | // abstract class RenderFrogJar extends RenderObject { } |
47 | // abstract class FrogJar extends RenderObjectWidget { const FrogJar({super.key}); } |
48 | // abstract class FrogJarParentData extends ParentData { late Size size; } |
49 | // abstract class SomeWidget extends StatefulWidget { const SomeWidget({super.key}); } |
50 | // typedef ChildWidget = Placeholder; |
51 | // class _SomeWidgetState extends State |
52 | // abstract class RenderFoo extends RenderObject { } |
53 | // abstract class Foo extends RenderObjectWidget { const Foo({super.key}); } |
54 | // abstract class StatefulWidgetX { const StatefulWidgetX({this.key}); final Key? key; Widget build(BuildContext context, State state); } |
55 | // class SpecialWidget extends StatelessWidget { const SpecialWidget({ super.key, this.handler }); final VoidCallback? handler; @override Widget build(BuildContext context) => this; } |
56 | // late Object? _myState, newValue; |
57 | // int _counter = 0; |
58 | // Future |
59 | // late AnimationController animation; |
60 | |
61 | class _DebugOnly { |
62 | const _DebugOnly(); |
63 | } |
64 | |
65 | /// An annotation used by test_analysis package to verify patterns are followed |
66 | /// that allow for tree-shaking of both fields and their initializers. This |
67 | /// annotation has no impact on code by itself, but indicates the following pattern |
68 | /// should be followed for a given field: |
69 | /// |
70 | /// ```dart |
71 | /// class Bar { |
72 | /// final Object? bar = kDebugMode ? Object() : null; |
73 | /// } |
74 | /// ``` |
75 | const _DebugOnly _debugOnly = _DebugOnly(); |
76 | |
77 | // KEYS |
78 | |
79 | /// A key that takes its identity from the object used as its value. |
80 | /// |
81 | /// Used to tie the identity of a widget to the identity of an object used to |
82 | /// generate that widget. |
83 | /// |
84 | /// See also: |
85 | /// |
86 | /// * [Key], the base class for all keys. |
87 | /// * The discussion at [Widget.key] for more information about how widgets use |
88 | /// keys. |
89 | class ObjectKey extends LocalKey { |
90 | /// Creates a key that uses [identical] on [value] for its [operator==]. |
91 | const ObjectKey(this.value); |
92 | |
93 | /// The object whose identity is used by this key's [operator==]. |
94 | final Object? value; |
95 | |
96 | @override |
97 | bool operator ==(Object other) { |
98 | if (other.runtimeType != runtimeType) { |
99 | return false; |
100 | } |
101 | return other is ObjectKey && identical(other.value, value); |
102 | } |
103 | |
104 | @override |
105 | int get hashCode => Object.hash(runtimeType, identityHashCode(value)); |
106 | |
107 | @override |
108 | String toString() { |
109 | if (runtimeType == ObjectKey) { |
110 | return '[${describeIdentity(value)} ]'; |
111 | } |
112 | return '[${objectRuntimeType(this, 'ObjectKey')} ${describeIdentity(value)} ]'; |
113 | } |
114 | } |
115 | |
116 | /// A key that is unique across the entire app. |
117 | /// |
118 | /// Global keys uniquely identify elements. Global keys provide access to other |
119 | /// objects that are associated with those elements, such as [BuildContext]. |
120 | /// For [StatefulWidget]s, global keys also provide access to [State]. |
121 | /// |
122 | /// Widgets that have global keys reparent their subtrees when they are moved |
123 | /// from one location in the tree to another location in the tree. In order to |
124 | /// reparent its subtree, a widget must arrive at its new location in the tree |
125 | /// in the same animation frame in which it was removed from its old location in |
126 | /// the tree. |
127 | /// |
128 | /// Reparenting an [Element] using a global key is relatively expensive, as |
129 | /// this operation will trigger a call to [State.deactivate] on the associated |
130 | /// [State] and all of its descendants; then force all widgets that depends |
131 | /// on an [InheritedWidget] to rebuild. |
132 | /// |
133 | /// If you don't need any of the features listed above, consider using a [Key], |
134 | /// [ValueKey], [ObjectKey], or [UniqueKey] instead. |
135 | /// |
136 | /// You cannot simultaneously include two widgets in the tree with the same |
137 | /// global key. Attempting to do so will assert at runtime. |
138 | /// |
139 | /// ## Pitfalls |
140 | /// |
141 | /// GlobalKeys should not be re-created on every build. They should usually be |
142 | /// long-lived objects owned by a [State] object, for example. |
143 | /// |
144 | /// Creating a new GlobalKey on every build will throw away the state of the |
145 | /// subtree associated with the old key and create a new fresh subtree for the |
146 | /// new key. Besides harming performance, this can also cause unexpected |
147 | /// behavior in widgets in the subtree. For example, a [GestureDetector] in the |
148 | /// subtree will be unable to track ongoing gestures since it will be recreated |
149 | /// on each build. |
150 | /// |
151 | /// Instead, a good practice is to let a State object own the GlobalKey, and |
152 | /// instantiate it outside the build method, such as in [State.initState]. |
153 | /// |
154 | /// See also: |
155 | /// |
156 | /// * The discussion at [Widget.key] for more information about how widgets use |
157 | /// keys. |
158 | @optionalTypeArgs |
159 | abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { |
160 | /// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a label used for |
161 | /// debugging. |
162 | /// |
163 | /// The label is purely for debugging and not used for comparing the identity |
164 | /// of the key. |
165 | factory GlobalKey({String? debugLabel}) => LabeledGlobalKey<T>(debugLabel); |
166 | |
167 | /// Creates a global key without a label. |
168 | /// |
169 | /// Used by subclasses because the factory constructor shadows the implicit |
170 | /// constructor. |
171 | const GlobalKey.constructor() : super.empty(); |
172 | |
173 | Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this]; |
174 | |
175 | /// The build context in which the widget with this key builds. |
176 | /// |
177 | /// The current context is null if there is no widget in the tree that matches |
178 | /// this global key. |
179 | BuildContext? get currentContext => _currentElement; |
180 | |
181 | /// The widget in the tree that currently has this global key. |
182 | /// |
183 | /// The current widget is null if there is no widget in the tree that matches |
184 | /// this global key. |
185 | Widget? get currentWidget => _currentElement?.widget; |
186 | |
187 | /// The [State] for the widget in the tree that currently has this global key. |
188 | /// |
189 | /// The current state is null if (1) there is no widget in the tree that |
190 | /// matches this global key, (2) that widget is not a [StatefulWidget], or the |
191 | /// associated [State] object is not a subtype of `T`. |
192 | T? get currentState => switch (_currentElement) { |
193 | StatefulElement(:final T state) => state, |
194 | _ => null, |
195 | }; |
196 | } |
197 | |
198 | /// A global key with a debugging label. |
199 | /// |
200 | /// The debug label is useful for documentation and for debugging. The label |
201 | /// does not affect the key's identity. |
202 | @optionalTypeArgs |
203 | class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
204 | /// Creates a global key with a debugging label. |
205 | /// |
206 | /// The label does not affect the key's identity. |
207 | // ignore: prefer_const_constructors_in_immutables , never use const for this class |
208 | LabeledGlobalKey(this._debugLabel) : super.constructor(); |
209 | |
210 | final String? _debugLabel; |
211 | |
212 | @override |
213 | String toString() { |
214 | final String label = _debugLabel != null ? '$_debugLabel ': ''; |
215 | if (runtimeType == LabeledGlobalKey) { |
216 | return '[GlobalKey#${shortHash(this)} $label]'; |
217 | } |
218 | return'[${describeIdentity(this)}$label]'; |
219 | } |
220 | } |
221 | |
222 | /// A global key that takes its identity from the object used as its value. |
223 | /// |
224 | /// Used to tie the identity of a widget to the identity of an object used to |
225 | /// generate that widget. |
226 | /// |
227 | /// Any [GlobalObjectKey] created for the same object will match. |
228 | /// |
229 | /// If the object is not private, then it is possible that collisions will occur |
230 | /// where independent widgets will reuse the same object as their |
231 | /// [GlobalObjectKey] value in a different part of the tree, leading to a global |
232 | /// key conflict. To avoid this problem, create a private [GlobalObjectKey] |
233 | /// subclass, as in: |
234 | /// |
235 | /// ```dart |
236 | /// class _MyKey extends GlobalObjectKey { |
237 | /// const _MyKey(super.value); |
238 | /// } |
239 | /// ``` |
240 | /// |
241 | /// Since the [runtimeType] of the key is part of its identity, this will |
242 | /// prevent clashes with other [GlobalObjectKey]s even if they have the same |
243 | /// value. |
244 | @optionalTypeArgs |
245 | class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
246 | /// Creates a global key that uses [identical] on [value] for its [operator==]. |
247 | const GlobalObjectKey(this.value) : super.constructor(); |
248 | |
249 | /// The object whose identity is used by this key's [operator==]. |
250 | final Object value; |
251 | |
252 | @override |
253 | bool operator ==(Object other) { |
254 | if (other.runtimeType != runtimeType) { |
255 | return false; |
256 | } |
257 | return other is GlobalObjectKey<T> && identical(other.value, value); |
258 | } |
259 | |
260 | @override |
261 | int get hashCode => identityHashCode(value); |
262 | |
263 | @override |
264 | String toString() { |
265 | String selfType = objectRuntimeType(this,'GlobalObjectKey'); |
266 | // The runtimeType string of a GlobalObjectKey() returns 'GlobalObjectKey |
267 | // because GlobalObjectKey is instantiated to its bounds. To avoid cluttering the output |
268 | // we remove the suffix. |
269 | const String suffix ='<State<StatefulWidget>>'; |
270 | if (selfType.endsWith(suffix)) { |
271 | selfType = selfType.substring(0, selfType.length - suffix.length); |
272 | } |
273 | return'[$selfType${describeIdentity(value)}]'; |
274 | } |
275 | } |
276 | |
277 | /// Describes the configuration for an [Element]. |
278 | /// |
279 | /// Widgets are the central class hierarchy in the Flutter framework. A widget |
280 | /// is an immutable description of part of a user interface. Widgets can be |
281 | /// inflated into elements, which manage the underlying render tree. |
282 | /// |
283 | /// Widgets themselves have no mutable state (all their fields must be final). |
284 | /// If you wish to associate mutable state with a widget, consider using a |
285 | /// [StatefulWidget], which creates a [State] object (via |
286 | /// [StatefulWidget.createState]) whenever it is inflated into an element and |
287 | /// incorporated into the tree. |
288 | /// |
289 | /// A given widget can be included in the tree zero or more times. In particular |
290 | /// a given widget can be placed in the tree multiple times. Each time a widget |
291 | /// is placed in the tree, it is inflated into an [Element], which means a |
292 | /// widget that is incorporated into the tree multiple times will be inflated |
293 | /// multiple times. |
294 | /// |
295 | /// The [key] property controls how one widget replaces another widget in the |
296 | /// tree. If the [runtimeType] and [key] properties of the two widgets are |
297 | /// [operator==], respectively, then the new widget replaces the old widget by |
298 | /// updating the underlying element (i.e., by calling [Element.update] with the |
299 | /// new widget). Otherwise, the old element is removed from the tree, the new |
300 | /// widget is inflated into an element, and the new element is inserted into the |
301 | /// tree. |
302 | /// |
303 | /// See also: |
304 | /// |
305 | /// * [StatefulWidget] and [State], for widgets that can build differently |
306 | /// several times over their lifetime. |
307 | /// * [InheritedWidget], for widgets that introduce ambient state that can |
308 | /// be read by descendant widgets. |
309 | /// * [StatelessWidget], for widgets that always build the same way given a |
310 | /// particular configuration and ambient state. |
311 | @immutable |
312 | abstract class Widget extends DiagnosticableTree { |
313 | /// Initializes [key] for subclasses. |
314 | const Widget({this.key}); |
315 | |
316 | /// Controls how one widget replaces another widget in the tree. |
317 | /// |
318 | /// If the [runtimeType] and [key] properties of the two widgets are |
319 | /// [operator==], respectively, then the new widget replaces the old widget by |
320 | /// updating the underlying element (i.e., by calling [Element.update] with the |
321 | /// new widget). Otherwise, the old element is removed from the tree, the new |
322 | /// widget is inflated into an element, and the new element is inserted into the |
323 | /// tree. |
324 | /// |
325 | /// In addition, using a [GlobalKey] as the widget's [key] allows the element |
326 | /// to be moved around the tree (changing parent) without losing state. When a |
327 | /// new widget is found (its key and type do not match a previous widget in |
328 | /// the same location), but there was a widget with that same global key |
329 | /// elsewhere in the tree in the previous frame, then that widget's element is |
330 | /// moved to the new location. |
331 | /// |
332 | /// Generally, a widget that is the only child of another widget does not need |
333 | /// an explicit key. |
334 | /// |
335 | /// See also: |
336 | /// |
337 | /// * The discussions at [Key] and [GlobalKey]. |
338 | final Key? key; |
339 | |
340 | /// Inflates this configuration to a concrete instance. |
341 | /// |
342 | /// A given widget can be included in the tree zero or more times. In particular |
343 | /// a given widget can be placed in the tree multiple times. Each time a widget |
344 | /// is placed in the tree, it is inflated into an [Element], which means a |
345 | /// widget that is incorporated into the tree multiple times will be inflated |
346 | /// multiple times. |
347 | @protected |
348 | @factory |
349 | Element createElement(); |
350 | |
351 | /// A short, textual description of this widget. |
352 | @override |
353 | String toStringShort() { |
354 | final String type = objectRuntimeType(this,'Widget'); |
355 | return key == null ? type :'$type-$key'; |
356 | } |
357 | |
358 | @override |
359 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
360 | super.debugFillProperties(properties); |
361 | properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; |
362 | } |
363 | |
364 | @override |
365 | @nonVirtual |
366 | bool operator ==(Object other) => super == other; |
367 | |
368 | @override |
369 | @nonVirtual |
370 | int get hashCode => super.hashCode; |
371 | |
372 | /// Whether the `newWidget` can be used to update an [Element] that currently |
373 | /// has the `oldWidget` as its configuration. |
374 | /// |
375 | /// An element that uses a given widget as its configuration can be updated to |
376 | /// use another widget as its configuration if, and only if, the two widgets |
377 | /// have [runtimeType] and [key] properties that are [operator==]. |
378 | /// |
379 | /// If the widgets have no key (their key is null), then they are considered a |
380 | /// match if they have the same type, even if their children are completely |
381 | /// different. |
382 | static bool canUpdate(Widget oldWidget, Widget newWidget) { |
383 | return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; |
384 | } |
385 | |
386 | // Return a numeric encoding of the specific `Widget` concrete subtype. |
387 | // This is used in `Element.updateChild` to determine if a hot reload modified the |
388 | // superclass of a mounted element's configuration. The encoding of each `Widget` |
389 | // must match the corresponding `Element` encoding in `Element._debugConcreteSubtype`. |
390 | static int _debugConcreteSubtype(Widget widget) { |
391 | return widget is StatefulWidget |
392 | ? 1 |
393 | : widget is StatelessWidget |
394 | ? 2 |
395 | : 0; |
396 | } |
397 | } |
398 | |
399 | /// A widget that does not require mutable state. |
400 | /// |
401 | /// A stateless widget is a widget that describes part of the user interface by |
402 | /// building a constellation of other widgets that describe the user interface |
403 | /// more concretely. The building process continues recursively until the |
404 | /// description of the user interface is fully concrete (e.g., consists |
405 | /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s). |
406 | /// |
407 | /// {@youtube 560 315 https://www.youtube.com/watch?v=wE7khGHVkYY} |
408 | /// |
409 | /// Stateless widget are useful when the part of the user interface you are |
410 | /// describing does not depend on anything other than the configuration |
411 | /// information in the object itself and the [BuildContext] in which the widget |
412 | /// is inflated. For compositions that can change dynamically, e.g. due to |
413 | /// having an internal clock-driven state, or depending on some system state, |
414 | /// consider using [StatefulWidget]. |
415 | /// |
416 | /// ## Performance considerations |
417 | /// |
418 | /// The [build] method of a stateless widget is typically only called in three |
419 | /// situations: the first time the widget is inserted in the tree, when the |
420 | /// widget's parent changes its configuration (see [Element.rebuild]), and when |
421 | /// an [InheritedWidget] it depends on changes. |
422 | /// |
423 | /// If a widget's parent will regularly change the widget's configuration, or if |
424 | /// it depends on inherited widgets that frequently change, then it is important |
425 | /// to optimize the performance of the [build] method to maintain a fluid |
426 | /// rendering performance. |
427 | /// |
428 | /// There are several techniques one can use to minimize the impact of |
429 | /// rebuilding a stateless widget: |
430 | /// |
431 | /// * Minimize the number of nodes transitively created by the build method and |
432 | /// any widgets it creates. For example, instead of an elaborate arrangement |
433 | /// of [Row]s, [Column]s, [Padding]s, and [SizedBox]es to position a single |
434 | /// child in a particularly fancy manner, consider using just an [Align] or a |
435 | /// [CustomSingleChildLayout]. Instead of an intricate layering of multiple |
436 | /// [Container]s and with [Decoration]s to draw just the right graphical |
437 | /// effect, consider a single [CustomPaint] widget. |
438 | /// |
439 | /// * Use `const` widgets where possible, and provide a `const` constructor for |
440 | /// the widget so that users of the widget can also do so. |
441 | /// |
442 | /// * Consider refactoring the stateless widget into a stateful widget so that |
443 | /// it can use some of the techniques described at [StatefulWidget], such as |
444 | /// caching common parts of subtrees and using [GlobalKey]s when changing the |
445 | /// tree structure. |
446 | /// |
447 | /// * If the widget is likely to get rebuilt frequently due to the use of |
448 | /// [InheritedWidget]s, consider refactoring the stateless widget into |
449 | /// multiple widgets, with the parts of the tree that change being pushed to |
450 | /// the leaves. For example instead of building a tree with four widgets, the |
451 | /// inner-most widget depending on the [Theme], consider factoring out the |
452 | /// part of the build function that builds the inner-most widget into its own |
453 | /// widget, so that only the inner-most widget needs to be rebuilt when the |
454 | /// theme changes. |
455 | /// {@template flutter.flutter.widgets.framework.prefer_const_over_helper} |
456 | /// * When trying to create a reusable piece of UI, prefer using a widget |
457 | /// rather than a helper method. For example, if there was a function used to |
458 | /// build a widget, a [State.setState] call would require Flutter to entirely |
459 | /// rebuild the returned wrapping widget. If a [Widget] was used instead, |
460 | /// Flutter would be able to efficiently re-render only those parts that |
461 | /// really need to be updated. Even better, if the created widget is `const`, |
462 | /// Flutter would short-circuit most of the rebuild work. |
463 | /// {@endtemplate} |
464 | /// |
465 | /// This video gives more explanations on why `const` constructors are important |
466 | /// and why a [Widget] is better than a helper method. |
467 | /// |
468 | /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo} |
469 | /// |
470 | /// {@tool snippet} |
471 | /// |
472 | /// The following is a skeleton of a stateless widget subclass called `GreenFrog`. |
473 | /// |
474 | /// Normally, widgets have more constructor arguments, each of which corresponds |
475 | /// to a `final` property. |
476 | /// |
477 | /// ```dart |
478 | /// class GreenFrog extends StatelessWidget { |
479 | /// const GreenFrog({ super.key }); |
480 | /// |
481 | /// @override |
482 | /// Widget build(BuildContext context) { |
483 | /// return Container(color: const Color(0xFF2DBD3A)); |
484 | /// } |
485 | /// } |
486 | /// ``` |
487 | /// {@end-tool} |
488 | /// |
489 | /// {@tool snippet} |
490 | /// |
491 | /// This next example shows the more generic widget `Frog` which can be given |
492 | /// a color and a child: |
493 | /// |
494 | /// ```dart |
495 | /// class Frog extends StatelessWidget { |
496 | /// const Frog({ |
497 | /// super.key, |
498 | /// this.color = const Color(0xFF2DBD3A), |
499 | /// this.child, |
500 | /// }); |
501 | /// |
502 | /// final Color color; |
503 | /// final Widget? child; |
504 | /// |
505 | /// @override |
506 | /// Widget build(BuildContext context) { |
507 | /// return ColoredBox(color: color, child: child); |
508 | /// } |
509 | /// } |
510 | /// ``` |
511 | /// {@end-tool} |
512 | /// |
513 | /// By convention, widget constructors only use named arguments. Also by |
514 | /// convention, the first argument is [key], and the last argument is `child`, |
515 | /// `children`, or the equivalent. |
516 | /// |
517 | /// See also: |
518 | /// |
519 | /// * [StatefulWidget] and [State], for widgets that can build differently |
520 | /// several times over their lifetime. |
521 | /// * [InheritedWidget], for widgets that introduce ambient state that can |
522 | /// be read by descendant widgets. |
523 | abstract class StatelessWidget extends Widget { |
524 | /// Initializes [key] for subclasses. |
525 | const StatelessWidget({super.key}); |
526 | |
527 | /// Creates a [StatelessElement] to manage this widget's location in the tree. |
528 | /// |
529 | /// It is uncommon for subclasses to override this method. |
530 | @override |
531 | StatelessElement createElement() => StatelessElement(this); |
532 | |
533 | /// Describes the part of the user interface represented by this widget. |
534 | /// |
535 | /// The framework calls this method when this widget is inserted into the tree |
536 | /// in a given [BuildContext] and when the dependencies of this widget change |
537 | /// (e.g., an [InheritedWidget] referenced by this widget changes). This |
538 | /// method can potentially be called in every frame and should not have any side |
539 | /// effects beyond building a widget. |
540 | /// |
541 | /// The framework replaces the subtree below this widget with the widget |
542 | /// returned by this method, either by updating the existing subtree or by |
543 | /// removing the subtree and inflating a new subtree, depending on whether the |
544 | /// widget returned by this method can update the root of the existing |
545 | /// subtree, as determined by calling [Widget.canUpdate]. |
546 | /// |
547 | /// Typically implementations return a newly created constellation of widgets |
548 | /// that are configured with information from this widget's constructor and |
549 | /// from the given [BuildContext]. |
550 | /// |
551 | /// The given [BuildContext] contains information about the location in the |
552 | /// tree at which this widget is being built. For example, the context |
553 | /// provides the set of inherited widgets for this location in the tree. A |
554 | /// given widget might be built with multiple different [BuildContext] |
555 | /// arguments over time if the widget is moved around the tree or if the |
556 | /// widget is inserted into the tree in multiple places at once. |
557 | /// |
558 | /// The implementation of this method must only depend on: |
559 | /// |
560 | /// * the fields of the widget, which themselves must not change over time, |
561 | /// and |
562 | /// * any ambient state obtained from the `context` using |
563 | /// [BuildContext.dependOnInheritedWidgetOfExactType]. |
564 | /// |
565 | /// If a widget's [build] method is to depend on anything else, use a |
566 | /// [StatefulWidget] instead. |
567 | /// |
568 | /// See also: |
569 | /// |
570 | /// * [StatelessWidget], which contains the discussion on performance considerations. |
571 | @protected |
572 | Widget build(BuildContext context); |
573 | } |
574 | |
575 | /// A widget that has mutable state. |
576 | /// |
577 | /// State is information that (1) can be read synchronously when the widget is |
578 | /// built and (2) might change during the lifetime of the widget. It is the |
579 | /// responsibility of the widget implementer to ensure that the [State] is |
580 | /// promptly notified when such state changes, using [State.setState]. |
581 | /// |
582 | /// A stateful widget is a widget that describes part of the user interface by |
583 | /// building a constellation of other widgets that describe the user interface |
584 | /// more concretely. The building process continues recursively until the |
585 | /// description of the user interface is fully concrete (e.g., consists |
586 | /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s). |
587 | /// |
588 | /// Stateful widgets are useful when the part of the user interface you are |
589 | /// describing can change dynamically, e.g. due to having an internal |
590 | /// clock-driven state, or depending on some system state. For compositions that |
591 | /// depend only on the configuration information in the object itself and the |
592 | /// [BuildContext] in which the widget is inflated, consider using |
593 | /// [StatelessWidget]. |
594 | /// |
595 | /// {@youtube 560 315 https://www.youtube.com/watch?v=AqCMFXEmf3w} |
596 | /// |
597 | /// [StatefulWidget] instances themselves are immutable and store their mutable |
598 | /// state either in separate [State] objects that are created by the |
599 | /// [createState] method, or in objects to which that [State] subscribes, for |
600 | /// example [Stream] or [ChangeNotifier] objects, to which references are stored |
601 | /// in final fields on the [StatefulWidget] itself. |
602 | /// |
603 | /// The framework calls [createState] whenever it inflates a |
604 | /// [StatefulWidget], which means that multiple [State] objects might be |
605 | /// associated with the same [StatefulWidget] if that widget has been inserted |
606 | /// into the tree in multiple places. Similarly, if a [StatefulWidget] is |
607 | /// removed from the tree and later inserted in to the tree again, the framework |
608 | /// will call [createState] again to create a fresh [State] object, simplifying |
609 | /// the lifecycle of [State] objects. |
610 | /// |
611 | /// A [StatefulWidget] keeps the same [State] object when moving from one |
612 | /// location in the tree to another if its creator used a [GlobalKey] for its |
613 | /// [key]. Because a widget with a [GlobalKey] can be used in at most one |
614 | /// location in the tree, a widget that uses a [GlobalKey] has at most one |
615 | /// associated element. The framework takes advantage of this property when |
616 | /// moving a widget with a global key from one location in the tree to another |
617 | /// by grafting the (unique) subtree associated with that widget from the old |
618 | /// location to the new location (instead of recreating the subtree at the new |
619 | /// location). The [State] objects associated with [StatefulWidget] are grafted |
620 | /// along with the rest of the subtree, which means the [State] object is reused |
621 | /// (instead of being recreated) in the new location. However, in order to be |
622 | /// eligible for grafting, the widget must be inserted into the new location in |
623 | /// the same animation frame in which it was removed from the old location. |
624 | /// |
625 | /// ## Performance considerations |
626 | /// |
627 | /// There are two primary categories of [StatefulWidget]s. |
628 | /// |
629 | /// The first is one which allocates resources in [State.initState] and disposes |
630 | /// of them in [State.dispose], but which does not depend on [InheritedWidget]s |
631 | /// or call [State.setState]. Such widgets are commonly used at the root of an |
632 | /// application or page, and communicate with subwidgets via [ChangeNotifier]s, |
633 | /// [Stream]s, or other such objects. Stateful widgets following such a pattern |
634 | /// are relatively cheap (in terms of CPU and GPU cycles), because they are |
635 | /// built once then never update. They can, therefore, have somewhat complicated |
636 | /// and deep build methods. |
637 | /// |
638 | /// The second category is widgets that use [State.setState] or depend on |
639 | /// [InheritedWidget]s. These will typically rebuild many times during the |
640 | /// application's lifetime, and it is therefore important to minimize the impact |
641 | /// of rebuilding such a widget. (They may also use [State.initState] or |
642 | /// [State.didChangeDependencies] and allocate resources, but the important part |
643 | /// is that they rebuild.) |
644 | /// |
645 | /// There are several techniques one can use to minimize the impact of |
646 | /// rebuilding a stateful widget: |
647 | /// |
648 | /// * Push the state to the leaves. For example, if your page has a ticking |
649 | /// clock, rather than putting the state at the top of the page and |
650 | /// rebuilding the entire page each time the clock ticks, create a dedicated |
651 | /// clock widget that only updates itself. |
652 | /// |
653 | /// * Minimize the number of nodes transitively created by the build method and |
654 | /// any widgets it creates. Ideally, a stateful widget would only create a |
655 | /// single widget, and that widget would be a [RenderObjectWidget]. |
656 | /// (Obviously this isn't always practical, but the closer a widget gets to |
657 | /// this ideal, the more efficient it will be.) |
658 | /// |
659 | /// * If a subtree does not change, cache the widget that represents that |
660 | /// subtree and re-use it each time it can be used. To do this, assign |
661 | /// a widget to a `final` state variable and re-use it in the build method. It |
662 | /// is massively more efficient for a widget to be re-used than for a new (but |
663 | /// identically-configured) widget to be created. Another caching strategy |
664 | /// consists in extracting the mutable part of the widget into a [StatefulWidget] |
665 | /// which accepts a child parameter. |
666 | /// |
667 | /// * Use `const` widgets where possible. (This is equivalent to caching a |
668 | /// widget and re-using it.) |
669 | /// |
670 | /// * Avoid changing the depth of any created subtrees or changing the type of |
671 | /// any widgets in the subtree. For example, rather than returning either the |
672 | /// child or the child wrapped in an [IgnorePointer], always wrap the child |
673 | /// widget in an [IgnorePointer] and control the [IgnorePointer.ignoring] |
674 | /// property. This is because changing the depth of the subtree requires |
675 | /// rebuilding, laying out, and painting the entire subtree, whereas just |
676 | /// changing the property will require the least possible change to the |
677 | /// render tree (in the case of [IgnorePointer], for example, no layout or |
678 | /// repaint is necessary at all). |
679 | /// |
680 | /// * If the depth must be changed for some reason, consider wrapping the |
681 | /// common parts of the subtrees in widgets that have a [GlobalKey] that |
682 | /// remains consistent for the life of the stateful widget. (The |
683 | /// [KeyedSubtree] widget may be useful for this purpose if no other widget |
684 | /// can conveniently be assigned the key.) |
685 | /// |
686 | /// {@macro flutter.flutter.widgets.framework.prefer_const_over_helper} |
687 | /// |
688 | /// This video gives more explanations on why `const` constructors are important |
689 | /// and why a [Widget] is better than a helper method. |
690 | /// |
691 | /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo} |
692 | /// |
693 | /// For more details on the mechanics of rebuilding a widget, see |
694 | /// the discussion at [Element.rebuild]. |
695 | /// |
696 | /// {@tool snippet} |
697 | /// |
698 | /// This is a skeleton of a stateful widget subclass called `YellowBird`. |
699 | /// |
700 | /// In this example, the [State] has no actual state. State is normally |
701 | /// represented as private member fields. Also, normally widgets have more |
702 | /// constructor arguments, each of which corresponds to a `final` property. |
703 | /// |
704 | /// ```dart |
705 | /// class YellowBird extends StatefulWidget { |
706 | /// const YellowBird({ super.key }); |
707 | /// |
708 | /// @override |
709 | /// State<YellowBird> createState() => _YellowBirdState(); |
710 | /// } |
711 | /// |
712 | /// class _YellowBirdState extends State<YellowBird> { |
713 | /// @override |
714 | /// Widget build(BuildContext context) { |
715 | /// return Container(color: const Color(0xFFFFE306)); |
716 | /// } |
717 | /// } |
718 | /// ``` |
719 | /// {@end-tool} |
720 | /// {@tool snippet} |
721 | /// |
722 | /// This example shows the more generic widget `Bird` which can be given a |
723 | /// color and a child, and which has some internal state with a method that |
724 | /// can be called to mutate it: |
725 | /// |
726 | /// ```dart |
727 | /// class Bird extends StatefulWidget { |
728 | /// const Bird({ |
729 | /// super.key, |
730 | /// this.color = const Color(0xFFFFE306), |
731 | /// this.child, |
732 | /// }); |
733 | /// |
734 | /// final Color color; |
735 | /// final Widget? child; |
736 | /// |
737 | /// @override |
738 | /// State<Bird> createState() => _BirdState(); |
739 | /// } |
740 | /// |
741 | /// class _BirdState extends State<Bird> { |
742 | /// double _size = 1.0; |
743 | /// |
744 | /// void grow() { |
745 | /// setState(() { _size += 0.1; }); |
746 | /// } |
747 | /// |
748 | /// @override |
749 | /// Widget build(BuildContext context) { |
750 | /// return Container( |
751 | /// color: widget.color, |
752 | /// transform: Matrix4.diagonal3Values(_size, _size, 1.0), |
753 | /// child: widget.child, |
754 | /// ); |
755 | /// } |
756 | /// } |
757 | /// ``` |
758 | /// {@end-tool} |
759 | /// |
760 | /// By convention, widget constructors only use named arguments. Also by |
761 | /// convention, the first argument is [key], and the last argument is `child`, |
762 | /// `children`, or the equivalent. |
763 | /// |
764 | /// See also: |
765 | /// |
766 | /// * [State], where the logic behind a [StatefulWidget] is hosted. |
767 | /// * [StatelessWidget], for widgets that always build the same way given a |
768 | /// particular configuration and ambient state. |
769 | /// * [InheritedWidget], for widgets that introduce ambient state that can |
770 | /// be read by descendant widgets. |
771 | abstract class StatefulWidget extends Widget { |
772 | /// Initializes [key] for subclasses. |
773 | const StatefulWidget({super.key}); |
774 | |
775 | /// Creates a [StatefulElement] to manage this widget's location in the tree. |
776 | /// |
777 | /// It is uncommon for subclasses to override this method. |
778 | @override |
779 | StatefulElement createElement() => StatefulElement(this); |
780 | |
781 | /// Creates the mutable state for this widget at a given location in the tree. |
782 | /// |
783 | /// Subclasses should override this method to return a newly created |
784 | /// instance of their associated [State] subclass: |
785 | /// |
786 | /// ```dart |
787 | /// @override |
788 | /// State<SomeWidget> createState() => _SomeWidgetState(); |
789 | /// ``` |
790 | /// |
791 | /// The framework can call this method multiple times over the lifetime of |
792 | /// a [StatefulWidget]. For example, if the widget is inserted into the tree |
793 | /// in multiple locations, the framework will create a separate [State] object |
794 | /// for each location. Similarly, if the widget is removed from the tree and |
795 | /// later inserted into the tree again, the framework will call [createState] |
796 | /// again to create a fresh [State] object, simplifying the lifecycle of |
797 | /// [State] objects. |
798 | @protected |
799 | @factory |
800 | State createState(); |
801 | } |
802 | |
803 | /// Tracks the lifecycle of [State] objects when asserts are enabled. |
804 | enum _StateLifecycle { |
805 | /// The [State] object has been created. [State.initState] is called at this |
806 | /// time. |
807 | created, |
808 | |
809 | /// The [State.initState] method has been called but the [State] object is |
810 | /// not yet ready to build. [State.didChangeDependencies] is called at this time. |
811 | initialized, |
812 | |
813 | /// The [State] object is ready to build and [State.dispose] has not yet been |
814 | /// called. |
815 | ready, |
816 | |
817 | /// The [State.dispose] method has been called and the [State] object is |
818 | /// no longer able to build. |
819 | defunct, |
820 | } |
821 | |
822 | /// The signature of [State.setState] functions. |
823 | typedef StateSetter = void Function(VoidCallback fn); |
824 | |
825 | /// The logic and internal state for a [StatefulWidget]. |
826 | /// |
827 | /// State is information that (1) can be read synchronously when the widget is |
828 | /// built and (2) might change during the lifetime of the widget. It is the |
829 | /// responsibility of the widget implementer to ensure that the [State] is |
830 | /// promptly notified when such state changes, using [State.setState]. |
831 | /// |
832 | /// [State] objects are created by the framework by calling the |
833 | /// [StatefulWidget.createState] method when inflating a [StatefulWidget] to |
834 | /// insert it into the tree. Because a given [StatefulWidget] instance can be |
835 | /// inflated multiple times (e.g., the widget is incorporated into the tree in |
836 | /// multiple places at once), there might be more than one [State] object |
837 | /// associated with a given [StatefulWidget] instance. Similarly, if a |
838 | /// [StatefulWidget] is removed from the tree and later inserted in to the tree |
839 | /// again, the framework will call [StatefulWidget.createState] again to create |
840 | /// a fresh [State] object, simplifying the lifecycle of [State] objects. |
841 | /// |
842 | /// [State] objects have the following lifecycle: |
843 | /// |
844 | /// * The framework creates a [State] object by calling |
845 | /// [StatefulWidget.createState]. |
846 | /// * The newly created [State] object is associated with a [BuildContext]. |
847 | /// This association is permanent: the [State] object will never change its |
848 | /// [BuildContext]. However, the [BuildContext] itself can be moved around |
849 | /// the tree along with its subtree. At this point, the [State] object is |
850 | /// considered [mounted]. |
851 | /// * The framework calls [initState]. Subclasses of [State] should override |
852 | /// [initState] to perform one-time initialization that depends on the |
853 | /// [BuildContext] or the widget, which are available as the [context] and |
854 | /// [widget] properties, respectively, when the [initState] method is |
855 | /// called. |
856 | /// * The framework calls [didChangeDependencies]. Subclasses of [State] should |
857 | /// override [didChangeDependencies] to perform initialization involving |
858 | /// [InheritedWidget]s. If [BuildContext.dependOnInheritedWidgetOfExactType] is |
859 | /// called, the [didChangeDependencies] method will be called again if the |
860 | /// inherited widgets subsequently change or if the widget moves in the tree. |
861 | /// * At this point, the [State] object is fully initialized and the framework |
862 | /// might call its [build] method any number of times to obtain a |
863 | /// description of the user interface for this subtree. [State] objects can |
864 | /// spontaneously request to rebuild their subtree by calling their |
865 | /// [setState] method, which indicates that some of their internal state |
866 | /// has changed in a way that might impact the user interface in this |
867 | /// subtree. |
868 | /// * During this time, a parent widget might rebuild and request that this |
869 | /// location in the tree update to display a new widget with the same |
870 | /// [runtimeType] and [Widget.key]. When this happens, the framework will |
871 | /// update the [widget] property to refer to the new widget and then call the |
872 | /// [didUpdateWidget] method with the previous widget as an argument. [State] |
873 | /// objects should override [didUpdateWidget] to respond to changes in their |
874 | /// associated widget (e.g., to start implicit animations). The framework |
875 | /// always calls [build] after calling [didUpdateWidget], which means any |
876 | /// calls to [setState] in [didUpdateWidget] are redundant. (See also the |
877 | /// discussion at [Element.rebuild].) |
878 | /// * During development, if a hot reload occurs (whether initiated from the |
879 | /// command line `flutter` tool by pressing `r`, or from an IDE), the |
880 | /// [reassemble] method is called. This provides an opportunity to |
881 | /// reinitialize any data that was prepared in the [initState] method. |
882 | /// * If the subtree containing the [State] object is removed from the tree |
883 | /// (e.g., because the parent built a widget with a different [runtimeType] |
884 | /// or [Widget.key]), the framework calls the [deactivate] method. Subclasses |
885 | /// should override this method to clean up any links between this object |
886 | /// and other elements in the tree (e.g. if you have provided an ancestor |
887 | /// with a pointer to a descendant's [RenderObject]). |
888 | /// * At this point, the framework might reinsert this subtree into another |
889 | /// part of the tree. If that happens, the framework will ensure that it |
890 | /// calls [build] to give the [State] object a chance to adapt to its new |
891 | /// location in the tree. If the framework does reinsert this subtree, it |
892 | /// will do so before the end of the animation frame in which the subtree was |
893 | /// removed from the tree. For this reason, [State] objects can defer |
894 | /// releasing most resources until the framework calls their [dispose] |
895 | /// method. |
896 | /// * If the framework does not reinsert this subtree by the end of the current |
897 | /// animation frame, the framework will call [dispose], which indicates that |
898 | /// this [State] object will never build again. Subclasses should override |
899 | /// this method to release any resources retained by this object (e.g., |
900 | /// stop any active animations). |
901 | /// * After the framework calls [dispose], the [State] object is considered |
902 | /// unmounted and the [mounted] property is false. It is an error to call |
903 | /// [setState] at this point. This stage of the lifecycle is terminal: there |
904 | /// is no way to remount a [State] object that has been disposed. |
905 | /// |
906 | /// See also: |
907 | /// |
908 | /// * [StatefulWidget], where the current configuration of a [State] is hosted, |
909 | /// and whose documentation has sample code for [State]. |
910 | /// * [StatelessWidget], for widgets that always build the same way given a |
911 | /// particular configuration and ambient state. |
912 | /// * [InheritedWidget], for widgets that introduce ambient state that can |
913 | /// be read by descendant widgets. |
914 | /// * [Widget], for an overview of widgets in general. |
915 | @optionalTypeArgs |
916 | abstract class State<T extends StatefulWidget> with Diagnosticable { |
917 | /// The current configuration. |
918 | /// |
919 | /// A [State] object's configuration is the corresponding [StatefulWidget] |
920 | /// instance. This property is initialized by the framework before calling |
921 | /// [initState]. If the parent updates this location in the tree to a new |
922 | /// widget with the same [runtimeType] and [Widget.key] as the current |
923 | /// configuration, the framework will update this property to refer to the new |
924 | /// widget and then call [didUpdateWidget], passing the old configuration as |
925 | /// an argument. |
926 | T get widget => _widget!; |
927 | T? _widget; |
928 | |
929 | /// The current stage in the lifecycle for this state object. |
930 | /// |
931 | /// This field is used by the framework when asserts are enabled to verify |
932 | /// that [State] objects move through their lifecycle in an orderly fashion. |
933 | _StateLifecycle _debugLifecycleState = _StateLifecycle.created; |
934 | |
935 | /// Verifies that the [State] that was created is one that expects to be |
936 | /// created for that particular [Widget]. |
937 | bool _debugTypesAreRight(Widget widget) => widget is T; |
938 | |
939 | /// The location in the tree where this widget builds. |
940 | /// |
941 | /// The framework associates [State] objects with a [BuildContext] after |
942 | /// creating them with [StatefulWidget.createState] and before calling |
943 | /// [initState]. The association is permanent: the [State] object will never |
944 | /// change its [BuildContext]. However, the [BuildContext] itself can be moved |
945 | /// around the tree. |
946 | /// |
947 | /// After calling [dispose], the framework severs the [State] object's |
948 | /// connection with the [BuildContext]. |
949 | BuildContext get context { |
950 | assert(() { |
951 | if (_element == null) { |
952 | throw FlutterError( |
953 | 'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n' |
954 | 'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.', |
955 | ); |
956 | } |
957 | return true; |
958 | }()); |
959 | return _element!; |
960 | } |
961 | |
962 | StatefulElement? _element; |
963 | |
964 | /// Whether this [State] object is currently in a tree. |
965 | /// |
966 | /// After creating a [State] object and before calling [initState], the |
967 | /// framework "mounts" the [State] object by associating it with a |
968 | /// [BuildContext]. The [State] object remains mounted until the framework |
969 | /// calls [dispose], after which time the framework will never ask the [State] |
970 | /// object to [build] again. |
971 | /// |
972 | /// It is an error to call [setState] unless [mounted] is true. |
973 | bool get mounted => _element != null; |
974 | |
975 | /// Called when this object is inserted into the tree. |
976 | /// |
977 | /// The framework will call this method exactly once for each [State] object |
978 | /// it creates. |
979 | /// |
980 | /// Override this method to perform initialization that depends on the |
981 | /// location at which this object was inserted into the tree (i.e., [context]) |
982 | /// or on the widget used to configure this object (i.e., [widget]). |
983 | /// |
984 | /// {@template flutter.widgets.State.initState} |
985 | /// If a [State]'s [build] method depends on an object that can itself |
986 | /// change state, for example a [ChangeNotifier] or [Stream], or some |
987 | /// other object to which one can subscribe to receive notifications, then |
988 | /// be sure to subscribe and unsubscribe properly in [initState], |
989 | /// [didUpdateWidget], and [dispose]: |
990 | /// |
991 | /// * In [initState], subscribe to the object. |
992 | /// * In [didUpdateWidget] unsubscribe from the old object and subscribe |
993 | /// to the new one if the updated widget configuration requires |
994 | /// replacing the object. |
995 | /// * In [dispose], unsubscribe from the object. |
996 | /// |
997 | /// {@endtemplate} |
998 | /// |
999 | /// You should not use [BuildContext.dependOnInheritedWidgetOfExactType] from this |
1000 | /// method. However, [didChangeDependencies] will be called immediately |
1001 | /// following this method, and [BuildContext.dependOnInheritedWidgetOfExactType] can |
1002 | /// be used there. |
1003 | /// |
1004 | /// Implementations of this method should start with a call to the inherited |
1005 | /// method, as in `super.initState()`. |
1006 | @protected |
1007 | @mustCallSuper |
1008 | void initState() { |
1009 | assert(_debugLifecycleState == _StateLifecycle.created); |
1010 | assert(debugMaybeDispatchCreated('widgets','State', this)); |
1011 | } |
1012 | |
1013 | /// Called whenever the widget configuration changes. |
1014 | /// |
1015 | /// If the parent widget rebuilds and requests that this location in the tree |
1016 | /// update to display a new widget with the same [runtimeType] and |
1017 | /// [Widget.key], the framework will update the [widget] property of this |
1018 | /// [State] object to refer to the new widget and then call this method |
1019 | /// with the previous widget as an argument. |
1020 | /// |
1021 | /// Override this method to respond when the [widget] changes (e.g., to start |
1022 | /// implicit animations). |
1023 | /// |
1024 | /// The framework always calls [build] after calling [didUpdateWidget], which |
1025 | /// means any calls to [setState] in [didUpdateWidget] are redundant. |
1026 | /// |
1027 | /// {@macro flutter.widgets.State.initState} |
1028 | /// |
1029 | /// Implementations of this method should start with a call to the inherited |
1030 | /// method, as in `super.didUpdateWidget(oldWidget)`. |
1031 | /// |
1032 | /// _See the discussion at [Element.rebuild] for more information on when this |
1033 | /// method is called._ |
1034 | @mustCallSuper |
1035 | @protected |
1036 | void didUpdateWidget(covariant T oldWidget) {} |
1037 | |
1038 | /// {@macro flutter.widgets.Element.reassemble} |
1039 | /// |
1040 | /// In addition to this method being invoked, it is guaranteed that the |
1041 | /// [build] method will be invoked when a reassemble is signaled. Most |
1042 | /// widgets therefore do not need to do anything in the [reassemble] method. |
1043 | /// |
1044 | /// See also: |
1045 | /// |
1046 | /// * [Element.reassemble] |
1047 | /// * [BindingBase.reassembleApplication] |
1048 | /// * [Image], which uses this to reload images. |
1049 | @protected |
1050 | @mustCallSuper |
1051 | void reassemble() {} |
1052 | |
1053 | /// Notify the framework that the internal state of this object has changed. |
1054 | /// |
1055 | /// Whenever you change the internal state of a [State] object, make the |
1056 | /// change in a function that you pass to [setState]: |
1057 | /// |
1058 | /// ```dart |
1059 | /// setState(() { _myState = newValue; }); |
1060 | /// ``` |
1061 | /// |
1062 | /// The provided callback is immediately called synchronously. It must not |
1063 | /// return a future (the callback cannot be `async`), since then it would be |
1064 | /// unclear when the state was actually being set. |
1065 | /// |
1066 | /// Calling [setState] notifies the framework that the internal state of this |
1067 | /// object has changed in a way that might impact the user interface in this |
1068 | /// subtree, which causes the framework to schedule a [build] for this [State] |
1069 | /// object. |
1070 | /// |
1071 | /// If you just change the state directly without calling [setState], the |
1072 | /// framework might not schedule a [build] and the user interface for this |
1073 | /// subtree might not be updated to reflect the new state. |
1074 | /// |
1075 | /// Generally it is recommended that the [setState] method only be used to |
1076 | /// wrap the actual changes to the state, not any computation that might be |
1077 | /// associated with the change. For example, here a value used by the [build] |
1078 | /// function is incremented, and then the change is written to disk, but only |
1079 | /// the increment is wrapped in the [setState]: |
1080 | /// |
1081 | /// ```dart |
1082 | /// Future<void> _incrementCounter() async { |
1083 | /// setState(() { |
1084 | /// _counter++; |
1085 | /// }); |
1086 | /// Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package |
1087 | /// final String dirName = directory.path; |
1088 | /// await File('$dirName/counter.txt').writeAsString('$_counter'); |
1089 | /// } |
1090 | /// ``` |
1091 | /// |
1092 | /// Sometimes, the changed state is in some other object not owned by the |
1093 | /// widget [State], but the widget nonetheless needs to be updated to react to |
1094 | /// the new state. This is especially common with [Listenable]s, such as |
1095 | /// [AnimationController]s. |
1096 | /// |
1097 | /// In such cases, it is good practice to leave a comment in the callback |
1098 | /// passed to [setState] that explains what state changed: |
1099 | /// |
1100 | /// ```dart |
1101 | /// void _update() { |
1102 | /// setState(() { /* The animation changed. */ }); |
1103 | /// } |
1104 | /// //... |
1105 | /// animation.addListener(_update); |
1106 | /// ``` |
1107 | /// |
1108 | /// It is an error to call this method after the framework calls [dispose]. |
1109 | /// You can determine whether it is legal to call this method by checking |
1110 | /// whether the [mounted] property is true. That said, it is better practice |
1111 | /// to cancel whatever work might trigger the [setState] rather than merely |
1112 | /// checking for [mounted] before calling [setState], as otherwise CPU cycles |
1113 | /// will be wasted. |
1114 | /// |
1115 | /// ## Design discussion |
1116 | /// |
1117 | /// The original version of this API was a method called `markNeedsBuild`, for |
1118 | /// consistency with [RenderObject.markNeedsLayout], |
1119 | /// [RenderObject.markNeedsPaint], _et al_. |
1120 | /// |
1121 | /// However, early user testing of the Flutter framework revealed that people |
1122 | /// would call `markNeedsBuild()` much more often than necessary. Essentially, |
1123 | /// people used it like a good luck charm, any time they weren't sure if they |
1124 | /// needed to call it, they would call it, just in case. |
1125 | /// |
1126 | /// Naturally, this led to performance issues in applications. |
1127 | /// |
1128 | /// When the API was changed to take a callback instead, this practice was |
1129 | /// greatly reduced. One hypothesis is that prompting developers to actually |
1130 | /// update their state in a callback caused developers to think more carefully |
1131 | /// about what exactly was being updated, and thus improved their understanding |
1132 | /// of the appropriate times to call the method. |
1133 | /// |
1134 | /// In practice, the [setState] method's implementation is trivial: it calls |
1135 | /// the provided callback synchronously, then calls [Element.markNeedsBuild]. |
1136 | /// |
1137 | /// ## Performance considerations |
1138 | /// |
1139 | /// There is minimal _direct_ overhead to calling this function, and as it is |
1140 | /// expected to be called at most once per frame, the overhead is irrelevant |
1141 | /// anyway. Nonetheless, it is best to avoid calling this function redundantly |
1142 | /// (e.g. in a tight loop), as it does involve creating a closure and calling |
1143 | /// it. The method is idempotent, there is no benefit to calling it more than |
1144 | /// once per [State] per frame. |
1145 | /// |
1146 | /// The _indirect_ cost of causing this function, however, is high: it causes |
1147 | /// the widget to rebuild, possibly triggering rebuilds for the entire subtree |
1148 | /// rooted at this widget, and further triggering a relayout and repaint of |
1149 | /// the entire corresponding [RenderObject] subtree. |
1150 | /// |
1151 | /// For this reason, this method should only be called when the [build] method |
1152 | /// will, as a result of whatever state change was detected, change its result |
1153 | /// meaningfully. |
1154 | /// |
1155 | /// See also: |
1156 | /// |
1157 | /// * [StatefulWidget], the API documentation for which has a section on |
1158 | /// performance considerations that are relevant here. |
1159 | @protected |
1160 | void setState(VoidCallback fn) { |
1161 | assert(() { |
1162 | if (_debugLifecycleState == _StateLifecycle.defunct) { |
1163 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1164 | ErrorSummary('setState() called after dispose():$this'), |
1165 | ErrorDescription( |
1166 | 'This error happens if you call setState() on a State object for a widget that ' |
1167 | 'no longer appears in the widget tree (e.g., whose parent widget no longer ' |
1168 | 'includes the widget in its build). This error can occur when code calls ' |
1169 | 'setState() from a timer or an animation callback.', |
1170 | ), |
1171 | ErrorHint( |
1172 | 'The preferred solution is ' |
1173 | 'to cancel the timer or stop listening to the animation in the dispose() ' |
1174 | 'callback. Another solution is to check the "mounted" property of this ' |
1175 | 'object before calling setState() to ensure the object is still in the ' |
1176 | 'tree.', |
1177 | ), |
1178 | ErrorHint( |
1179 | 'This error might indicate a memory leak if setState() is being called ' |
1180 | 'because another object is retaining a reference to this State object ' |
1181 | 'after it has been removed from the tree. To avoid memory leaks, ' |
1182 | 'consider breaking the reference to this object during dispose().', |
1183 | ), |
1184 | ]); |
1185 | } |
1186 | if (_debugLifecycleState == _StateLifecycle.created && !mounted) { |
1187 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1188 | ErrorSummary('setState() called in constructor:$this'), |
1189 | ErrorHint( |
1190 | 'This happens when you call setState() on a State object for a widget that ' |
1191 | "hasn't been inserted into the widget tree yet. It is not necessary to call " |
1192 | 'setState() in the constructor, since the state is already assumed to be dirty ' |
1193 | 'when it is initially created.', |
1194 | ), |
1195 | ]); |
1196 | } |
1197 | return true; |
1198 | }()); |
1199 | final Object? result = fn() as dynamic; |
1200 | assert(() { |
1201 | if (result is Future) { |
1202 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1203 | ErrorSummary('setState() callback argument returned a Future.'), |
1204 | ErrorDescription( |
1205 | 'The setState() method on$thiswas called with a closure or method that ' |
1206 | 'returned a Future. Maybe it is marked as "async".', |
1207 | ), |
1208 | ErrorHint( |
1209 | 'Instead of performing asynchronous work inside a call to setState(), first ' |
1210 | 'execute the work (without updating the widget state), and then synchronously ' |
1211 | 'update the state inside a call to setState().', |
1212 | ), |
1213 | ]); |
1214 | } |
1215 | // We ignore other types of return values so that you can do things like: |
1216 | // setState(() => x = 3); |
1217 | return true; |
1218 | }()); |
1219 | _element!.markNeedsBuild(); |
1220 | } |
1221 | |
1222 | /// Called when this object is removed from the tree. |
1223 | /// |
1224 | /// The framework calls this method whenever it removes this [State] object |
1225 | /// from the tree. In some cases, the framework will reinsert the [State] |
1226 | /// object into another part of the tree (e.g., if the subtree containing this |
1227 | /// [State] object is grafted from one location in the tree to another due to |
1228 | /// the use of a [GlobalKey]). If that happens, the framework will call |
1229 | /// [activate] to give the [State] object a chance to reacquire any resources |
1230 | /// that it released in [deactivate]. It will then also call [build] to give |
1231 | /// the [State] object a chance to adapt to its new location in the tree. If |
1232 | /// the framework does reinsert this subtree, it will do so before the end of |
1233 | /// the animation frame in which the subtree was removed from the tree. For |
1234 | /// this reason, [State] objects can defer releasing most resources until the |
1235 | /// framework calls their [dispose] method. |
1236 | /// |
1237 | /// Subclasses should override this method to clean up any links between |
1238 | /// this object and other elements in the tree (e.g. if you have provided an |
1239 | /// ancestor with a pointer to a descendant's [RenderObject]). |
1240 | /// |
1241 | /// Implementations of this method should end with a call to the inherited |
1242 | /// method, as in `super.deactivate()`. |
1243 | /// |
1244 | /// See also: |
1245 | /// |
1246 | /// * [dispose], which is called after [deactivate] if the widget is removed |
1247 | /// from the tree permanently. |
1248 | @protected |
1249 | @mustCallSuper |
1250 | void deactivate() {} |
1251 | |
1252 | /// Called when this object is reinserted into the tree after having been |
1253 | /// removed via [deactivate]. |
1254 | /// |
1255 | /// In most cases, after a [State] object has been deactivated, it is _not_ |
1256 | /// reinserted into the tree, and its [dispose] method will be called to |
1257 | /// signal that it is ready to be garbage collected. |
1258 | /// |
1259 | /// In some cases, however, after a [State] object has been deactivated, the |
1260 | /// framework will reinsert it into another part of the tree (e.g., if the |
1261 | /// subtree containing this [State] object is grafted from one location in |
1262 | /// the tree to another due to the use of a [GlobalKey]). If that happens, |
1263 | /// the framework will call [activate] to give the [State] object a chance to |
1264 | /// reacquire any resources that it released in [deactivate]. It will then |
1265 | /// also call [build] to give the object a chance to adapt to its new |
1266 | /// location in the tree. If the framework does reinsert this subtree, it |
1267 | /// will do so before the end of the animation frame in which the subtree was |
1268 | /// removed from the tree. For this reason, [State] objects can defer |
1269 | /// releasing most resources until the framework calls their [dispose] method. |
1270 | /// |
1271 | /// The framework does not call this method the first time a [State] object |
1272 | /// is inserted into the tree. Instead, the framework calls [initState] in |
1273 | /// that situation. |
1274 | /// |
1275 | /// Implementations of this method should start with a call to the inherited |
1276 | /// method, as in `super.activate()`. |
1277 | /// |
1278 | /// See also: |
1279 | /// |
1280 | /// * [Element.activate], the corresponding method when an element |
1281 | /// transitions from the "inactive" to the "active" lifecycle state. |
1282 | @protected |
1283 | @mustCallSuper |
1284 | void activate() {} |
1285 | |
1286 | /// Called when this object is removed from the tree permanently. |
1287 | /// |
1288 | /// The framework calls this method when this [State] object will never |
1289 | /// build again. After the framework calls [dispose], the [State] object is |
1290 | /// considered unmounted and the [mounted] property is false. It is an error |
1291 | /// to call [setState] at this point. This stage of the lifecycle is terminal: |
1292 | /// there is no way to remount a [State] object that has been disposed. |
1293 | /// |
1294 | /// Subclasses should override this method to release any resources retained |
1295 | /// by this object (e.g., stop any active animations). |
1296 | /// |
1297 | /// {@macro flutter.widgets.State.initState} |
1298 | /// |
1299 | /// Implementations of this method should end with a call to the inherited |
1300 | /// method, as in `super.dispose()`. |
1301 | /// |
1302 | /// ## Caveats |
1303 | /// |
1304 | /// This method is _not_ invoked at times where a developer might otherwise |
1305 | /// expect it, such as application shutdown or dismissal via platform |
1306 | /// native methods. |
1307 | /// |
1308 | /// ### Application shutdown |
1309 | /// |
1310 | /// There is no way to predict when application shutdown will happen. For |
1311 | /// example, a user's battery could catch fire, or the user could drop the |
1312 | /// device into a swimming pool, or the operating system could unilaterally |
1313 | /// terminate the application process due to memory pressure. |
1314 | /// |
1315 | /// Applications are responsible for ensuring that they are well-behaved |
1316 | /// even in the face of a rapid unscheduled termination. |
1317 | /// |
1318 | /// To artificially cause the entire widget tree to be disposed, consider |
1319 | /// calling [runApp] with a widget such as [SizedBox.shrink]. |
1320 | /// |
1321 | /// To listen for platform shutdown messages (and other lifecycle changes), |
1322 | /// consider the [AppLifecycleListener] API. |
1323 | /// |
1324 | /// {@macro flutter.widgets.runApp.dismissal} |
1325 | /// |
1326 | /// See the method used to bootstrap the app (e.g. [runApp] or [runWidget]) |
1327 | /// for suggestions on how to release resources more eagerly. |
1328 | /// |
1329 | /// See also: |
1330 | /// |
1331 | /// * [deactivate], which is called prior to [dispose]. |
1332 | @protected |
1333 | @mustCallSuper |
1334 | void dispose() { |
1335 | assert(_debugLifecycleState == _StateLifecycle.ready); |
1336 | assert(() { |
1337 | _debugLifecycleState = _StateLifecycle.defunct; |
1338 | return true; |
1339 | }()); |
1340 | assert(debugMaybeDispatchDisposed(this)); |
1341 | } |
1342 | |
1343 | /// Describes the part of the user interface represented by this widget. |
1344 | /// |
1345 | /// The framework calls this method in a number of different situations. For |
1346 | /// example: |
1347 | /// |
1348 | /// * After calling [initState]. |
1349 | /// * After calling [didUpdateWidget]. |
1350 | /// * After receiving a call to [setState]. |
1351 | /// * After a dependency of this [State] object changes (e.g., an |
1352 | /// [InheritedWidget] referenced by the previous [build] changes). |
1353 | /// * After calling [deactivate] and then reinserting the [State] object into |
1354 | /// the tree at another location. |
1355 | /// |
1356 | /// This method can potentially be called in every frame and should not have |
1357 | /// any side effects beyond building a widget. |
1358 | /// |
1359 | /// The framework replaces the subtree below this widget with the widget |
1360 | /// returned by this method, either by updating the existing subtree or by |
1361 | /// removing the subtree and inflating a new subtree, depending on whether the |
1362 | /// widget returned by this method can update the root of the existing |
1363 | /// subtree, as determined by calling [Widget.canUpdate]. |
1364 | /// |
1365 | /// Typically implementations return a newly created constellation of widgets |
1366 | /// that are configured with information from this widget's constructor, the |
1367 | /// given [BuildContext], and the internal state of this [State] object. |
1368 | /// |
1369 | /// The given [BuildContext] contains information about the location in the |
1370 | /// tree at which this widget is being built. For example, the context |
1371 | /// provides the set of inherited widgets for this location in the tree. The |
1372 | /// [BuildContext] argument is always the same as the [context] property of |
1373 | /// this [State] object and will remain the same for the lifetime of this |
1374 | /// object. The [BuildContext] argument is provided redundantly here so that |
1375 | /// this method matches the signature for a [WidgetBuilder]. |
1376 | /// |
1377 | /// ## Design discussion |
1378 | /// |
1379 | /// ### Why is the [build] method on [State], and not [StatefulWidget]? |
1380 | /// |
1381 | /// Putting a `Widget build(BuildContext context)` method on [State] rather |
1382 | /// than putting a `Widget build(BuildContext context, State state)` method |
1383 | /// on [StatefulWidget] gives developers more flexibility when subclassing |
1384 | /// [StatefulWidget]. |
1385 | /// |
1386 | /// For example, [AnimatedWidget] is a subclass of [StatefulWidget] that |
1387 | /// introduces an abstract `Widget build(BuildContext context)` method for its |
1388 | /// subclasses to implement. If [StatefulWidget] already had a [build] method |
1389 | /// that took a [State] argument, [AnimatedWidget] would be forced to provide |
1390 | /// its [State] object to subclasses even though its [State] object is an |
1391 | /// internal implementation detail of [AnimatedWidget]. |
1392 | /// |
1393 | /// Conceptually, [StatelessWidget] could also be implemented as a subclass of |
1394 | /// [StatefulWidget] in a similar manner. If the [build] method were on |
1395 | /// [StatefulWidget] rather than [State], that would not be possible anymore. |
1396 | /// |
1397 | /// Putting the [build] function on [State] rather than [StatefulWidget] also |
1398 | /// helps avoid a category of bugs related to closures implicitly capturing |
1399 | /// `this`. If you defined a closure in a [build] function on a |
1400 | /// [StatefulWidget], that closure would implicitly capture `this`, which is |
1401 | /// the current widget instance, and would have the (immutable) fields of that |
1402 | /// instance in scope: |
1403 | /// |
1404 | /// ```dart |
1405 | /// // (this is not valid Flutter code) |
1406 | /// class MyButton extends StatefulWidgetX { |
1407 | /// MyButton({super.key, required this.color}); |
1408 | /// |
1409 | /// final Color color; |
1410 | /// |
1411 | /// @override |
1412 | /// Widget build(BuildContext context, State state) { |
1413 | /// return SpecialWidget( |
1414 | /// handler: () { print('color: $color'); }, |
1415 | /// ); |
1416 | /// } |
1417 | /// } |
1418 | /// ``` |
1419 | /// |
1420 | /// For example, suppose the parent builds `MyButton` with `color` being blue, |
1421 | /// the `$color` in the print function refers to blue, as expected. Now, |
1422 | /// suppose the parent rebuilds `MyButton` with green. The closure created by |
1423 | /// the first build still implicitly refers to the original widget and the |
1424 | /// `$color` still prints blue even through the widget has been updated to |
1425 | /// green; should that closure outlive its widget, it would print outdated |
1426 | /// information. |
1427 | /// |
1428 | /// In contrast, with the [build] function on the [State] object, closures |
1429 | /// created during [build] implicitly capture the [State] instance instead of |
1430 | /// the widget instance: |
1431 | /// |
1432 | /// ```dart |
1433 | /// class MyButton extends StatefulWidget { |
1434 | /// const MyButton({super.key, this.color = Colors.teal}); |
1435 | /// |
1436 | /// final Color color; |
1437 | /// // ... |
1438 | /// } |
1439 | /// |
1440 | /// class MyButtonState extends State<MyButton> { |
1441 | /// // ... |
1442 | /// @override |
1443 | /// Widget build(BuildContext context) { |
1444 | /// return SpecialWidget( |
1445 | /// handler: () { print('color: ${widget.color}'); }, |
1446 | /// ); |
1447 | /// } |
1448 | /// } |
1449 | /// ``` |
1450 | /// |
1451 | /// Now when the parent rebuilds `MyButton` with green, the closure created by |
1452 | /// the first build still refers to [State] object, which is preserved across |
1453 | /// rebuilds, but the framework has updated that [State] object's [widget] |
1454 | /// property to refer to the new `MyButton` instance and `${widget.color}` |
1455 | /// prints green, as expected. |
1456 | /// |
1457 | /// See also: |
1458 | /// |
1459 | /// * [StatefulWidget], which contains the discussion on performance considerations. |
1460 | @protected |
1461 | Widget build(BuildContext context); |
1462 | |
1463 | /// Called when a dependency of this [State] object changes. |
1464 | /// |
1465 | /// For example, if the previous call to [build] referenced an |
1466 | /// [InheritedWidget] that later changed, the framework would call this |
1467 | /// method to notify this object about the change. |
1468 | /// |
1469 | /// This method is also called immediately after [initState]. It is safe to |
1470 | /// call [BuildContext.dependOnInheritedWidgetOfExactType] from this method. |
1471 | /// |
1472 | /// Subclasses rarely override this method because the framework always |
1473 | /// calls [build] after a dependency changes. Some subclasses do override |
1474 | /// this method because they need to do some expensive work (e.g., network |
1475 | /// fetches) when their dependencies change, and that work would be too |
1476 | /// expensive to do for every build. |
1477 | @protected |
1478 | @mustCallSuper |
1479 | void didChangeDependencies() {} |
1480 | |
1481 | @override |
1482 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1483 | super.debugFillProperties(properties); |
1484 | assert(() { |
1485 | properties.add( |
1486 | EnumProperty<_StateLifecycle>( |
1487 | 'lifecycle state', |
1488 | _debugLifecycleState, |
1489 | defaultValue: _StateLifecycle.ready, |
1490 | ), |
1491 | ); |
1492 | return true; |
1493 | }()); |
1494 | properties.add(ObjectFlagProperty<T>('_widget', _widget, ifNull:'no widget')); |
1495 | properties.add( |
1496 | ObjectFlagProperty<StatefulElement>('_element', _element, ifNull:'not mounted'), |
1497 | ); |
1498 | } |
1499 | |
1500 | // If @protected State methods are added or removed, the analysis rule should be |
1501 | // updated accordingly (dev/bots/custom_rules/protect_public_state_subtypes.dart) |
1502 | } |
1503 | |
1504 | /// A widget that has a child widget provided to it, instead of building a new |
1505 | /// widget. |
1506 | /// |
1507 | /// Useful as a base class for other widgets, such as [InheritedWidget] and |
1508 | /// [ParentDataWidget]. |
1509 | /// |
1510 | /// See also: |
1511 | /// |
1512 | /// * [InheritedWidget], for widgets that introduce ambient state that can |
1513 | /// be read by descendant widgets. |
1514 | /// * [ParentDataWidget], for widgets that populate the |
1515 | /// [RenderObject.parentData] slot of their child's [RenderObject] to |
1516 | /// configure the parent widget's layout. |
1517 | /// * [StatefulWidget] and [State], for widgets that can build differently |
1518 | /// several times over their lifetime. |
1519 | /// * [StatelessWidget], for widgets that always build the same way given a |
1520 | /// particular configuration and ambient state. |
1521 | /// * [Widget], for an overview of widgets in general. |
1522 | abstract class ProxyWidget extends Widget { |
1523 | /// Creates a widget that has exactly one child widget. |
1524 | const ProxyWidget({super.key, required this.child}); |
1525 | |
1526 | /// The widget below this widget in the tree. |
1527 | /// |
1528 | /// {@template flutter.widgets.ProxyWidget.child} |
1529 | /// This widget can only have one child. To lay out multiple children, let this |
1530 | /// widget's child be a widget such as [Row], [Column], or [Stack], which have a |
1531 | /// `children` property, and then provide the children to that widget. |
1532 | /// {@endtemplate} |
1533 | final Widget child; |
1534 | } |
1535 | |
1536 | /// Base class for widgets that hook [ParentData] information to children of |
1537 | /// [RenderObjectWidget]s. |
1538 | /// |
1539 | /// This can be used to provide per-child configuration for |
1540 | /// [RenderObjectWidget]s with more than one child. For example, [Stack] uses |
1541 | /// the [Positioned] parent data widget to position each child. |
1542 | /// |
1543 | /// A [ParentDataWidget] is specific to a particular kind of [ParentData]. That |
1544 | /// class is `T`, the [ParentData] type argument. |
1545 | /// |
1546 | /// {@tool snippet} |
1547 | /// |
1548 | /// This example shows how you would build a [ParentDataWidget] to configure a |
1549 | /// `FrogJar` widget's children by specifying a [Size] for each one. |
1550 | /// |
1551 | /// ```dart |
1552 | /// class FrogSize extends ParentDataWidget<FrogJarParentData> { |
1553 | /// const FrogSize({ |
1554 | /// super.key, |
1555 | /// required this.size, |
1556 | /// required super.child, |
1557 | /// }); |
1558 | /// |
1559 | /// final Size size; |
1560 | /// |
1561 | /// @override |
1562 | /// void applyParentData(RenderObject renderObject) { |
1563 | /// final FrogJarParentData parentData = renderObject.parentData! as FrogJarParentData; |
1564 | /// if (parentData.size != size) { |
1565 | /// parentData.size = size; |
1566 | /// final RenderFrogJar targetParent = renderObject.parent! as RenderFrogJar; |
1567 | /// targetParent.markNeedsLayout(); |
1568 | /// } |
1569 | /// } |
1570 | /// |
1571 | /// @override |
1572 | /// Type get debugTypicalAncestorWidgetClass => FrogJar; |
1573 | /// } |
1574 | /// ``` |
1575 | /// {@end-tool} |
1576 | /// |
1577 | /// See also: |
1578 | /// |
1579 | /// * [RenderObject], the superclass for layout algorithms. |
1580 | /// * [RenderObject.parentData], the slot that this class configures. |
1581 | /// * [ParentData], the superclass of the data that will be placed in |
1582 | /// [RenderObject.parentData] slots. The `T` type parameter for |
1583 | /// [ParentDataWidget] is a [ParentData]. |
1584 | /// * [RenderObjectWidget], the class for widgets that wrap [RenderObject]s. |
1585 | /// * [StatefulWidget] and [State], for widgets that can build differently |
1586 | /// several times over their lifetime. |
1587 | abstract class ParentDataWidget<T extends ParentData> extends ProxyWidget { |
1588 | /// Abstract const constructor. This constructor enables subclasses to provide |
1589 | /// const constructors so that they can be used in const expressions. |
1590 | const ParentDataWidget({super.key, required super.child}); |
1591 | |
1592 | @override |
1593 | ParentDataElement<T> createElement() => ParentDataElement<T>(this); |
1594 | |
1595 | /// Checks if this widget can apply its parent data to the provided |
1596 | /// `renderObject`. |
1597 | /// |
1598 | /// The [RenderObject.parentData] of the provided `renderObject` is |
1599 | /// typically set up by an ancestor [RenderObjectWidget] of the type returned |
1600 | /// by [debugTypicalAncestorWidgetClass]. |
1601 | /// |
1602 | /// This is called just before [applyParentData] is invoked with the same |
1603 | /// [RenderObject] provided to that method. |
1604 | bool debugIsValidRenderObject(RenderObject renderObject) { |
1605 | assert(T != dynamic); |
1606 | assert(T != ParentData); |
1607 | return renderObject.parentData is T; |
1608 | } |
1609 | |
1610 | /// Describes the [RenderObjectWidget] that is typically used to set up the |
1611 | /// [ParentData] that [applyParentData] will write to. |
1612 | /// |
1613 | /// This is only used in error messages to tell users what widget typically |
1614 | /// wraps this [ParentDataWidget] through |
1615 | /// [debugTypicalAncestorWidgetDescription]. |
1616 | /// |
1617 | /// ## Implementations |
1618 | /// |
1619 | /// The returned Type should describe a subclass of `RenderObjectWidget`. If |
1620 | /// more than one Type is supported, use |
1621 | /// [debugTypicalAncestorWidgetDescription], which typically inserts this |
1622 | /// value but can be overridden to describe more than one Type. |
1623 | /// |
1624 | /// ```dart |
1625 | /// @override |
1626 | /// Type get debugTypicalAncestorWidgetClass => FrogJar; |
1627 | /// ``` |
1628 | /// |
1629 | /// If the "typical" parent is generic (`Foo<T>`), consider specifying either |
1630 | /// a typical type argument (e.g. `Foo<int>` if `int` is typically how the |
1631 | /// type is specialized), or specifying the upper bound (e.g. `Foo<Object?>`). |
1632 | Type get debugTypicalAncestorWidgetClass; |
1633 | |
1634 | /// Describes the [RenderObjectWidget] that is typically used to set up the |
1635 | /// [ParentData] that [applyParentData] will write to. |
1636 | /// |
1637 | /// This is only used in error messages to tell users what widget typically |
1638 | /// wraps this [ParentDataWidget]. |
1639 | /// |
1640 | /// Returns [debugTypicalAncestorWidgetClass] by default as a String. This can |
1641 | /// be overridden to describe more than one Type of valid parent. |
1642 | String get debugTypicalAncestorWidgetDescription =>'$debugTypicalAncestorWidgetClass'; |
1643 | |
1644 | Iterable<DiagnosticsNode> _debugDescribeIncorrectParentDataType({ |
1645 | required ParentData? parentData, |
1646 | RenderObjectWidget? parentDataCreator, |
1647 | DiagnosticsNode? ownershipChain, |
1648 | }) { |
1649 | assert(T != dynamic); |
1650 | assert(T != ParentData); |
1651 | |
1652 | final String description = |
1653 | 'The ParentDataWidget$thiswants to apply ParentData of type$Tto a RenderObject'; |
1654 | return <DiagnosticsNode>[ |
1655 | if (parentData == null) |
1656 | ErrorDescription('$description, which has not been set up to receive any ParentData.') |
1657 | else |
1658 | ErrorDescription( |
1659 | '$description, which has been set up to accept ParentData of incompatible type${parentData.runtimeType}.', |
1660 | ), |
1661 | ErrorHint( |
1662 | 'Usually, this means that the$runtimeTypewidget has the wrong ancestor RenderObjectWidget. ' |
1663 | 'Typically,$runtimeTypewidgets are placed directly inside$debugTypicalAncestorWidgetDescriptionwidgets.', |
1664 | ), |
1665 | if (parentDataCreator != null) |
1666 | ErrorHint( |
1667 | 'The offending$runtimeTypeis currently placed inside a${parentDataCreator.runtimeType}widget.', |
1668 | ), |
1669 | if (ownershipChain != null) |
1670 | ErrorDescription( |
1671 | 'The ownership chain for the RenderObject that received the incompatible parent data was:\n$ownershipChain', |
1672 | ), |
1673 | ]; |
1674 | } |
1675 | |
1676 | /// Write the data from this widget into the given render object's parent data. |
1677 | /// |
1678 | /// The framework calls this function whenever it detects that the |
1679 | /// [RenderObject] associated with the [child] has outdated |
1680 | /// [RenderObject.parentData]. For example, if the render object was recently |
1681 | /// inserted into the render tree, the render object's parent data might not |
1682 | /// match the data in this widget. |
1683 | /// |
1684 | /// Subclasses are expected to override this function to copy data from their |
1685 | /// fields into the [RenderObject.parentData] field of the given render |
1686 | /// object. The render object's parent is guaranteed to have been created by a |
1687 | /// widget of type `T`, which usually means that this function can assume that |
1688 | /// the render object's parent data object inherits from a particular class. |
1689 | /// |
1690 | /// If this function modifies data that can change the parent's layout or |
1691 | /// painting, this function is responsible for calling |
1692 | /// [RenderObject.markNeedsLayout] or [RenderObject.markNeedsPaint] on the |
1693 | /// parent, as appropriate. |
1694 | @protected |
1695 | void applyParentData(RenderObject renderObject); |
1696 | |
1697 | /// Whether the [ParentDataElement.applyWidgetOutOfTurn] method is allowed |
1698 | /// with this widget. |
1699 | /// |
1700 | /// This should only return true if this widget represents a [ParentData] |
1701 | /// configuration that will have no impact on the layout or paint phase. |
1702 | /// |
1703 | /// See also: |
1704 | /// |
1705 | /// * [ParentDataElement.applyWidgetOutOfTurn], which verifies this in debug |
1706 | /// mode. |
1707 | @protected |
1708 | bool debugCanApplyOutOfTurn() => false; |
1709 | } |
1710 | |
1711 | /// Base class for widgets that efficiently propagate information down the tree. |
1712 | /// |
1713 | /// {@youtube 560 315 https://www.youtube.com/watch?v=og-vJqLzg2c} |
1714 | /// |
1715 | /// To obtain the nearest instance of a particular type of inherited widget from |
1716 | /// a build context, use [BuildContext.dependOnInheritedWidgetOfExactType]. |
1717 | /// |
1718 | /// Inherited widgets, when referenced in this way, will cause the consumer to |
1719 | /// rebuild when the inherited widget itself changes state. |
1720 | /// |
1721 | /// {@youtube 560 315 https://www.youtube.com/watch?v=Zbm3hjPjQMk} |
1722 | /// |
1723 | /// {@tool snippet} |
1724 | /// |
1725 | /// The following is a skeleton of an inherited widget called `FrogColor`: |
1726 | /// |
1727 | /// ```dart |
1728 | /// class FrogColor extends InheritedWidget { |
1729 | /// const FrogColor({ |
1730 | /// super.key, |
1731 | /// required this.color, |
1732 | /// required super.child, |
1733 | /// }); |
1734 | /// |
1735 | /// final Color color; |
1736 | /// |
1737 | /// static FrogColor? maybeOf(BuildContext context) { |
1738 | /// return context.dependOnInheritedWidgetOfExactType<FrogColor>(); |
1739 | /// } |
1740 | /// |
1741 | /// static FrogColor of(BuildContext context) { |
1742 | /// final FrogColor? result = maybeOf(context); |
1743 | /// assert(result != null, 'No FrogColor found in context'); |
1744 | /// return result!; |
1745 | /// } |
1746 | /// |
1747 | /// @override |
1748 | /// bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color; |
1749 | /// } |
1750 | /// ``` |
1751 | /// {@end-tool} |
1752 | /// |
1753 | /// ## Implementing the `of` and `maybeOf` methods |
1754 | /// |
1755 | /// The convention is to provide two static methods, `of` and `maybeOf`, on the |
1756 | /// [InheritedWidget] which call |
1757 | /// [BuildContext.dependOnInheritedWidgetOfExactType]. This allows the class to |
1758 | /// define its own fallback logic in case there isn't a widget in scope. |
1759 | /// |
1760 | /// The `of` method typically returns a non-nullable instance and asserts if the |
1761 | /// [InheritedWidget] isn't found, and the `maybeOf` method returns a nullable |
1762 | /// instance, and returns null if the [InheritedWidget] isn't found. The `of` |
1763 | /// method is typically implemented by calling `maybeOf` internally. |
1764 | /// |
1765 | /// Sometimes, the `of` and `maybeOf` methods return some data rather than the |
1766 | /// inherited widget itself; for example, in this case it could have returned a |
1767 | /// [Color] instead of the `FrogColor` widget. |
1768 | /// |
1769 | /// Occasionally, the inherited widget is an implementation detail of another |
1770 | /// class, and is therefore private. The `of` and `maybeOf` methods in that case |
1771 | /// are typically implemented on the public class instead. For example, [Theme] |
1772 | /// is implemented as a [StatelessWidget] that builds a private inherited |
1773 | /// widget; [Theme.of] looks for that private inherited widget using |
1774 | /// [BuildContext.dependOnInheritedWidgetOfExactType] and then returns the |
1775 | /// [ThemeData] inside it. |
1776 | /// |
1777 | /// ## Calling the `of` or `maybeOf` methods |
1778 | /// |
1779 | /// When using the `of` or `maybeOf` methods, the `context` must be a descendant |
1780 | /// of the [InheritedWidget], meaning it must be "below" the [InheritedWidget] |
1781 | /// in the tree. |
1782 | /// |
1783 | /// {@tool snippet} |
1784 | /// |
1785 | /// In this example, the `context` used is the one from the [Builder], which is |
1786 | /// a child of the `FrogColor` widget, so this works. |
1787 | /// |
1788 | /// ```dart |
1789 | /// // continuing from previous example... |
1790 | /// class MyPage extends StatelessWidget { |
1791 | /// const MyPage({super.key}); |
1792 | /// |
1793 | /// @override |
1794 | /// Widget build(BuildContext context) { |
1795 | /// return Scaffold( |
1796 | /// body: FrogColor( |
1797 | /// color: Colors.green, |
1798 | /// child: Builder( |
1799 | /// builder: (BuildContext innerContext) { |
1800 | /// return Text( |
1801 | /// 'Hello Frog', |
1802 | /// style: TextStyle(color: FrogColor.of(innerContext).color), |
1803 | /// ); |
1804 | /// }, |
1805 | /// ), |
1806 | /// ), |
1807 | /// ); |
1808 | /// } |
1809 | /// } |
1810 | /// ``` |
1811 | /// {@end-tool} |
1812 | /// |
1813 | /// {@tool snippet} |
1814 | /// |
1815 | /// In this example, the `context` used is the one from the `MyOtherPage` |
1816 | /// widget, which is a parent of the `FrogColor` widget, so this does not work, |
1817 | /// and will assert when `FrogColor.of` is called. |
1818 | /// |
1819 | /// ```dart |
1820 | /// // continuing from previous example... |
1821 | /// |
1822 | /// class MyOtherPage extends StatelessWidget { |
1823 | /// const MyOtherPage({super.key}); |
1824 | /// |
1825 | /// @override |
1826 | /// Widget build(BuildContext context) { |
1827 | /// return Scaffold( |
1828 | /// body: FrogColor( |
1829 | /// color: Colors.green, |
1830 | /// child: Text( |
1831 | /// 'Hello Frog', |
1832 | /// style: TextStyle(color: FrogColor.of(context).color), |
1833 | /// ), |
1834 | /// ), |
1835 | /// ); |
1836 | /// } |
1837 | /// } |
1838 | /// ``` |
1839 | /// {@end-tool} {@youtube 560 315 https://www.youtube.com/watch?v=1t-8rBCGBYw} |
1840 | /// |
1841 | /// See also: |
1842 | /// |
1843 | /// * [StatefulWidget] and [State], for widgets that can build differently |
1844 | /// several times over their lifetime. |
1845 | /// * [StatelessWidget], for widgets that always build the same way given a |
1846 | /// particular configuration and ambient state. |
1847 | /// * [Widget], for an overview of widgets in general. |
1848 | /// * [InheritedNotifier], an inherited widget whose value can be a |
1849 | /// [Listenable], and which will notify dependents whenever the value sends |
1850 | /// notifications. |
1851 | /// * [InheritedModel], an inherited widget that allows clients to subscribe to |
1852 | /// changes for subparts of the value. |
1853 | abstract class InheritedWidget extends ProxyWidget { |
1854 | /// Abstract const constructor. This constructor enables subclasses to provide |
1855 | /// const constructors so that they can be used in const expressions. |
1856 | const InheritedWidget({super.key, required super.child}); |
1857 | |
1858 | @override |
1859 | InheritedElement createElement() => InheritedElement(this); |
1860 | |
1861 | /// Whether the framework should notify widgets that inherit from this widget. |
1862 | /// |
1863 | /// When this widget is rebuilt, sometimes we need to rebuild the widgets that |
1864 | /// inherit from this widget but sometimes we do not. For example, if the data |
1865 | /// held by this widget is the same as the data held by `oldWidget`, then we |
1866 | /// do not need to rebuild the widgets that inherited the data held by |
1867 | /// `oldWidget`. |
1868 | /// |
1869 | /// The framework distinguishes these cases by calling this function with the |
1870 | /// widget that previously occupied this location in the tree as an argument. |
1871 | /// The given widget is guaranteed to have the same [runtimeType] as this |
1872 | /// object. |
1873 | @protected |
1874 | bool updateShouldNotify(covariant InheritedWidget oldWidget); |
1875 | } |
1876 | |
1877 | /// [RenderObjectWidget]s provide the configuration for [RenderObjectElement]s, |
1878 | /// which wrap [RenderObject]s, which provide the actual rendering of the |
1879 | /// application. |
1880 | /// |
1881 | /// Usually, rather than subclassing [RenderObjectWidget] directly, render |
1882 | /// object widgets subclass one of: |
1883 | /// |
1884 | /// * [LeafRenderObjectWidget], if the widget has no children. |
1885 | /// * [SingleChildRenderObjectWidget], if the widget has exactly one child. |
1886 | /// * [MultiChildRenderObjectWidget], if the widget takes a list of children. |
1887 | /// * [SlottedMultiChildRenderObjectWidget], if the widget organizes its |
1888 | /// children in different named slots. |
1889 | /// |
1890 | /// Subclasses must implement [createRenderObject] and [updateRenderObject]. |
1891 | abstract class RenderObjectWidget extends Widget { |
1892 | /// Abstract const constructor. This constructor enables subclasses to provide |
1893 | /// const constructors so that they can be used in const expressions. |
1894 | const RenderObjectWidget({super.key}); |
1895 | |
1896 | /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass. |
1897 | @override |
1898 | @factory |
1899 | RenderObjectElement createElement(); |
1900 | |
1901 | /// Creates an instance of the [RenderObject] class that this |
1902 | /// [RenderObjectWidget] represents, using the configuration described by this |
1903 | /// [RenderObjectWidget]. |
1904 | /// |
1905 | /// This method should not do anything with the children of the render object. |
1906 | /// That should instead be handled by the method that overrides |
1907 | /// [RenderObjectElement.mount] in the object rendered by this object's |
1908 | /// [createElement] method. See, for example, |
1909 | /// [SingleChildRenderObjectElement.mount]. |
1910 | @protected |
1911 | @factory |
1912 | RenderObject createRenderObject(BuildContext context); |
1913 | |
1914 | /// Copies the configuration described by this [RenderObjectWidget] to the |
1915 | /// given [RenderObject], which will be of the same type as returned by this |
1916 | /// object's [createRenderObject]. |
1917 | /// |
1918 | /// This method should not do anything to update the children of the render |
1919 | /// object. That should instead be handled by the method that overrides |
1920 | /// [RenderObjectElement.update] in the object rendered by this object's |
1921 | /// [createElement] method. See, for example, |
1922 | /// [SingleChildRenderObjectElement.update]. |
1923 | @protected |
1924 | void updateRenderObject(BuildContext context, covariant RenderObject renderObject) {} |
1925 | |
1926 | /// A render object previously associated with this widget has been removed |
1927 | /// from the tree. The given [RenderObject] will be of the same type as |
1928 | /// returned by this object's [createRenderObject]. |
1929 | @protected |
1930 | void didUnmountRenderObject(covariant RenderObject renderObject) {} |
1931 | } |
1932 | |
1933 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses |
1934 | /// that have no children. |
1935 | /// |
1936 | /// Subclasses must implement [createRenderObject] and [updateRenderObject]. |
1937 | abstract class LeafRenderObjectWidget extends RenderObjectWidget { |
1938 | /// Abstract const constructor. This constructor enables subclasses to provide |
1939 | /// const constructors so that they can be used in const expressions. |
1940 | const LeafRenderObjectWidget({super.key}); |
1941 | |
1942 | @override |
1943 | LeafRenderObjectElement createElement() => LeafRenderObjectElement(this); |
1944 | } |
1945 | |
1946 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses |
1947 | /// that have a single child slot. |
1948 | /// |
1949 | /// The render object assigned to this widget should make use of |
1950 | /// [RenderObjectWithChildMixin] to implement a single-child model. The mixin |
1951 | /// exposes a [RenderObjectWithChildMixin.child] property that allows retrieving |
1952 | /// the render object belonging to the [child] widget. |
1953 | /// |
1954 | /// Subclasses must implement [createRenderObject] and [updateRenderObject]. |
1955 | abstract class SingleChildRenderObjectWidget extends RenderObjectWidget { |
1956 | /// Abstract const constructor. This constructor enables subclasses to provide |
1957 | /// const constructors so that they can be used in const expressions. |
1958 | const SingleChildRenderObjectWidget({super.key, this.child}); |
1959 | |
1960 | /// The widget below this widget in the tree. |
1961 | /// |
1962 | /// {@macro flutter.widgets.ProxyWidget.child} |
1963 | final Widget? child; |
1964 | |
1965 | @override |
1966 | SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this); |
1967 | } |
1968 | |
1969 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses |
1970 | /// that have a single list of children. (This superclass only provides the |
1971 | /// storage for that child list, it doesn't actually provide the updating |
1972 | /// logic.) |
1973 | /// |
1974 | /// Subclasses must use a [RenderObject] that mixes in |
1975 | /// [ContainerRenderObjectMixin], which provides the necessary functionality to |
1976 | /// visit the children of the container render object (the render object |
1977 | /// belonging to the [children] widgets). Typically, subclasses will use a |
1978 | /// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and |
1979 | /// [RenderBoxContainerDefaultsMixin]. |
1980 | /// |
1981 | /// Subclasses must implement [createRenderObject] and [updateRenderObject]. |
1982 | /// |
1983 | /// See also: |
1984 | /// |
1985 | /// * [Stack], which uses [MultiChildRenderObjectWidget]. |
1986 | /// * [RenderStack], for an example implementation of the associated render |
1987 | /// object. |
1988 | /// * [SlottedMultiChildRenderObjectWidget], which configures a |
1989 | /// [RenderObject] that instead of having a single list of children organizes |
1990 | /// its children in named slots. |
1991 | abstract class MultiChildRenderObjectWidget extends RenderObjectWidget { |
1992 | /// Initializes fields for subclasses. |
1993 | const MultiChildRenderObjectWidget({super.key, this.children = const <Widget>[]}); |
1994 | |
1995 | /// The widgets below this widget in the tree. |
1996 | /// |
1997 | /// If this list is going to be mutated, it is usually wise to put a [Key] on |
1998 | /// each of the child widgets, so that the framework can match old |
1999 | /// configurations to new configurations and maintain the underlying render |
2000 | /// objects. |
2001 | /// |
2002 | /// Also, a [Widget] in Flutter is immutable, so directly modifying the |
2003 | /// [children] such as `someMultiChildRenderObjectWidget.children.add(...)` or |
2004 | /// as the example code below will result in incorrect behaviors. Whenever the |
2005 | /// children list is modified, a new list object should be provided. |
2006 | /// |
2007 | /// ```dart |
2008 | /// // This code is incorrect. |
2009 | /// class SomeWidgetState extends State<SomeWidget> { |
2010 | /// final List<Widget> _children = <Widget>[]; |
2011 | /// |
2012 | /// void someHandler() { |
2013 | /// setState(() { |
2014 | /// _children.add(const ChildWidget()); |
2015 | /// }); |
2016 | /// } |
2017 | /// |
2018 | /// @override |
2019 | /// Widget build(BuildContext context) { |
2020 | /// // Reusing `List |
2021 | /// return Row(children: _children); |
2022 | /// } |
2023 | /// } |
2024 | /// ``` |
2025 | /// |
2026 | /// The following code corrects the problem mentioned above. |
2027 | /// |
2028 | /// ```dart |
2029 | /// class SomeWidgetState extends State<SomeWidget> { |
2030 | /// final List<Widget> _children = <Widget>[]; |
2031 | /// |
2032 | /// void someHandler() { |
2033 | /// setState(() { |
2034 | /// // The key here allows Flutter to reuse the underlying render |
2035 | /// // objects even if the children list is recreated. |
2036 | /// _children.add(ChildWidget(key: UniqueKey())); |
2037 | /// }); |
2038 | /// } |
2039 | /// |
2040 | /// @override |
2041 | /// Widget build(BuildContext context) { |
2042 | /// // Always create a new list of children as a Widget is immutable. |
2043 | /// return Row(children: _children.toList()); |
2044 | /// } |
2045 | /// } |
2046 | /// ``` |
2047 | final List<Widget> children; |
2048 | |
2049 | @override |
2050 | MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this); |
2051 | } |
2052 | |
2053 | // ELEMENTS |
2054 | |
2055 | enum _ElementLifecycle { initial, active, inactive, defunct } |
2056 | |
2057 | class _InactiveElements { |
2058 | bool _locked = false; |
2059 | final Set<Element> _elements = HashSet<Element>(); |
2060 | |
2061 | void _unmount(Element element) { |
2062 | assert(element._lifecycleState == _ElementLifecycle.inactive); |
2063 | assert(() { |
2064 | if (debugPrintGlobalKeyedWidgetLifecycle) { |
2065 | if (element.widget.key is GlobalKey) { |
2066 | debugPrint('Discarding$elementfrom inactive elements list.'); |
2067 | } |
2068 | } |
2069 | return true; |
2070 | }()); |
2071 | element.visitChildren((Element child) { |
2072 | assert(child._parent == element); |
2073 | _unmount(child); |
2074 | }); |
2075 | element.unmount(); |
2076 | assert(element._lifecycleState == _ElementLifecycle.defunct); |
2077 | } |
2078 | |
2079 | void _unmountAll() { |
2080 | _locked = true; |
2081 | final List<Element> elements = _elements.toList()..sort(Element._sort); |
2082 | _elements.clear(); |
2083 | try { |
2084 | elements.reversed.forEach(_unmount); |
2085 | } finally { |
2086 | assert(_elements.isEmpty); |
2087 | _locked = false; |
2088 | } |
2089 | } |
2090 | |
2091 | static void _deactivateRecursively(Element element) { |
2092 | assert(element._lifecycleState == _ElementLifecycle.active); |
2093 | element.deactivate(); |
2094 | assert(element._lifecycleState == _ElementLifecycle.inactive); |
2095 | element.visitChildren(_deactivateRecursively); |
2096 | assert(() { |
2097 | element.debugDeactivated(); |
2098 | return true; |
2099 | }()); |
2100 | } |
2101 | |
2102 | void add(Element element) { |
2103 | assert(!_locked); |
2104 | assert(!_elements.contains(element)); |
2105 | assert(element._parent == null); |
2106 | if (element._lifecycleState == _ElementLifecycle.active) { |
2107 | _deactivateRecursively(element); |
2108 | } |
2109 | _elements.add(element); |
2110 | } |
2111 | |
2112 | void remove(Element element) { |
2113 | assert(!_locked); |
2114 | assert(_elements.contains(element)); |
2115 | assert(element._parent == null); |
2116 | _elements.remove(element); |
2117 | assert(element._lifecycleState != _ElementLifecycle.active); |
2118 | } |
2119 | |
2120 | bool debugContains(Element element) { |
2121 | late bool result; |
2122 | assert(() { |
2123 | result = _elements.contains(element); |
2124 | return true; |
2125 | }()); |
2126 | return result; |
2127 | } |
2128 | } |
2129 | |
2130 | /// Signature for the callback to [BuildContext.visitChildElements]. |
2131 | /// |
2132 | /// The argument is the child being visited. |
2133 | /// |
2134 | /// It is safe to call `element.visitChildElements` reentrantly within |
2135 | /// this callback. |
2136 | typedef ElementVisitor = void Function(Element element); |
2137 | |
2138 | /// Signature for the callback to [BuildContext.visitAncestorElements]. |
2139 | /// |
2140 | /// The argument is the ancestor being visited. |
2141 | /// |
2142 | /// Return false to stop the walk. |
2143 | typedef ConditionalElementVisitor = bool Function(Element element); |
2144 | |
2145 | /// A handle to the location of a widget in the widget tree. |
2146 | /// |
2147 | /// This class presents a set of methods that can be used from |
2148 | /// [StatelessWidget.build] methods and from methods on [State] objects. |
2149 | /// |
2150 | /// [BuildContext] objects are passed to [WidgetBuilder] functions (such as |
2151 | /// [StatelessWidget.build]), and are available from the [State.context] member. |
2152 | /// Some static functions (e.g. [showDialog], [Theme.of], and so forth) also |
2153 | /// take build contexts so that they can act on behalf of the calling widget, or |
2154 | /// obtain data specifically for the given context. |
2155 | /// |
2156 | /// Each widget has its own [BuildContext], which becomes the parent of the |
2157 | /// widget returned by the [StatelessWidget.build] or [State.build] function. |
2158 | /// (And similarly, the parent of any children for [RenderObjectWidget]s.) |
2159 | /// |
2160 | /// In particular, this means that within a build method, the build context of |
2161 | /// the widget of the build method is not the same as the build context of the |
2162 | /// widgets returned by that build method. This can lead to some tricky cases. |
2163 | /// For example, [Theme.of(context)] looks for the nearest enclosing [Theme] of |
2164 | /// the given build context. If a build method for a widget Q includes a [Theme] |
2165 | /// within its returned widget tree, and attempts to use [Theme.of] passing its |
2166 | /// own context, the build method for Q will not find that [Theme] object. It |
2167 | /// will instead find whatever [Theme] was an ancestor to the widget Q. If the |
2168 | /// build context for a subpart of the returned tree is needed, a [Builder] |
2169 | /// widget can be used: the build context passed to the [Builder.builder] |
2170 | /// callback will be that of the [Builder] itself. |
2171 | /// |
2172 | /// For example, in the following snippet, the [ScaffoldState.showBottomSheet] |
2173 | /// method is called on the [Scaffold] widget that the build method itself |
2174 | /// creates. If a [Builder] had not been used, and instead the `context` |
2175 | /// argument of the build method itself had been used, no [Scaffold] would have |
2176 | /// been found, and the [Scaffold.of] function would have returned null. |
2177 | /// |
2178 | /// ```dart |
2179 | /// @override |
2180 | /// Widget build(BuildContext context) { |
2181 | /// // here, Scaffold.of(context) returns null |
2182 | /// return Scaffold( |
2183 | /// appBar: AppBar(title: const Text('Demo')), |
2184 | /// body: Builder( |
2185 | /// builder: (BuildContext context) { |
2186 | /// return TextButton( |
2187 | /// child: const Text('BUTTON'), |
2188 | /// onPressed: () { |
2189 | /// Scaffold.of(context).showBottomSheet( |
2190 | /// (BuildContext context) { |
2191 | /// return Container( |
2192 | /// alignment: Alignment.center, |
2193 | /// height: 200, |
2194 | /// color: Colors.amber, |
2195 | /// child: Center( |
2196 | /// child: Column( |
2197 | /// mainAxisSize: MainAxisSize.min, |
2198 | /// children: <Widget>[ |
2199 | /// const Text('BottomSheet'), |
2200 | /// ElevatedButton( |
2201 | /// child: const Text('Close BottomSheet'), |
2202 | /// onPressed: () { |
2203 | /// Navigator.pop(context); |
2204 | /// }, |
2205 | /// ) |
2206 | /// ], |
2207 | /// ), |
2208 | /// ), |
2209 | /// ); |
2210 | /// }, |
2211 | /// ); |
2212 | /// }, |
2213 | /// ); |
2214 | /// }, |
2215 | /// ) |
2216 | /// ); |
2217 | /// } |
2218 | /// ``` |
2219 | /// |
2220 | /// The [BuildContext] for a particular widget can change location over time as |
2221 | /// the widget is moved around the tree. Because of this, values returned from |
2222 | /// the methods on this class should not be cached beyond the execution of a |
2223 | /// single synchronous function. |
2224 | /// |
2225 | /// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g} |
2226 | /// |
2227 | /// Avoid storing instances of [BuildContext]s because they may become invalid |
2228 | /// if the widget they are associated with is unmounted from the widget tree. |
2229 | /// {@template flutter.widgets.BuildContext.asynchronous_gap} |
2230 | /// If a [BuildContext] is used across an asynchronous gap (i.e. after performing |
2231 | /// an asynchronous operation), consider checking [mounted] to determine whether |
2232 | /// the context is still valid before interacting with it: |
2233 | /// |
2234 | /// ```dart |
2235 | /// @override |
2236 | /// Widget build(BuildContext context) { |
2237 | /// return OutlinedButton( |
2238 | /// onPressed: () async { |
2239 | /// await Future<void>.delayed(const Duration(seconds: 1)); |
2240 | /// if (context.mounted) { |
2241 | /// Navigator.of(context).pop(); |
2242 | /// } |
2243 | /// }, |
2244 | /// child: const Text('Delayed pop'), |
2245 | /// ); |
2246 | /// } |
2247 | /// ``` |
2248 | /// {@endtemplate} |
2249 | /// |
2250 | /// [BuildContext] objects are actually [Element] objects. The [BuildContext] |
2251 | /// interface is used to discourage direct manipulation of [Element] objects. |
2252 | abstract class BuildContext { |
2253 | /// The current configuration of the [Element] that is this [BuildContext]. |
2254 | Widget get widget; |
2255 | |
2256 | /// The [BuildOwner] for this context. The [BuildOwner] is in charge of |
2257 | /// managing the rendering pipeline for this context. |
2258 | BuildOwner? get owner; |
2259 | |
2260 | /// Whether the [Widget] this context is associated with is currently |
2261 | /// mounted in the widget tree. |
2262 | /// |
2263 | /// Accessing the properties of the [BuildContext] or calling any methods on |
2264 | /// it is only valid while mounted is true. If mounted is false, assertions |
2265 | /// will trigger. |
2266 | /// |
2267 | /// Once unmounted, a given [BuildContext] will never become mounted again. |
2268 | /// |
2269 | /// {@macro flutter.widgets.BuildContext.asynchronous_gap} |
2270 | bool get mounted; |
2271 | |
2272 | /// Whether the [widget] is currently updating the widget or render tree. |
2273 | /// |
2274 | /// For [StatefulWidget]s and [StatelessWidget]s this flag is true while |
2275 | /// their respective build methods are executing. |
2276 | /// [RenderObjectWidget]s set this to true while creating or configuring their |
2277 | /// associated [RenderObject]s. |
2278 | /// Other [Widget] types may set this to true for conceptually similar phases |
2279 | /// of their lifecycle. |
2280 | /// |
2281 | /// When this is true, it is safe for [widget] to establish a dependency to an |
2282 | /// [InheritedWidget] by calling [dependOnInheritedElement] or |
2283 | /// [dependOnInheritedWidgetOfExactType]. |
2284 | /// |
2285 | /// Accessing this flag in release mode is not valid. |
2286 | bool get debugDoingBuild; |
2287 | |
2288 | /// The current [RenderObject] for the widget. If the widget is a |
2289 | /// [RenderObjectWidget], this is the render object that the widget created |
2290 | /// for itself. Otherwise, it is the render object of the first descendant |
2291 | /// [RenderObjectWidget]. |
2292 | /// |
2293 | /// This method will only return a valid result after the build phase is |
2294 | /// complete. It is therefore not valid to call this from a build method. |
2295 | /// It should only be called from interaction event handlers (e.g. |
2296 | /// gesture callbacks) or layout or paint callbacks. It is also not valid to |
2297 | /// call if [State.mounted] returns false. |
2298 | /// |
2299 | /// If the render object is a [RenderBox], which is the common case, then the |
2300 | /// size of the render object can be obtained from the [size] getter. This is |
2301 | /// only valid after the layout phase, and should therefore only be examined |
2302 | /// from paint callbacks or interaction event handlers (e.g. gesture |
2303 | /// callbacks). |
2304 | /// |
2305 | /// For details on the different phases of a frame, see the discussion at |
2306 | /// [WidgetsBinding.drawFrame]. |
2307 | /// |
2308 | /// Calling this method is theoretically relatively expensive (O(N) in the |
2309 | /// depth of the tree), but in practice is usually cheap because the tree |
2310 | /// usually has many render objects and therefore the distance to the nearest |
2311 | /// render object is usually short. |
2312 | RenderObject? findRenderObject(); |
2313 | |
2314 | /// The size of the [RenderBox] returned by [findRenderObject]. |
2315 | /// |
2316 | /// This getter will only return a valid result after the layout phase is |
2317 | /// complete. It is therefore not valid to call this from a build method. |
2318 | /// It should only be called from paint callbacks or interaction event |
2319 | /// handlers (e.g. gesture callbacks). |
2320 | /// |
2321 | /// For details on the different phases of a frame, see the discussion at |
2322 | /// [WidgetsBinding.drawFrame]. |
2323 | /// |
2324 | /// This getter will only return a valid result if [findRenderObject] actually |
2325 | /// returns a [RenderBox]. If [findRenderObject] returns a render object that |
2326 | /// is not a subtype of [RenderBox] (e.g., [RenderView]), this getter will |
2327 | /// throw an exception in debug mode and will return null in release mode. |
2328 | /// |
2329 | /// Calling this getter is theoretically relatively expensive (O(N) in the |
2330 | /// depth of the tree), but in practice is usually cheap because the tree |
2331 | /// usually has many render objects and therefore the distance to the nearest |
2332 | /// render object is usually short. |
2333 | Size? get size; |
2334 | |
2335 | /// Registers this build context with [ancestor] such that when |
2336 | /// [ancestor]'s widget changes this build context is rebuilt. |
2337 | /// |
2338 | /// Returns `ancestor.widget`. |
2339 | /// |
2340 | /// This method is rarely called directly. Most applications should use |
2341 | /// [dependOnInheritedWidgetOfExactType], which calls this method after finding |
2342 | /// the appropriate [InheritedElement] ancestor. |
2343 | /// |
2344 | /// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can |
2345 | /// be called apply to this method as well. |
2346 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect}); |
2347 | |
2348 | /// Returns the nearest widget of the given type `T` and creates a dependency |
2349 | /// on it, or null if no appropriate widget is found. |
2350 | /// |
2351 | /// The widget found will be a concrete [InheritedWidget] subclass, and |
2352 | /// calling [dependOnInheritedWidgetOfExactType] registers this build context |
2353 | /// with the returned widget. When that widget changes (or a new widget of |
2354 | /// that type is introduced, or the widget goes away), this build context is |
2355 | /// rebuilt so that it can obtain new values from that widget. |
2356 | /// |
2357 | /// {@template flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType} |
2358 | /// This is typically called implicitly from `of()` static methods, e.g. |
2359 | /// [Theme.of]. |
2360 | /// |
2361 | /// This method should not be called from widget constructors or from |
2362 | /// [State.initState] methods, because those methods would not get called |
2363 | /// again if the inherited value were to change. To ensure that the widget |
2364 | /// correctly updates itself when the inherited value changes, only call this |
2365 | /// (directly or indirectly) from build methods, layout and paint callbacks, |
2366 | /// or from [State.didChangeDependencies] (which is called immediately after |
2367 | /// [State.initState]). |
2368 | /// |
2369 | /// This method should not be called from [State.dispose] because the element |
2370 | /// tree is no longer stable at that time. To refer to an ancestor from that |
2371 | /// method, save a reference to the ancestor in [State.didChangeDependencies]. |
2372 | /// It is safe to use this method from [State.deactivate], which is called |
2373 | /// whenever the widget is removed from the tree. |
2374 | /// |
2375 | /// It is also possible to call this method from interaction event handlers |
2376 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as |
2377 | /// that value is not cached and/or reused later. |
2378 | /// |
2379 | /// Calling this method is O(1) with a small constant factor, but will lead to |
2380 | /// the widget being rebuilt more often. |
2381 | /// |
2382 | /// Once a widget registers a dependency on a particular type by calling this |
2383 | /// method, it will be rebuilt, and [State.didChangeDependencies] will be |
2384 | /// called, whenever changes occur relating to that widget until the next time |
2385 | /// the widget or one of its ancestors is moved (for example, because an |
2386 | /// ancestor is added or removed). |
2387 | /// |
2388 | /// The [aspect] parameter is only used when `T` is an |
2389 | /// [InheritedWidget] subclasses that supports partial updates, like |
2390 | /// [InheritedModel]. It specifies what "aspect" of the inherited |
2391 | /// widget this context depends on. |
2392 | /// {@endtemplate} |
2393 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}); |
2394 | |
2395 | /// Returns the nearest widget of the given [InheritedWidget] subclass `T` or |
2396 | /// null if an appropriate ancestor is not found. |
2397 | /// |
2398 | /// This method does not introduce a dependency the way that the more typical |
2399 | /// [dependOnInheritedWidgetOfExactType] does, so this context will not be |
2400 | /// rebuilt if the [InheritedWidget] changes. This function is meant for those |
2401 | /// uncommon use cases where a dependency is undesirable. |
2402 | /// |
2403 | /// This method should not be called from [State.dispose] because the element |
2404 | /// tree is no longer stable at that time. To refer to an ancestor from that |
2405 | /// method, save a reference to the ancestor in [State.didChangeDependencies]. |
2406 | /// It is safe to use this method from [State.deactivate], which is called |
2407 | /// whenever the widget is removed from the tree. |
2408 | /// |
2409 | /// It is also possible to call this method from interaction event handlers |
2410 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as |
2411 | /// that value is not cached and/or reused later. |
2412 | /// |
2413 | /// Calling this method is O(1) with a small constant factor. |
2414 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>(); |
2415 | |
2416 | /// Obtains the element corresponding to the nearest widget of the given type `T`, |
2417 | /// which must be the type of a concrete [InheritedWidget] subclass. |
2418 | /// |
2419 | /// Returns null if no such element is found. |
2420 | /// |
2421 | /// {@template flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType} |
2422 | /// Calling this method is O(1) with a small constant factor. |
2423 | /// |
2424 | /// This method does not establish a relationship with the target in the way |
2425 | /// that [dependOnInheritedWidgetOfExactType] does. |
2426 | /// |
2427 | /// This method should not be called from [State.dispose] because the element |
2428 | /// tree is no longer stable at that time. To refer to an ancestor from that |
2429 | /// method, save a reference to the ancestor by calling |
2430 | /// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is |
2431 | /// safe to use this method from [State.deactivate], which is called whenever |
2432 | /// the widget is removed from the tree. |
2433 | /// {@endtemplate} |
2434 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>(); |
2435 | |
2436 | /// Returns the nearest ancestor widget of the given type `T`, which must be the |
2437 | /// type of a concrete [Widget] subclass. |
2438 | /// |
2439 | /// {@template flutter.widgets.BuildContext.findAncestorWidgetOfExactType} |
2440 | /// In general, [dependOnInheritedWidgetOfExactType] is more useful, since |
2441 | /// inherited widgets will trigger consumers to rebuild when they change. This |
2442 | /// method is appropriate when used in interaction event handlers (e.g. |
2443 | /// gesture callbacks) or for performing one-off tasks such as asserting that |
2444 | /// you have or don't have a widget of a specific type as an ancestor. The |
2445 | /// return value of a Widget's build method should not depend on the value |
2446 | /// returned by this method, because the build context will not rebuild if the |
2447 | /// return value of this method changes. This could lead to a situation where |
2448 | /// data used in the build method changes, but the widget is not rebuilt. |
2449 | /// |
2450 | /// Calling this method is relatively expensive (O(N) in the depth of the |
2451 | /// tree). Only call this method if the distance from this widget to the |
2452 | /// desired ancestor is known to be small and bounded. |
2453 | /// |
2454 | /// This method should not be called from [State.deactivate] or [State.dispose] |
2455 | /// because the widget tree is no longer stable at that time. To refer to |
2456 | /// an ancestor from one of those methods, save a reference to the ancestor |
2457 | /// by calling [findAncestorWidgetOfExactType] in [State.didChangeDependencies]. |
2458 | /// |
2459 | /// Returns null if a widget of the requested type does not appear in the |
2460 | /// ancestors of this context. |
2461 | /// {@endtemplate} |
2462 | T? findAncestorWidgetOfExactType<T extends Widget>(); |
2463 | |
2464 | /// Returns the [State] object of the nearest ancestor [StatefulWidget] widget |
2465 | /// that is an instance of the given type `T`. |
2466 | /// |
2467 | /// {@template flutter.widgets.BuildContext.findAncestorStateOfType} |
2468 | /// This should not be used from build methods, because the build context will |
2469 | /// not be rebuilt if the value that would be returned by this method changes. |
2470 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such |
2471 | /// cases. This method is useful for changing the state of an ancestor widget in |
2472 | /// a one-off manner, for example, to cause an ancestor scrolling list to |
2473 | /// scroll this build context's widget into view, or to move the focus in |
2474 | /// response to user interaction. |
2475 | /// |
2476 | /// In general, though, consider using a callback that triggers a stateful |
2477 | /// change in the ancestor rather than using the imperative style implied by |
2478 | /// this method. This will usually lead to more maintainable and reusable code |
2479 | /// since it decouples widgets from each other. |
2480 | /// |
2481 | /// Calling this method is relatively expensive (O(N) in the depth of the |
2482 | /// tree). Only call this method if the distance from this widget to the |
2483 | /// desired ancestor is known to be small and bounded. |
2484 | /// |
2485 | /// This method should not be called from [State.deactivate] or [State.dispose] |
2486 | /// because the widget tree is no longer stable at that time. To refer to |
2487 | /// an ancestor from one of those methods, save a reference to the ancestor |
2488 | /// by calling [findAncestorStateOfType] in [State.didChangeDependencies]. |
2489 | /// {@endtemplate} |
2490 | /// |
2491 | /// {@tool snippet} |
2492 | /// |
2493 | /// ```dart |
2494 | /// ScrollableState? scrollable = context.findAncestorStateOfType<ScrollableState>(); |
2495 | /// ``` |
2496 | /// {@end-tool} |
2497 | T? findAncestorStateOfType<T extends State>(); |
2498 | |
2499 | /// Returns the [State] object of the furthest ancestor [StatefulWidget] widget |
2500 | /// that is an instance of the given type `T`. |
2501 | /// |
2502 | /// {@template flutter.widgets.BuildContext.findRootAncestorStateOfType} |
2503 | /// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent |
2504 | /// ancestors until there are none of the type instance of `T` remaining. |
2505 | /// Then returns the last one found. |
2506 | /// |
2507 | /// This operation is O(N) as well though N is the entire widget tree rather than |
2508 | /// a subtree. |
2509 | /// {@endtemplate} |
2510 | T? findRootAncestorStateOfType<T extends State>(); |
2511 | |
2512 | /// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget |
2513 | /// that is an instance of the given type `T`. |
2514 | /// |
2515 | /// {@template flutter.widgets.BuildContext.findAncestorRenderObjectOfType} |
2516 | /// This should not be used from build methods, because the build context will |
2517 | /// not be rebuilt if the value that would be returned by this method changes. |
2518 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such |
2519 | /// cases. This method is useful only in esoteric cases where a widget needs |
2520 | /// to cause an ancestor to change its layout or paint behavior. For example, |
2521 | /// it is used by [Material] so that [InkWell] widgets can trigger the ink |
2522 | /// splash on the [Material]'s actual render object. |
2523 | /// |
2524 | /// Calling this method is relatively expensive (O(N) in the depth of the |
2525 | /// tree). Only call this method if the distance from this widget to the |
2526 | /// desired ancestor is known to be small and bounded. |
2527 | /// |
2528 | /// This method should not be called from [State.deactivate] or [State.dispose] |
2529 | /// because the widget tree is no longer stable at that time. To refer to |
2530 | /// an ancestor from one of those methods, save a reference to the ancestor |
2531 | /// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies]. |
2532 | /// {@endtemplate} |
2533 | T? findAncestorRenderObjectOfType<T extends RenderObject>(); |
2534 | |
2535 | /// Walks the ancestor chain, starting with the parent of this build context's |
2536 | /// widget, invoking the argument for each ancestor. |
2537 | /// |
2538 | /// {@template flutter.widgets.BuildContext.visitAncestorElements} |
2539 | /// The callback is given a reference to the ancestor widget's corresponding |
2540 | /// [Element] object. The walk stops when it reaches the root widget or when |
2541 | /// the callback returns false. The callback must not return null. |
2542 | /// |
2543 | /// This is useful for inspecting the widget tree. |
2544 | /// |
2545 | /// Calling this method is relatively expensive (O(N) in the depth of the tree). |
2546 | /// |
2547 | /// This method should not be called from [State.deactivate] or [State.dispose] |
2548 | /// because the element tree is no longer stable at that time. To refer to |
2549 | /// an ancestor from one of those methods, save a reference to the ancestor |
2550 | /// by calling [visitAncestorElements] in [State.didChangeDependencies]. |
2551 | /// {@endtemplate} |
2552 | void visitAncestorElements(ConditionalElementVisitor visitor); |
2553 | |
2554 | /// Walks the children of this widget. |
2555 | /// |
2556 | /// {@template flutter.widgets.BuildContext.visitChildElements} |
2557 | /// This is useful for applying changes to children after they are built |
2558 | /// without waiting for the next frame, especially if the children are known, |
2559 | /// and especially if there is exactly one child (as is always the case for |
2560 | /// [StatefulWidget]s or [StatelessWidget]s). |
2561 | /// |
2562 | /// Calling this method is very cheap for build contexts that correspond to |
2563 | /// [StatefulWidget]s or [StatelessWidget]s (O(1), since there's only one |
2564 | /// child). |
2565 | /// |
2566 | /// Calling this method is potentially expensive for build contexts that |
2567 | /// correspond to [RenderObjectWidget]s (O(N) in the number of children). |
2568 | /// |
2569 | /// Calling this method recursively is extremely expensive (O(N) in the number |
2570 | /// of descendants), and should be avoided if possible. Generally it is |
2571 | /// significantly cheaper to use an [InheritedWidget] and have the descendants |
2572 | /// pull data down, than it is to use [visitChildElements] recursively to push |
2573 | /// data down to them. |
2574 | /// {@endtemplate} |
2575 | void visitChildElements(ElementVisitor visitor); |
2576 | |
2577 | /// Start bubbling this notification at the given build context. |
2578 | /// |
2579 | /// The notification will be delivered to any [NotificationListener] widgets |
2580 | /// with the appropriate type parameters that are ancestors of the given |
2581 | /// [BuildContext]. |
2582 | void dispatchNotification(Notification notification); |
2583 | |
2584 | /// Returns a description of the [Element] associated with the current build context. |
2585 | /// |
2586 | /// The `name` is typically something like "The element being rebuilt was". |
2587 | /// |
2588 | /// See also: |
2589 | /// |
2590 | /// * [Element.describeElements], which can be used to describe a list of elements. |
2591 | DiagnosticsNode describeElement( |
2592 | String name, { |
2593 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty, |
2594 | }); |
2595 | |
2596 | /// Returns a description of the [Widget] associated with the current build context. |
2597 | /// |
2598 | /// The `name` is typically something like "The widget being rebuilt was". |
2599 | DiagnosticsNode describeWidget( |
2600 | String name, { |
2601 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty, |
2602 | }); |
2603 | |
2604 | /// Adds a description of a specific type of widget missing from the current |
2605 | /// build context's ancestry tree. |
2606 | /// |
2607 | /// You can find an example of using this method in [debugCheckHasMaterial]. |
2608 | List<DiagnosticsNode> describeMissingAncestor({required Type expectedAncestorType}); |
2609 | |
2610 | /// Adds a description of the ownership chain from a specific [Element] |
2611 | /// to the error report. |
2612 | /// |
2613 | /// The ownership chain is useful for debugging the source of an element. |
2614 | DiagnosticsNode describeOwnershipChain(String name); |
2615 | } |
2616 | |
2617 | /// A class that determines the scope of a [BuildOwner.buildScope] operation. |
2618 | /// |
2619 | /// The [BuildOwner.buildScope] method rebuilds all dirty [Element]s who share |
2620 | /// the same [Element.buildScope] as its `context` argument, and skips those |
2621 | /// with a different [Element.buildScope]. |
2622 | /// |
2623 | /// [Element]s by default have the same `buildScope` as their parents. Special |
2624 | /// [Element]s may override [Element.buildScope] to create an isolated build scope |
2625 | /// for its descendants. The [LayoutBuilder] widget, for example, establishes its |
2626 | /// own [BuildScope] such that no descendant [Element]s may rebuild prematurely |
2627 | /// until the incoming constraints are known. |
2628 | final class BuildScope { |
2629 | /// Creates a [BuildScope] with an optional [scheduleRebuild] callback. |
2630 | BuildScope({this.scheduleRebuild}); |
2631 | |
2632 | // Whether `scheduleRebuild` is called. |
2633 | bool _buildScheduled = false; |
2634 | // Whether [BuildOwner.buildScope] is actively running in this [BuildScope]. |
2635 | bool _building = false; |
2636 | |
2637 | /// An optional [VoidCallback] that will be called when [Element]s in this |
2638 | /// [BuildScope] are marked as dirty for the first time. |
2639 | /// |
2640 | /// This callback usually signifies that the [BuildOwner.buildScope] method |
2641 | /// must be called at a later time in this frame to rebuild dirty elements in |
2642 | /// this [BuildScope]. It will **not** be called if this scope is actively being |
2643 | /// built by [BuildOwner.buildScope], since the [BuildScope] will be clean when |
2644 | /// [BuildOwner.buildScope] returns. |
2645 | final VoidCallback? scheduleRebuild; |
2646 | |
2647 | /// Whether [_dirtyElements] need to be sorted again as a result of more |
2648 | /// elements becoming dirty during the build. |
2649 | /// |
2650 | /// This is necessary to preserve the sort order defined by [Element._sort]. |
2651 | /// |
2652 | /// This field is set to null when [BuildOwner.buildScope] is not actively |
2653 | /// rebuilding the widget tree. |
2654 | bool? _dirtyElementsNeedsResorting; |
2655 | final List<Element> _dirtyElements = <Element>[]; |
2656 | |
2657 | @pragma('dart2js:tryInline') |
2658 | @pragma('vm:prefer-inline') |
2659 | @pragma('wasm:prefer-inline') |
2660 | void _scheduleBuildFor(Element element) { |
2661 | assert(identical(element.buildScope, this)); |
2662 | if (!element._inDirtyList) { |
2663 | _dirtyElements.add(element); |
2664 | element._inDirtyList = true; |
2665 | } |
2666 | if (!_buildScheduled && !_building) { |
2667 | _buildScheduled = true; |
2668 | scheduleRebuild?.call(); |
2669 | } |
2670 | if (_dirtyElementsNeedsResorting != null) { |
2671 | _dirtyElementsNeedsResorting = true; |
2672 | } |
2673 | } |
2674 | |
2675 | @pragma('dart2js:tryInline') |
2676 | @pragma('vm:prefer-inline') |
2677 | @pragma('wasm:prefer-inline') |
2678 | @pragma('vm:notify-debugger-on-exception') |
2679 | void _tryRebuild(Element element) { |
2680 | assert(element._inDirtyList); |
2681 | assert(identical(element.buildScope, this)); |
2682 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(element.widget); |
2683 | if (isTimelineTracked) { |
2684 | Map<String, String>? debugTimelineArguments; |
2685 | assert(() { |
2686 | if (kDebugMode && debugEnhanceBuildTimelineArguments) { |
2687 | debugTimelineArguments = element.widget.toDiagnosticsNode().toTimelineArguments(); |
2688 | } |
2689 | return true; |
2690 | }()); |
2691 | FlutterTimeline.startSync('${element.widget.runtimeType}', arguments: debugTimelineArguments); |
2692 | } |
2693 | try { |
2694 | element.rebuild(); |
2695 | } catch (e, stack) { |
2696 | _reportException( |
2697 | ErrorDescription('while rebuilding dirty elements'), |
2698 | e, |
2699 | stack, |
2700 | informationCollector: |
2701 | () => <DiagnosticsNode>[ |
2702 | if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(element)), |
2703 | element.describeElement('The element being rebuilt at the time was'), |
2704 | ], |
2705 | ); |
2706 | } |
2707 | if (isTimelineTracked) { |
2708 | FlutterTimeline.finishSync(); |
2709 | } |
2710 | } |
2711 | |
2712 | bool _debugAssertElementInScope(Element element, Element debugBuildRoot) { |
2713 | final bool isInScope = element._debugIsDescendantOf(debugBuildRoot) || !element.debugIsActive; |
2714 | if (isInScope) { |
2715 | return true; |
2716 | } |
2717 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2718 | ErrorSummary('Tried to build dirty widget in the wrong build scope.'), |
2719 | ErrorDescription( |
2720 | 'A widget which was marked as dirty and is still active was scheduled to be built, ' |
2721 | 'but the current build scope unexpectedly does not contain that widget.', |
2722 | ), |
2723 | ErrorHint( |
2724 | 'Sometimes this is detected when an element is removed from the widget tree, but the ' |
2725 | 'element somehow did not get marked as inactive. In that case, it might be caused by ' |
2726 | 'an ancestor element failing to implement visitChildren correctly, thus preventing ' |
2727 | 'some or all of its descendants from being correctly deactivated.', |
2728 | ), |
2729 | DiagnosticsProperty<Element>( |
2730 | 'The root of the build scope was', |
2731 | debugBuildRoot, |
2732 | style: DiagnosticsTreeStyle.errorProperty, |
2733 | ), |
2734 | DiagnosticsProperty<Element>( |
2735 | 'The offending element (which does not appear to be a descendant of the root of the build scope) was', |
2736 | element, |
2737 | style: DiagnosticsTreeStyle.errorProperty, |
2738 | ), |
2739 | ]); |
2740 | } |
2741 | |
2742 | @pragma('vm:notify-debugger-on-exception') |
2743 | void _flushDirtyElements({required Element debugBuildRoot}) { |
2744 | assert(_dirtyElementsNeedsResorting == null,'_flushDirtyElements must be non-reentrant'); |
2745 | _dirtyElements.sort(Element._sort); |
2746 | _dirtyElementsNeedsResorting = false; |
2747 | try { |
2748 | for (int index = 0; index < _dirtyElements.length; index = _dirtyElementIndexAfter(index)) { |
2749 | final Element element = _dirtyElements[index]; |
2750 | if (identical(element.buildScope, this)) { |
2751 | assert(_debugAssertElementInScope(element, debugBuildRoot)); |
2752 | _tryRebuild(element); |
2753 | } |
2754 | } |
2755 | assert(() { |
2756 | final Iterable<Element> missedElements = _dirtyElements.where( |
2757 | (Element element) => |
2758 | element.debugIsActive && element.dirty && identical(element.buildScope, this), |
2759 | ); |
2760 | if (missedElements.isNotEmpty) { |
2761 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2762 | ErrorSummary('buildScope missed some dirty elements.'), |
2763 | ErrorHint( |
2764 | 'This probably indicates that the dirty list should have been resorted but was not.', |
2765 | ), |
2766 | DiagnosticsProperty<Element>( |
2767 | 'The context argument of the buildScope call was', |
2768 | debugBuildRoot, |
2769 | style: DiagnosticsTreeStyle.errorProperty, |
2770 | ), |
2771 | Element.describeElements( |
2772 | 'The list of missed elements at the end of the buildScope call was', |
2773 | missedElements, |
2774 | ), |
2775 | ]); |
2776 | } |
2777 | return true; |
2778 | }()); |
2779 | } finally { |
2780 | for (final Element element in _dirtyElements) { |
2781 | if (identical(element.buildScope, this)) { |
2782 | element._inDirtyList = false; |
2783 | } |
2784 | } |
2785 | _dirtyElements.clear(); |
2786 | _dirtyElementsNeedsResorting = null; |
2787 | _buildScheduled = false; |
2788 | } |
2789 | } |
2790 | |
2791 | @pragma('dart2js:tryInline') |
2792 | @pragma('vm:prefer-inline') |
2793 | @pragma('wasm:prefer-inline') |
2794 | int _dirtyElementIndexAfter(int index) { |
2795 | if (!_dirtyElementsNeedsResorting!) { |
2796 | return index + 1; |
2797 | } |
2798 | index += 1; |
2799 | _dirtyElements.sort(Element._sort); |
2800 | _dirtyElementsNeedsResorting = false; |
2801 | while (index > 0 && _dirtyElements[index - 1].dirty) { |
2802 | // It is possible for previously dirty but inactive widgets to move right in the list. |
2803 | // We therefore have to move the index left in the list to account for this. |
2804 | // We don't know how many could have moved. However, we do know that the only possible |
2805 | // change to the list is that nodes that were previously to the left of the index have |
2806 | // now moved to be to the right of the right-most cleaned node, and we do know that |
2807 | // all the clean nodes were to the left of the index. So we move the index left |
2808 | // until just after the right-most clean node. |
2809 | index -= 1; |
2810 | } |
2811 | assert(() { |
2812 | for (int i = index - 1; i >= 0; i -= 1) { |
2813 | final Element element = _dirtyElements[i]; |
2814 | assert(!element.dirty || element._lifecycleState != _ElementLifecycle.active); |
2815 | } |
2816 | return true; |
2817 | }()); |
2818 | return index; |
2819 | } |
2820 | } |
2821 | |
2822 | /// Manager class for the widgets framework. |
2823 | /// |
2824 | /// This class tracks which widgets need rebuilding, and handles other tasks |
2825 | /// that apply to widget trees as a whole, such as managing the inactive element |
2826 | /// list for the tree and triggering the "reassemble" command when necessary |
2827 | /// during hot reload when debugging. |
2828 | /// |
2829 | /// The main build owner is typically owned by the [WidgetsBinding], and is |
2830 | /// driven from the operating system along with the rest of the |
2831 | /// build/layout/paint pipeline. |
2832 | /// |
2833 | /// Additional build owners can be built to manage off-screen widget trees. |
2834 | /// |
2835 | /// To assign a build owner to a tree, use the |
2836 | /// [RootElementMixin.assignOwner] method on the root element of the |
2837 | /// widget tree. |
2838 | /// |
2839 | /// {@tool dartpad} |
2840 | /// This example shows how to build an off-screen widget tree used to measure |
2841 | /// the layout size of the rendered tree. For some use cases, the simpler |
2842 | /// [Offstage] widget may be a better alternative to this approach. |
2843 | /// |
2844 | /// ** See code in examples/api/lib/widgets/framework/build_owner.0.dart ** |
2845 | /// {@end-tool} |
2846 | class BuildOwner { |
2847 | /// Creates an object that manages widgets. |
2848 | /// |
2849 | /// If the `focusManager` argument is not specified or is null, this will |
2850 | /// construct a new [FocusManager] and register its global input handlers |
2851 | /// via [FocusManager.registerGlobalHandlers], which will modify static |
2852 | /// state. Callers wishing to avoid altering this state can explicitly pass |
2853 | /// a focus manager here. |
2854 | BuildOwner({this.onBuildScheduled, FocusManager? focusManager}) |
2855 | : focusManager = focusManager ?? (FocusManager()..registerGlobalHandlers()); |
2856 | |
2857 | /// Called on each build pass when the first buildable element is marked |
2858 | /// dirty. |
2859 | VoidCallback? onBuildScheduled; |
2860 | |
2861 | final _InactiveElements _inactiveElements = _InactiveElements(); |
2862 | |
2863 | bool _scheduledFlushDirtyElements = false; |
2864 | |
2865 | /// The object in charge of the focus tree. |
2866 | /// |
2867 | /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain |
2868 | /// the [FocusScopeNode] for a given [BuildContext]. |
2869 | /// |
2870 | /// See [FocusManager] for more details. |
2871 | /// |
2872 | /// This field will default to a [FocusManager] that has registered its |
2873 | /// global input handlers via [FocusManager.registerGlobalHandlers]. Callers |
2874 | /// wishing to avoid registering those handlers (and modifying the associated |
2875 | /// static state) can explicitly pass a focus manager to the [BuildOwner.new] |
2876 | /// constructor. |
2877 | FocusManager focusManager; |
2878 | |
2879 | /// Adds an element to the dirty elements list so that it will be rebuilt |
2880 | /// when [WidgetsBinding.drawFrame] calls [buildScope]. |
2881 | void scheduleBuildFor(Element element) { |
2882 | assert(element.owner == this); |
2883 | assert(element._parentBuildScope != null); |
2884 | assert(() { |
2885 | if (debugPrintScheduleBuildForStacks) { |
2886 | debugPrintStack( |
2887 | label: |
2888 | 'scheduleBuildFor() called for$element${element.buildScope._dirtyElements.contains(element) ?" (ALREADY IN LIST)":""}', |
2889 | ); |
2890 | } |
2891 | if (!element.dirty) { |
2892 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2893 | ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.'), |
2894 | element.describeElement('The method was called for the following element'), |
2895 | ErrorDescription( |
2896 | 'This element is not current marked as dirty. Make sure to set the dirty flag before ' |
2897 | 'calling scheduleBuildFor().', |
2898 | ), |
2899 | ErrorHint( |
2900 | 'If you did not attempt to call scheduleBuildFor() yourself, then this probably ' |
2901 | 'indicates a bug in the widgets framework. Please report it:\n' |
2902 | ' https://github.com/flutter/flutter/issues/new?template=02_bug.yml', |
2903 | ), |
2904 | ]); |
2905 | } |
2906 | return true; |
2907 | }()); |
2908 | final BuildScope buildScope = element.buildScope; |
2909 | assert(() { |
2910 | if (debugPrintScheduleBuildForStacks && element._inDirtyList) { |
2911 | debugPrintStack( |
2912 | label: |
2913 | 'BuildOwner.scheduleBuildFor() called; ' |
2914 | '_dirtyElementsNeedsResorting was${buildScope._dirtyElementsNeedsResorting}(now true); ' |
2915 | 'The dirty list for the current build scope is:${buildScope._dirtyElements}', |
2916 | ); |
2917 | } |
2918 | if (!_debugBuilding && element._inDirtyList) { |
2919 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
2920 | ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.'), |
2921 | ErrorHint( |
2922 | 'The BuildOwner.scheduleBuildFor() method called on an Element ' |
2923 | 'that is already in the dirty list.', |
2924 | ), |
2925 | element.describeElement('the dirty Element was'), |
2926 | ]); |
2927 | } |
2928 | return true; |
2929 | }()); |
2930 | if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { |
2931 | _scheduledFlushDirtyElements = true; |
2932 | onBuildScheduled!(); |
2933 | } |
2934 | buildScope._scheduleBuildFor(element); |
2935 | assert(() { |
2936 | if (debugPrintScheduleBuildForStacks) { |
2937 | debugPrint("...the build scope's dirty list is now:$buildScope._dirtyElements"); |
2938 | } |
2939 | return true; |
2940 | }()); |
2941 | } |
2942 | |
2943 | int _debugStateLockLevel = 0; |
2944 | bool get _debugStateLocked => _debugStateLockLevel > 0; |
2945 | |
2946 | /// Whether this widget tree is in the build phase. |
2947 | /// |
2948 | /// Only valid when asserts are enabled. |
2949 | bool get debugBuilding => _debugBuilding; |
2950 | bool _debugBuilding = false; |
2951 | Element? _debugCurrentBuildTarget; |
2952 | |
2953 | /// Establishes a scope in which calls to [State.setState] are forbidden, and |
2954 | /// calls the given `callback`. |
2955 | /// |
2956 | /// This mechanism is used to ensure that, for instance, [State.dispose] does |
2957 | /// not call [State.setState]. |
2958 | void lockState(VoidCallback callback) { |
2959 | assert(_debugStateLockLevel >= 0); |
2960 | assert(() { |
2961 | _debugStateLockLevel += 1; |
2962 | return true; |
2963 | }()); |
2964 | try { |
2965 | callback(); |
2966 | } finally { |
2967 | assert(() { |
2968 | _debugStateLockLevel -= 1; |
2969 | return true; |
2970 | }()); |
2971 | } |
2972 | assert(_debugStateLockLevel >= 0); |
2973 | } |
2974 | |
2975 | /// Establishes a scope for updating the widget tree, and calls the given |
2976 | /// `callback`, if any. Then, builds all the elements that were marked as |
2977 | /// dirty using [scheduleBuildFor], in depth order. |
2978 | /// |
2979 | /// This mechanism prevents build methods from transitively requiring other |
2980 | /// build methods to run, potentially causing infinite loops. |
2981 | /// |
2982 | /// The dirty list is processed after `callback` returns, building all the |
2983 | /// elements that were marked as dirty using [scheduleBuildFor], in depth |
2984 | /// order. If elements are marked as dirty while this method is running, they |
2985 | /// must be deeper than the `context` node, and deeper than any |
2986 | /// previously-built node in this pass. |
2987 | /// |
2988 | /// To flush the current dirty list without performing any other work, this |
2989 | /// function can be called with no callback. This is what the framework does |
2990 | /// each frame, in [WidgetsBinding.drawFrame]. |
2991 | /// |
2992 | /// Only one [buildScope] can be active at a time. |
2993 | /// |
2994 | /// A [buildScope] implies a [lockState] scope as well. |
2995 | /// |
2996 | /// To print a console message every time this method is called, set |
2997 | /// [debugPrintBuildScope] to true. This is useful when debugging problems |
2998 | /// involving widgets not getting marked dirty, or getting marked dirty too |
2999 | /// often. |
3000 | @pragma('vm:notify-debugger-on-exception') |
3001 | void buildScope(Element context, [VoidCallback? callback]) { |
3002 | final BuildScope buildScope = context.buildScope; |
3003 | if (callback == null && buildScope._dirtyElements.isEmpty) { |
3004 | return; |
3005 | } |
3006 | assert(_debugStateLockLevel >= 0); |
3007 | assert(!_debugBuilding); |
3008 | assert(() { |
3009 | if (debugPrintBuildScope) { |
3010 | debugPrint( |
3011 | 'buildScope called with context$context; ' |
3012 | "its build scope's dirty list is:${buildScope._dirtyElements}", |
3013 | ); |
3014 | } |
3015 | _debugStateLockLevel += 1; |
3016 | _debugBuilding = true; |
3017 | return true; |
3018 | }()); |
3019 | if (!kReleaseMode) { |
3020 | Map<String, String>? debugTimelineArguments; |
3021 | assert(() { |
3022 | if (debugEnhanceBuildTimelineArguments) { |
3023 | debugTimelineArguments = <String, String>{ |
3024 | 'build scope dirty count':'${buildScope._dirtyElements.length}', |
3025 | 'build scope dirty list':'${buildScope._dirtyElements}', |
3026 | 'lock level':'$_debugStateLockLevel', |
3027 | 'scope context':'$context', |
3028 | }; |
3029 | } |
3030 | return true; |
3031 | }()); |
3032 | FlutterTimeline.startSync('BUILD', arguments: debugTimelineArguments); |
3033 | } |
3034 | try { |
3035 | _scheduledFlushDirtyElements = true; |
3036 | buildScope._building = true; |
3037 | if (callback != null) { |
3038 | assert(_debugStateLocked); |
3039 | Element? debugPreviousBuildTarget; |
3040 | assert(() { |
3041 | debugPreviousBuildTarget = _debugCurrentBuildTarget; |
3042 | _debugCurrentBuildTarget = context; |
3043 | return true; |
3044 | }()); |
3045 | try { |
3046 | callback(); |
3047 | } finally { |
3048 | assert(() { |
3049 | assert(_debugCurrentBuildTarget == context); |
3050 | _debugCurrentBuildTarget = debugPreviousBuildTarget; |
3051 | _debugElementWasRebuilt(context); |
3052 | return true; |
3053 | }()); |
3054 | } |
3055 | } |
3056 | buildScope._flushDirtyElements(debugBuildRoot: context); |
3057 | } finally { |
3058 | buildScope._building = false; |
3059 | _scheduledFlushDirtyElements = false; |
3060 | if (!kReleaseMode) { |
3061 | FlutterTimeline.finishSync(); |
3062 | } |
3063 | assert(_debugBuilding); |
3064 | assert(() { |
3065 | _debugBuilding = false; |
3066 | _debugStateLockLevel -= 1; |
3067 | if (debugPrintBuildScope) { |
3068 | debugPrint('buildScope finished'); |
3069 | } |
3070 | return true; |
3071 | }()); |
3072 | } |
3073 | assert(_debugStateLockLevel >= 0); |
3074 | } |
3075 | |
3076 | Map<Element, Set<GlobalKey>>? _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans; |
3077 | |
3078 | void _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans( |
3079 | Element node, |
3080 | GlobalKey key, |
3081 | ) { |
3082 | final Map<Element, Set<GlobalKey>> map = |
3083 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans ??= |
3084 | HashMap<Element, Set<GlobalKey>>(); |
3085 | final Set<GlobalKey> keys = map.putIfAbsent(node, () => HashSet<GlobalKey>()); |
3086 | keys.add(key); |
3087 | } |
3088 | |
3089 | void _debugElementWasRebuilt(Element node) { |
3090 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.remove(node); |
3091 | } |
3092 | |
3093 | final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{}; |
3094 | |
3095 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can |
3096 | // eliminate unused fields, but not their initializers. |
3097 | @_debugOnly |
3098 | final Set<Element>? _debugIllFatedElements = kDebugMode ? HashSet<Element>() : null; |
3099 | |
3100 | // This map keeps track which child reserves the global key with the parent. |
3101 | // Parent, child -> global key. |
3102 | // This provides us a way to remove old reservation while parent rebuilds the |
3103 | // child in the same slot. |
3104 | // |
3105 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can |
3106 | // eliminate unused fields, but not their initializers. |
3107 | @_debugOnly |
3108 | final Map<Element, Map<Element, GlobalKey>>? _debugGlobalKeyReservations = |
3109 | kDebugMode ? <Element, Map<Element, GlobalKey>>{} : null; |
3110 | |
3111 | /// The number of [GlobalKey] instances that are currently associated with |
3112 | /// [Element]s that have been built by this build owner. |
3113 | int get globalKeyCount => _globalKeyRegistry.length; |
3114 | |
3115 | void _debugRemoveGlobalKeyReservationFor(Element parent, Element child) { |
3116 | assert(() { |
3117 | _debugGlobalKeyReservations?[parent]?.remove(child); |
3118 | return true; |
3119 | }()); |
3120 | } |
3121 | |
3122 | void _registerGlobalKey(GlobalKey key, Element element) { |
3123 | assert(() { |
3124 | if (_globalKeyRegistry.containsKey(key)) { |
3125 | final Element oldElement = _globalKeyRegistry[key]!; |
3126 | assert(element.widget.runtimeType != oldElement.widget.runtimeType); |
3127 | _debugIllFatedElements?.add(oldElement); |
3128 | } |
3129 | return true; |
3130 | }()); |
3131 | _globalKeyRegistry[key] = element; |
3132 | } |
3133 | |
3134 | void _unregisterGlobalKey(GlobalKey key, Element element) { |
3135 | assert(() { |
3136 | if (_globalKeyRegistry.containsKey(key) && _globalKeyRegistry[key] != element) { |
3137 | final Element oldElement = _globalKeyRegistry[key]!; |
3138 | assert(element.widget.runtimeType != oldElement.widget.runtimeType); |
3139 | } |
3140 | return true; |
3141 | }()); |
3142 | if (_globalKeyRegistry[key] == element) { |
3143 | _globalKeyRegistry.remove(key); |
3144 | } |
3145 | } |
3146 | |
3147 | void _debugReserveGlobalKeyFor(Element parent, Element child, GlobalKey key) { |
3148 | assert(() { |
3149 | _debugGlobalKeyReservations?[parent] ??= <Element, GlobalKey>{}; |
3150 | _debugGlobalKeyReservations?[parent]![child] = key; |
3151 | return true; |
3152 | }()); |
3153 | } |
3154 | |
3155 | void _debugVerifyGlobalKeyReservation() { |
3156 | assert(() { |
3157 | final Map<GlobalKey, Element> keyToParent = <GlobalKey, Element>{}; |
3158 | _debugGlobalKeyReservations?.forEach((Element parent, Map<Element, GlobalKey> childToKey) { |
3159 | // We ignore parent that are unmounted or detached. |
3160 | if (parent._lifecycleState == _ElementLifecycle.defunct || |
3161 | parent.renderObject?.attached == false) { |
3162 | return; |
3163 | } |
3164 | childToKey.forEach((Element child, GlobalKey key) { |
3165 | // If parent = null, the node is deactivated by its parent and is |
3166 | // not re-attached to other part of the tree. We should ignore this |
3167 | // node. |
3168 | if (child._parent == null) { |
3169 | return; |
3170 | } |
3171 | // It is possible the same key registers to the same parent twice |
3172 | // with different children. That is illegal, but it is not in the |
3173 | // scope of this check. Such error will be detected in |
3174 | // _debugVerifyIllFatedPopulation or |
3175 | // _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans. |
3176 | if (keyToParent.containsKey(key) && keyToParent[key] != parent) { |
3177 | // We have duplication reservations for the same global key. |
3178 | final Element older = keyToParent[key]!; |
3179 | final Element newer = parent; |
3180 | final FlutterError error; |
3181 | if (older.toString() != newer.toString()) { |
3182 | error = FlutterError.fromParts(<DiagnosticsNode>[ |
3183 | ErrorSummary('Multiple widgets used the same GlobalKey.'), |
3184 | ErrorDescription( |
3185 | 'The key$keywas used by multiple widgets. The parents of those widgets were:\n' |
3186 | '-$older\n' |
3187 | '-$newer\n' |
3188 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
3189 | ), |
3190 | ]); |
3191 | } else { |
3192 | error = FlutterError.fromParts(<DiagnosticsNode>[ |
3193 | ErrorSummary('Multiple widgets used the same GlobalKey.'), |
3194 | ErrorDescription( |
3195 | 'The key$keywas used by multiple widgets. The parents of those widgets were ' |
3196 | 'different widgets that both had the following description:\n' |
3197 | '$parent\n' |
3198 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
3199 | ), |
3200 | ]); |
3201 | } |
3202 | // Fix the tree by removing the duplicated child from one of its |
3203 | // parents to resolve the duplicated key issue. This allows us to |
3204 | // tear down the tree during testing without producing additional |
3205 | // misleading exceptions. |
3206 | if (child._parent != older) { |
3207 | older.visitChildren((Element currentChild) { |
3208 | if (currentChild == child) { |
3209 | older.forgetChild(child); |
3210 | } |
3211 | }); |
3212 | } |
3213 | if (child._parent != newer) { |
3214 | newer.visitChildren((Element currentChild) { |
3215 | if (currentChild == child) { |
3216 | newer.forgetChild(child); |
3217 | } |
3218 | }); |
3219 | } |
3220 | throw error; |
3221 | } else { |
3222 | keyToParent[key] = parent; |
3223 | } |
3224 | }); |
3225 | }); |
3226 | _debugGlobalKeyReservations?.clear(); |
3227 | return true; |
3228 | }()); |
3229 | } |
3230 | |
3231 | void _debugVerifyIllFatedPopulation() { |
3232 | assert(() { |
3233 | Map<GlobalKey, Set<Element>>? duplicates; |
3234 | for (final Element element in _debugIllFatedElements ?? const <Element>{}) { |
3235 | if (element._lifecycleState != _ElementLifecycle.defunct) { |
3236 | assert(element.widget.key != null); |
3237 | final GlobalKey key = element.widget.key! as GlobalKey; |
3238 | assert(_globalKeyRegistry.containsKey(key)); |
3239 | duplicates ??= <GlobalKey, Set<Element>>{}; |
3240 | // Uses ordered set to produce consistent error message. |
3241 | final Set<Element> elements = duplicates.putIfAbsent(key, () => <Element>{}); |
3242 | elements.add(element); |
3243 | elements.add(_globalKeyRegistry[key]!); |
3244 | } |
3245 | } |
3246 | _debugIllFatedElements?.clear(); |
3247 | if (duplicates != null) { |
3248 | final List<DiagnosticsNode> information = <DiagnosticsNode>[]; |
3249 | information.add(ErrorSummary('Multiple widgets used the same GlobalKey.')); |
3250 | for (final GlobalKey key in duplicates.keys) { |
3251 | final Set<Element> elements = duplicates[key]!; |
3252 | // TODO(jacobr): this will omit the '- ' before each widget name and |
3253 | // use the more standard whitespace style instead. Please let me know |
3254 | // if the '- ' style is a feature we want to maintain and we can add |
3255 | // another tree style that supports it. I also see '* ' in some places |
3256 | // so it would be nice to unify and normalize. |
3257 | information.add( |
3258 | Element.describeElements( |
3259 | 'The key$keywas used by${elements.length}widgets', |
3260 | elements, |
3261 | ), |
3262 | ); |
3263 | } |
3264 | information.add( |
3265 | ErrorDescription( |
3266 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
3267 | ), |
3268 | ); |
3269 | throw FlutterError.fromParts(information); |
3270 | } |
3271 | return true; |
3272 | }()); |
3273 | } |
3274 | |
3275 | /// Complete the element build pass by unmounting any elements that are no |
3276 | /// longer active. |
3277 | /// |
3278 | /// This is called by [WidgetsBinding.drawFrame]. |
3279 | /// |
3280 | /// In debug mode, this also runs some sanity checks, for example checking for |
3281 | /// duplicate global keys. |
3282 | @pragma('vm:notify-debugger-on-exception') |
3283 | void finalizeTree() { |
3284 | if (!kReleaseMode) { |
3285 | FlutterTimeline.startSync('FINALIZE TREE'); |
3286 | } |
3287 | try { |
3288 | lockState(_inactiveElements._unmountAll); // this unregisters the GlobalKeys |
3289 | assert(() { |
3290 | try { |
3291 | _debugVerifyGlobalKeyReservation(); |
3292 | _debugVerifyIllFatedPopulation(); |
3293 | if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.isNotEmpty ?? false) { |
3294 | final Set<GlobalKey> keys = HashSet<GlobalKey>(); |
3295 | for (final Element element |
3296 | in _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys) { |
3297 | if (element._lifecycleState != _ElementLifecycle.defunct) { |
3298 | keys.addAll( |
3299 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans![element]!, |
3300 | ); |
3301 | } |
3302 | } |
3303 | if (keys.isNotEmpty) { |
3304 | final Map<String, int> keyStringCount = HashMap<String, int>(); |
3305 | for (final String key in keys.map<String>((GlobalKey key) => key.toString())) { |
3306 | if (keyStringCount.containsKey(key)) { |
3307 | keyStringCount.update(key, (int value) => value + 1); |
3308 | } else { |
3309 | keyStringCount[key] = 1; |
3310 | } |
3311 | } |
3312 | final List<String> keyLabels = <String>[ |
3313 | for (final MapEntry<String, int>(:String key, value: int count) |
3314 | in keyStringCount.entries) |
3315 | if (count == 1) |
3316 | key |
3317 | else |
3318 | '$key($countdifferent affected keys had this toString representation)', |
3319 | ]; |
3320 | final Iterable<Element> elements = |
3321 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys; |
3322 | final Map<String, int> elementStringCount = HashMap<String, int>(); |
3323 | for (final String element in elements.map<String>( |
3324 | (Element element) => element.toString(), |
3325 | )) { |
3326 | if (elementStringCount.containsKey(element)) { |
3327 | elementStringCount.update(element, (int value) => value + 1); |
3328 | } else { |
3329 | elementStringCount[element] = 1; |
3330 | } |
3331 | } |
3332 | final List<String> elementLabels = <String>[ |
3333 | for (final MapEntry<String, int>(key: String element, value: int count) |
3334 | in elementStringCount.entries) |
3335 | if (count == 1) |
3336 | element |
3337 | else |
3338 | '$element($countdifferent affected elements had this toString representation)', |
3339 | ]; |
3340 | assert(keyLabels.isNotEmpty); |
3341 | final String the = keys.length == 1 ?' the':''; |
3342 | final String s = keys.length == 1 ?'':'s'; |
3343 | final String were = keys.length == 1 ?'was':'were'; |
3344 | final String their = keys.length == 1 ?'its':'their'; |
3345 | final String respective = elementLabels.length == 1 ?'':' respective'; |
3346 | final String those = keys.length == 1 ?'that':'those'; |
3347 | final String s2 = elementLabels.length == 1 ?'':'s'; |
3348 | final String those2 = elementLabels.length == 1 ?'that':'those'; |
3349 | final String they = elementLabels.length == 1 ?'it':'they'; |
3350 | final String think = elementLabels.length == 1 ?'thinks':'think'; |
3351 | final String are = elementLabels.length == 1 ?'is':'are'; |
3352 | // TODO(jacobr): make this error more structured to better expose which widgets had problems. |
3353 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
3354 | ErrorSummary('Duplicate GlobalKey$sdetected in widget tree.'), |
3355 | // TODO(jacobr): refactor this code so the elements are clickable |
3356 | // in GUI debug tools. |
3357 | ErrorDescription( |
3358 | 'The following GlobalKey$s$werespecified multiple times in the widget tree. This will lead to ' |
3359 | 'parts of the widget tree being truncated unexpectedly, because the second time a key is seen, ' |
3360 | 'the previous instance is moved to the new location. The key$s$were:\n' |
3361 | '-${keyLabels.join("\n ")}\n' |
3362 | 'This was determined by noticing that after$thewidget$swith the above global key$s$weremoved ' |
3363 | 'out of$their$respectiveprevious parent$s2,$those2previous parent$s2never updated during this frame, meaning ' |
3364 | 'that$theyeither did not update at all or updated before the widget$s$weremoved, in either case ' |
3365 | 'implying that$theystill$thinkthat$theyshould have a child with$thoseglobal key$s.\n' |
3366 | 'The specific parent$s2that did not update after having one or more children forcibly removed ' |
3367 | 'due to GlobalKey reparenting$are:\n' |
3368 | '-${elementLabels.join("\n ")}' |
3369 | '\nA GlobalKey can only be specified on one widget at a time in the widget tree.', |
3370 | ), |
3371 | ]); |
3372 | } |
3373 | } |
3374 | } finally { |
3375 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear(); |
3376 | } |
3377 | return true; |
3378 | }()); |
3379 | } catch (e, stack) { |
3380 | // Catching the exception directly to avoid activating the ErrorWidget. |
3381 | // Since the tree is in a broken state, adding the ErrorWidget would |
3382 | // cause more exceptions. |
3383 | _reportException(ErrorSummary('while finalizing the widget tree'), e, stack); |
3384 | } finally { |
3385 | if (!kReleaseMode) { |
3386 | FlutterTimeline.finishSync(); |
3387 | } |
3388 | } |
3389 | } |
3390 | |
3391 | /// Cause the entire subtree rooted at the given [Element] to be entirely |
3392 | /// rebuilt. This is used by development tools when the application code has |
3393 | /// changed and is being hot-reloaded, to cause the widget tree to pick up any |
3394 | /// changed implementations. |
3395 | /// |
3396 | /// This is expensive and should not be called except during development. |
3397 | void reassemble(Element root) { |
3398 | if (!kReleaseMode) { |
3399 | FlutterTimeline.startSync('Preparing Hot Reload (widgets)'); |
3400 | } |
3401 | try { |
3402 | assert(root._parent == null); |
3403 | assert(root.owner == this); |
3404 | root.reassemble(); |
3405 | } finally { |
3406 | if (!kReleaseMode) { |
3407 | FlutterTimeline.finishSync(); |
3408 | } |
3409 | } |
3410 | } |
3411 | } |
3412 | |
3413 | /// Mixin this class to allow receiving [Notification] objects dispatched by |
3414 | /// child elements. |
3415 | /// |
3416 | /// See also: |
3417 | /// * [NotificationListener], for a widget that allows consuming notifications. |
3418 | mixin NotifiableElementMixin on Element { |
3419 | /// Called when a notification of the appropriate type arrives at this |
3420 | /// location in the tree. |
3421 | /// |
3422 | /// Return true to cancel the notification bubbling. Return false to |
3423 | /// allow the notification to continue to be dispatched to further ancestors. |
3424 | bool onNotification(Notification notification); |
3425 | |
3426 | @override |
3427 | void attachNotificationTree() { |
3428 | _notificationTree = _NotificationNode(_parent?._notificationTree, this); |
3429 | } |
3430 | } |
3431 | |
3432 | class _NotificationNode { |
3433 | _NotificationNode(this.parent, this.current); |
3434 | |
3435 | NotifiableElementMixin? current; |
3436 | _NotificationNode? parent; |
3437 | |
3438 | void dispatchNotification(Notification notification) { |
3439 | if (current?.onNotification(notification) ?? true) { |
3440 | return; |
3441 | } |
3442 | parent?.dispatchNotification(notification); |
3443 | } |
3444 | } |
3445 | |
3446 | bool _isProfileBuildsEnabledFor(Widget widget) { |
3447 | return debugProfileBuildsEnabled || |
3448 | (debugProfileBuildsEnabledUserWidgets && debugIsWidgetLocalCreation(widget)); |
3449 | } |
3450 | |
3451 | /// An instantiation of a [Widget] at a particular location in the tree. |
3452 | /// |
3453 | /// Widgets describe how to configure a subtree but the same widget can be used |
3454 | /// to configure multiple subtrees simultaneously because widgets are immutable. |
3455 | /// An [Element] represents the use of a widget to configure a specific location |
3456 | /// in the tree. Over time, the widget associated with a given element can |
3457 | /// change, for example, if the parent widget rebuilds and creates a new widget |
3458 | /// for this location. |
3459 | /// |
3460 | /// Elements form a tree. Most elements have a unique child, but some widgets |
3461 | /// (e.g., subclasses of [RenderObjectElement]) can have multiple children. |
3462 | /// |
3463 | /// Elements have the following lifecycle: |
3464 | /// |
3465 | /// * The framework creates an element by calling [Widget.createElement] on the |
3466 | /// widget that will be used as the element's initial configuration. |
3467 | /// * The framework calls [mount] to add the newly created element to the tree |
3468 | /// at a given slot in a given parent. The [mount] method is responsible for |
3469 | /// inflating any child widgets and calling [attachRenderObject] as |
3470 | /// necessary to attach any associated render objects to the render tree. |
3471 | /// * At this point, the element is considered "active" and might appear on |
3472 | /// screen. |
3473 | /// * At some point, the parent might decide to change the widget used to |
3474 | /// configure this element, for example because the parent rebuilt with new |
3475 | /// state. When this happens, the framework will call [update] with the new |
3476 | /// widget. The new widget will always have the same [runtimeType] and key as |
3477 | /// old widget. If the parent wishes to change the [runtimeType] or key of |
3478 | /// the widget at this location in the tree, it can do so by unmounting this |
3479 | /// element and inflating the new widget at this location. |
3480 | /// * At some point, an ancestor might decide to remove this element (or an |
3481 | /// intermediate ancestor) from the tree, which the ancestor does by calling |
3482 | /// [deactivateChild] on itself. Deactivating the intermediate ancestor will |
3483 | /// remove that element's render object from the render tree and add this |
3484 | /// element to the [owner]'s list of inactive elements, causing the framework |
3485 | /// to call [deactivate] on this element. |
3486 | /// * At this point, the element is considered "inactive" and will not appear |
3487 | /// on screen. An element can remain in the inactive state only until |
3488 | /// the end of the current animation frame. At the end of the animation |
3489 | /// frame, any elements that are still inactive will be unmounted. |
3490 | /// * If the element gets reincorporated into the tree (e.g., because it or one |
3491 | /// of its ancestors has a global key that is reused), the framework will |
3492 | /// remove the element from the [owner]'s list of inactive elements, call |
3493 | /// [activate] on the element, and reattach the element's render object to |
3494 | /// the render tree. (At this point, the element is again considered "active" |
3495 | /// and might appear on screen.) |
3496 | /// * If the element does not get reincorporated into the tree by the end of |
3497 | /// the current animation frame, the framework will call [unmount] on the |
3498 | /// element. |
3499 | /// * At this point, the element is considered "defunct" and will not be |
3500 | /// incorporated into the tree in the future. |
3501 | abstract class Element extends DiagnosticableTree implements BuildContext { |
3502 | /// Creates an element that uses the given widget as its configuration. |
3503 | /// |
3504 | /// Typically called by an override of [Widget.createElement]. |
3505 | Element(Widget widget) : _widget = widget { |
3506 | assert(debugMaybeDispatchCreated('widgets','Element', this)); |
3507 | } |
3508 | |
3509 | Element? _parent; |
3510 | _NotificationNode? _notificationTree; |
3511 | |
3512 | /// Compare two widgets for equality. |
3513 | /// |
3514 | /// When a widget is rebuilt with another that compares equal according |
3515 | /// to `operator ==`, it is assumed that the update is redundant and the |
3516 | /// work to update that branch of the tree is skipped. |
3517 | /// |
3518 | /// It is generally discouraged to override `operator ==` on any widget that |
3519 | /// has children, since a correct implementation would have to defer to the |
3520 | /// children's equality operator also, and that is an O(N²) operation: each |
3521 | /// child would need to itself walk all its children, each step of the tree. |
3522 | /// |
3523 | /// It is sometimes reasonable for a leaf widget (one with no children) to |
3524 | /// implement this method, if rebuilding the widget is known to be much more |
3525 | /// expensive than checking the widgets' parameters for equality and if the |
3526 | /// widget is expected to often be rebuilt with identical parameters. |
3527 | /// |
3528 | /// In general, however, it is more efficient to cache the widgets used |
3529 | /// in a build method if it is known that they will not change. |
3530 | @nonVirtual |
3531 | @override |
3532 | // ignore: avoid_equals_and_hash_code_on_mutable_classes, hash_and_equals |
3533 | bool operator ==(Object other) => identical(this, other); |
3534 | |
3535 | /// Information set by parent to define where this child fits in its parent's |
3536 | /// child list. |
3537 | /// |
3538 | /// A child widget's slot is determined when the parent's [updateChild] method |
3539 | /// is called to inflate the child widget. See [RenderObjectElement] for more |
3540 | /// details on slots. |
3541 | Object? get slot => _slot; |
3542 | Object? _slot; |
3543 | |
3544 | /// An integer that is guaranteed to be greater than the parent's, if any. |
3545 | /// The element at the root of the tree must have a depth greater than 0. |
3546 | int get depth { |
3547 | assert(() { |
3548 | if (_lifecycleState == _ElementLifecycle.initial) { |
3549 | throw FlutterError('Depth is only available when element has been mounted.'); |
3550 | } |
3551 | return true; |
3552 | }()); |
3553 | return _depth; |
3554 | } |
3555 | |
3556 | late int _depth; |
3557 | |
3558 | /// Returns result < 0 when [a] < [b], result == 0 when [a] == [b], result > 0 |
3559 | /// when [a] > [b]. |
3560 | static int _sort(Element a, Element b) { |
3561 | final int diff = a.depth - b.depth; |
3562 | // If depths are not equal, return the difference. |
3563 | if (diff != 0) { |
3564 | return diff; |
3565 | } |
3566 | // If the `dirty` values are not equal, sort with non-dirty elements being |
3567 | // less than dirty elements. |
3568 | final bool isBDirty = b.dirty; |
3569 | if (a.dirty != isBDirty) { |
3570 | return isBDirty ? -1 : 1; |
3571 | } |
3572 | // Otherwise, `depth`s and `dirty`s are equal. |
3573 | return 0; |
3574 | } |
3575 | |
3576 | // Return a numeric encoding of the specific `Element` concrete subtype. |
3577 | // This is used in `Element.updateChild` to determine if a hot reload modified the |
3578 | // superclass of a mounted element's configuration. The encoding of each `Element` |
3579 | // must match the corresponding `Widget` encoding in `Widget._debugConcreteSubtype`. |
3580 | static int _debugConcreteSubtype(Element element) { |
3581 | return element is StatefulElement |
3582 | ? 1 |
3583 | : element is StatelessElement |
3584 | ? 2 |
3585 | : 0; |
3586 | } |
3587 | |
3588 | /// The configuration for this element. |
3589 | /// |
3590 | /// Avoid overriding this field on [Element] subtypes to provide a more |
3591 | /// specific widget type (i.e. [StatelessElement] and [StatelessWidget]). |
3592 | /// Instead, cast at any call sites where the more specific type is required. |
3593 | /// This avoids significant cast overhead on the getter which is accessed |
3594 | /// throughout the framework internals during the build phase - and for which |
3595 | /// the more specific type information is not used. |
3596 | @override |
3597 | Widget get widget => _widget!; |
3598 | Widget? _widget; |
3599 | |
3600 | @override |
3601 | bool get mounted => _widget != null; |
3602 | |
3603 | /// Returns true if the Element is defunct. |
3604 | /// |
3605 | /// This getter always returns false in profile and release builds. |
3606 | /// See the lifecycle documentation for [Element] for additional information. |
3607 | bool get debugIsDefunct { |
3608 | bool isDefunct = false; |
3609 | assert(() { |
3610 | isDefunct = _lifecycleState == _ElementLifecycle.defunct; |
3611 | return true; |
3612 | }()); |
3613 | return isDefunct; |
3614 | } |
3615 | |
3616 | /// Returns true if the Element is active. |
3617 | /// |
3618 | /// This getter always returns false in profile and release builds. |
3619 | /// See the lifecycle documentation for [Element] for additional information. |
3620 | bool get debugIsActive { |
3621 | bool isActive = false; |
3622 | assert(() { |
3623 | isActive = _lifecycleState == _ElementLifecycle.active; |
3624 | return true; |
3625 | }()); |
3626 | return isActive; |
3627 | } |
3628 | |
3629 | /// The object that manages the lifecycle of this element. |
3630 | @override |
3631 | BuildOwner? get owner => _owner; |
3632 | BuildOwner? _owner; |
3633 | |
3634 | /// A [BuildScope] whose dirty [Element]s can only be rebuilt by |
3635 | /// [BuildOwner.buildScope] calls whose `context` argument is an [Element] |
3636 | /// within this [BuildScope]. |
3637 | /// |
3638 | /// The getter typically is only safe to access when this [Element] is [mounted]. |
3639 | /// |
3640 | /// The default implementation returns the parent [Element]'s [buildScope], |
3641 | /// as in most cases an [Element] is ready to rebuild as soon as its ancestors |
3642 | /// are no longer dirty. One notable exception is [LayoutBuilder]'s |
3643 | /// descendants, which must not rebuild until the incoming constraints become |
3644 | /// available. [LayoutBuilder]'s [Element] overrides [buildScope] to make none |
3645 | /// of its descendants can rebuild until the incoming constraints are known. |
3646 | /// |
3647 | /// If you choose to override this getter to establish your own [BuildScope], |
3648 | /// to flush the dirty [Element]s in the [BuildScope] you need to manually call |
3649 | /// [BuildOwner.buildScope] with the root [Element] of your [BuildScope] when |
3650 | /// appropriate, as the Flutter framework does not try to register or manage |
3651 | /// custom [BuildScope]s. |
3652 | /// |
3653 | /// Always return the same [BuildScope] instance if you override this getter. |
3654 | /// Changing the value returned by this getter at runtime is not |
3655 | /// supported. |
3656 | /// |
3657 | /// The [updateChild] method ignores [buildScope]: if the parent [Element] |
3658 | /// calls [updateChild] on a child with a different [BuildScope], the child may |
3659 | /// still rebuild. |
3660 | /// |
3661 | /// See also: |
3662 | /// |
3663 | /// * [LayoutBuilder], a widget that establishes a custom [BuildScope]. |
3664 | BuildScope get buildScope => _parentBuildScope!; |
3665 | // The cached value of the parent Element's build scope. The cache is updated |
3666 | // when this Element mounts or reparents. |
3667 | BuildScope? _parentBuildScope; |
3668 | |
3669 | /// {@template flutter.widgets.Element.reassemble} |
3670 | /// Called whenever the application is reassembled during debugging, for |
3671 | /// example during hot reload. |
3672 | /// |
3673 | /// This method should rerun any initialization logic that depends on global |
3674 | /// state, for example, image loading from asset bundles (since the asset |
3675 | /// bundle may have changed). |
3676 | /// |
3677 | /// This function will only be called during development. In release builds, |
3678 | /// the `ext.flutter.reassemble` hook is not available, and so this code will |
3679 | /// never execute. |
3680 | /// |
3681 | /// Implementers should not rely on any ordering for hot reload source update, |
3682 | /// reassemble, and build methods after a hot reload has been initiated. It is |
3683 | /// possible that a [Timer] (e.g. an [Animation]) or a debugging session |
3684 | /// attached to the isolate could trigger a build with reloaded code _before_ |
3685 | /// reassemble is called. Code that expects preconditions to be set by |
3686 | /// reassemble after a hot reload must be resilient to being called out of |
3687 | /// order, e.g. by fizzling instead of throwing. That said, once reassemble is |
3688 | /// called, build will be called after it at least once. |
3689 | /// {@endtemplate} |
3690 | /// |
3691 | /// See also: |
3692 | /// |
3693 | /// * [State.reassemble] |
3694 | /// * [BindingBase.reassembleApplication] |
3695 | /// * [Image], which uses this to reload images. |
3696 | @mustCallSuper |
3697 | @protected |
3698 | void reassemble() { |
3699 | markNeedsBuild(); |
3700 | visitChildren((Element child) { |
3701 | child.reassemble(); |
3702 | }); |
3703 | } |
3704 | |
3705 | bool _debugIsDescendantOf(Element target) { |
3706 | Element? element = this; |
3707 | while (element != null && element.depth > target.depth) { |
3708 | element = element._parent; |
3709 | } |
3710 | return element == target; |
3711 | } |
3712 | |
3713 | /// The render object at (or below) this location in the tree. |
3714 | /// |
3715 | /// If this object is a [RenderObjectElement], the render object is the one at |
3716 | /// this location in the tree. Otherwise, this getter will walk down the tree |
3717 | /// until it finds a [RenderObjectElement]. |
3718 | /// |
3719 | /// Some locations in the tree are not backed by a render object. In those |
3720 | /// cases, this getter returns null. This can happen, if the element is |
3721 | /// located outside of a [View] since only the element subtree rooted in a |
3722 | /// view has a render tree associated with it. |
3723 | RenderObject? get renderObject { |
3724 | Element? current = this; |
3725 | while (current != null) { |
3726 | if (current._lifecycleState == _ElementLifecycle.defunct) { |
3727 | break; |
3728 | } else if (current is RenderObjectElement) { |
3729 | return current.renderObject; |
3730 | } else { |
3731 | current = current.renderObjectAttachingChild; |
3732 | } |
3733 | } |
3734 | return null; |
3735 | } |
3736 | |
3737 | /// Returns the child of this [Element] that will insert a [RenderObject] into |
3738 | /// an ancestor of this Element to construct the render tree. |
3739 | /// |
3740 | /// Returns null if this Element doesn't have any children who need to attach |
3741 | /// a [RenderObject] to an ancestor of this [Element]. A [RenderObjectElement] |
3742 | /// will therefore return null because its children insert their |
3743 | /// [RenderObject]s into the [RenderObjectElement] itself and not into an |
3744 | /// ancestor of the [RenderObjectElement]. |
3745 | /// |
3746 | /// Furthermore, this may return null for [Element]s that hoist their own |
3747 | /// independent render tree and do not extend the ancestor render tree. |
3748 | @protected |
3749 | Element? get renderObjectAttachingChild { |
3750 | Element? next; |
3751 | visitChildren((Element child) { |
3752 | assert(next == null); // This verifies that there's only one child. |
3753 | next = child; |
3754 | }); |
3755 | return next; |
3756 | } |
3757 | |
3758 | @override |
3759 | List<DiagnosticsNode> describeMissingAncestor({required Type expectedAncestorType}) { |
3760 | final List<DiagnosticsNode> information = <DiagnosticsNode>[]; |
3761 | final List<Element> ancestors = <Element>[]; |
3762 | visitAncestorElements((Element element) { |
3763 | ancestors.add(element); |
3764 | return true; |
3765 | }); |
3766 | |
3767 | information.add( |
3768 | DiagnosticsProperty<Element>( |
3769 | 'The specific widget that could not find a$expectedAncestorTypeancestor was', |
3770 | this, |
3771 | style: DiagnosticsTreeStyle.errorProperty, |
3772 | ), |
3773 | ); |
3774 | |
3775 | if (ancestors.isNotEmpty) { |
3776 | information.add(describeElements('The ancestors of this widget were', ancestors)); |
3777 | } else { |
3778 | information.add( |
3779 | ErrorDescription( |
3780 | 'This widget is the root of the tree, so it has no ' |
3781 | 'ancestors, let alone a "$expectedAncestorType" ancestor.', |
3782 | ), |
3783 | ); |
3784 | } |
3785 | return information; |
3786 | } |
3787 | |
3788 | /// Returns a list of [Element]s from the current build context to the error report. |
3789 | static DiagnosticsNode describeElements(String name, Iterable<Element> elements) { |
3790 | return DiagnosticsBlock( |
3791 | name: name, |
3792 | children: |
3793 | elements |
3794 | .map<DiagnosticsNode>((Element element) => DiagnosticsProperty<Element>('', element)) |
3795 | .toList(), |
3796 | allowTruncate: true, |
3797 | ); |
3798 | } |
3799 | |
3800 | @override |
3801 | DiagnosticsNode describeElement( |
3802 | String name, { |
3803 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty, |
3804 | }) { |
3805 | return DiagnosticsProperty<Element>(name, this, style: style); |
3806 | } |
3807 | |
3808 | @override |
3809 | DiagnosticsNode describeWidget( |
3810 | String name, { |
3811 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty, |
3812 | }) { |
3813 | return DiagnosticsProperty<Element>(name, this, style: style); |
3814 | } |
3815 | |
3816 | @override |
3817 | DiagnosticsNode describeOwnershipChain(String name) { |
3818 | // TODO(jacobr): make this structured so clients can support clicks on |
3819 | // individual entries. For example, is this an iterable with arrows as |
3820 | // separators? |
3821 | return StringProperty(name, debugGetCreatorChain(10)); |
3822 | } |
3823 | |
3824 | // This is used to verify that Element objects move through life in an |
3825 | // orderly fashion. |
3826 | _ElementLifecycle _lifecycleState = _ElementLifecycle.initial; |
3827 | |
3828 | /// Calls the argument for each child. Must be overridden by subclasses that |
3829 | /// support having children. |
3830 | /// |
3831 | /// There is no guaranteed order in which the children will be visited, though |
3832 | /// it should be consistent over time. |
3833 | /// |
3834 | /// Calling this during build is dangerous: the child list might still be |
3835 | /// being updated at that point, so the children might not be constructed yet, |
3836 | /// or might be old children that are going to be replaced. This method should |
3837 | /// only be called if it is provable that the children are available. |
3838 | void visitChildren(ElementVisitor visitor) {} |
3839 | |
3840 | /// Calls the argument for each child considered onstage. |
3841 | /// |
3842 | /// Classes like [Offstage] and [Overlay] override this method to hide their |
3843 | /// children. |
3844 | /// |
3845 | /// Being onstage affects the element's discoverability during testing when |
3846 | /// you use Flutter's [Finder] objects. For example, when you instruct the |
3847 | /// test framework to tap on a widget, by default the finder will look for |
3848 | /// onstage elements and ignore the offstage ones. |
3849 | /// |
3850 | /// The default implementation defers to [visitChildren] and therefore treats |
3851 | /// the element as onstage. |
3852 | /// |
3853 | /// See also: |
3854 | /// |
3855 | /// * [Offstage] widget that hides its children. |
3856 | /// * [Finder] that skips offstage widgets by default. |
3857 | /// * [RenderObject.visitChildrenForSemantics], in contrast to this method, |
3858 | /// designed specifically for excluding parts of the UI from the semantics |
3859 | /// tree. |
3860 | void debugVisitOnstageChildren(ElementVisitor visitor) => visitChildren(visitor); |
3861 | |
3862 | /// Wrapper around [visitChildren] for [BuildContext]. |
3863 | @override |
3864 | void visitChildElements(ElementVisitor visitor) { |
3865 | assert(() { |
3866 | if (owner == null || !owner!._debugStateLocked) { |
3867 | return true; |
3868 | } |
3869 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
3870 | ErrorSummary('visitChildElements() called during build.'), |
3871 | ErrorDescription( |
3872 | "The BuildContext.visitChildElements() method can't be called during " |
3873 | 'build because the child list is still being updated at that point, ' |
3874 | 'so the children might not be constructed yet, or might be old children ' |
3875 | 'that are going to be replaced.', |
3876 | ), |
3877 | ]); |
3878 | }()); |
3879 | visitChildren(visitor); |
3880 | } |
3881 | |
3882 | /// Update the given child with the given new configuration. |
3883 | /// |
3884 | /// This method is the core of the widgets system. It is called each time we |
3885 | /// are to add, update, or remove a child based on an updated configuration. |
3886 | /// |
3887 | /// The `newSlot` argument specifies the new value for this element's [slot]. |
3888 | /// |
3889 | /// If the `child` is null, and the `newWidget` is not null, then we have a new |
3890 | /// child for which we need to create an [Element], configured with `newWidget`. |
3891 | /// |
3892 | /// If the `newWidget` is null, and the `child` is not null, then we need to |
3893 | /// remove it because it no longer has a configuration. |
3894 | /// |
3895 | /// If neither are null, then we need to update the `child`'s configuration to |
3896 | /// be the new configuration given by `newWidget`. If `newWidget` can be given |
3897 | /// to the existing child (as determined by [Widget.canUpdate]), then it is so |
3898 | /// given. Otherwise, the old child needs to be disposed and a new child |
3899 | /// created for the new configuration. |
3900 | /// |
3901 | /// If both are null, then we don't have a child and won't have a child, so we |
3902 | /// do nothing. |
3903 | /// |
3904 | /// The [updateChild] method returns the new child, if it had to create one, |
3905 | /// or the child that was passed in, if it just had to update the child, or |
3906 | /// null, if it removed the child and did not replace it. |
3907 | /// |
3908 | /// The following table summarizes the above: |
3909 | /// |
3910 | /// | | **newWidget == null** | **newWidget != null** | |
3911 | /// | :-----------------: | :--------------------- | :---------------------- | |
3912 | /// | **child == null** | Returns null. | Returns new [Element]. | |
3913 | /// | **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. | |
3914 | /// |
3915 | /// The `newSlot` argument is used only if `newWidget` is not null. If `child` |
3916 | /// is null (or if the old child cannot be updated), then the `newSlot` is |
3917 | /// given to the new [Element] that is created for the child, via |
3918 | /// [inflateWidget]. If `child` is not null (and the old child _can_ be |
3919 | /// updated), then the `newSlot` is given to [updateSlotForChild] to update |
3920 | /// its slot, in case it has moved around since it was last built. |
3921 | /// |
3922 | /// See the [RenderObjectElement] documentation for more information on slots. |
3923 | @protected |
3924 | @pragma('dart2js:tryInline') |
3925 | @pragma('vm:prefer-inline') |
3926 | @pragma('wasm:prefer-inline') |
3927 | Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { |
3928 | if (newWidget == null) { |
3929 | if (child != null) { |
3930 | deactivateChild(child); |
3931 | } |
3932 | return null; |
3933 | } |
3934 | |
3935 | final Element newChild; |
3936 | if (child != null) { |
3937 | bool hasSameSuperclass = true; |
3938 | // When the type of a widget is changed between Stateful and Stateless via |
3939 | // hot reload, the element tree will end up in a partially invalid state. |
3940 | // That is, if the widget was a StatefulWidget and is now a StatelessWidget, |
3941 | // then the element tree currently contains a StatefulElement that is incorrectly |
3942 | // referencing a StatelessWidget (and likewise with StatelessElement). |
3943 | // |
3944 | // To avoid crashing due to type errors, we need to gently guide the invalid |
3945 | // element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition |
3946 | // returns false which prevents us from trying to update the existing element |
3947 | // incorrectly. |
3948 | // |
3949 | // For the case where the widget becomes Stateful, we also need to avoid |
3950 | // accessing `StatelessElement.widget` as the cast on the getter will |
3951 | // cause a type error to be thrown. Here we avoid that by short-circuiting |
3952 | // the `Widget.canUpdate` check once `hasSameSuperclass` is false. |
3953 | assert(() { |
3954 | final int oldElementClass = Element._debugConcreteSubtype(child); |
3955 | final int newWidgetClass = Widget._debugConcreteSubtype(newWidget); |
3956 | hasSameSuperclass = oldElementClass == newWidgetClass; |
3957 | return true; |
3958 | }()); |
3959 | if (hasSameSuperclass && child.widget == newWidget) { |
3960 | // We don't insert a timeline event here, because otherwise it's |
3961 | // confusing that widgets that "don't update" (because they didn't |
3962 | // change) get "charged" on the timeline. |
3963 | if (child.slot != newSlot) { |
3964 | updateSlotForChild(child, newSlot); |
3965 | } |
3966 | newChild = child; |
3967 | } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { |
3968 | if (child.slot != newSlot) { |
3969 | updateSlotForChild(child, newSlot); |
3970 | } |
3971 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget); |
3972 | if (isTimelineTracked) { |
3973 | Map<String, String>? debugTimelineArguments; |
3974 | assert(() { |
3975 | if (kDebugMode && debugEnhanceBuildTimelineArguments) { |
3976 | debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments(); |
3977 | } |
3978 | return true; |
3979 | }()); |
3980 | FlutterTimeline.startSync('${newWidget.runtimeType}', arguments: debugTimelineArguments); |
3981 | } |
3982 | child.update(newWidget); |
3983 | if (isTimelineTracked) { |
3984 | FlutterTimeline.finishSync(); |
3985 | } |
3986 | assert(child.widget == newWidget); |
3987 | assert(() { |
3988 | child.owner!._debugElementWasRebuilt(child); |
3989 | return true; |
3990 | }()); |
3991 | newChild = child; |
3992 | } else { |
3993 | deactivateChild(child); |
3994 | assert(child._parent == null); |
3995 | // The [debugProfileBuildsEnabled] code for this branch is inside |
3996 | // [inflateWidget], since some [Element]s call [inflateWidget] directly |
3997 | // instead of going through [updateChild]. |
3998 | newChild = inflateWidget(newWidget, newSlot); |
3999 | } |
4000 | } else { |
4001 | // The [debugProfileBuildsEnabled] code for this branch is inside |
4002 | // [inflateWidget], since some [Element]s call [inflateWidget] directly |
4003 | // instead of going through [updateChild]. |
4004 | newChild = inflateWidget(newWidget, newSlot); |
4005 | } |
4006 | |
4007 | assert(() { |
4008 | if (child != null) { |
4009 | _debugRemoveGlobalKeyReservation(child); |
4010 | } |
4011 | final Key? key = newWidget.key; |
4012 | if (key is GlobalKey) { |
4013 | assert(owner != null); |
4014 | owner!._debugReserveGlobalKeyFor(this, newChild, key); |
4015 | } |
4016 | return true; |
4017 | }()); |
4018 | |
4019 | return newChild; |
4020 | } |
4021 | |
4022 | /// Updates the children of this element to use new widgets. |
4023 | /// |
4024 | /// Attempts to update the given old children list using the given new |
4025 | /// widgets, removing obsolete elements and introducing new ones as necessary, |
4026 | /// and then returns the new child list. |
4027 | /// |
4028 | /// During this function the `oldChildren` list must not be modified. If the |
4029 | /// caller wishes to remove elements from `oldChildren` reentrantly while |
4030 | /// this function is on the stack, the caller can supply a `forgottenChildren` |
4031 | /// argument, which can be modified while this function is on the stack. |
4032 | /// Whenever this function reads from `oldChildren`, this function first |
4033 | /// checks whether the child is in `forgottenChildren`. If it is, the function |
4034 | /// acts as if the child was not in `oldChildren`. |
4035 | /// |
4036 | /// This function is a convenience wrapper around [updateChild], which updates |
4037 | /// each individual child. If `slots` is non-null, the value for the `newSlot` |
4038 | /// argument of [updateChild] is retrieved from that list using the index that |
4039 | /// the currently processed `child` corresponds to in the `newWidgets` list |
4040 | /// (`newWidgets` and `slots` must have the same length). If `slots` is null, |
4041 | /// an [IndexedSlot<Element>] is used as the value for the `newSlot` argument. |
4042 | /// In that case, [IndexedSlot.index] is set to the index that the currently |
4043 | /// processed `child` corresponds to in the `newWidgets` list and |
4044 | /// [IndexedSlot.value] is set to the [Element] of the previous widget in that |
4045 | /// list (or null if it is the first child). |
4046 | /// |
4047 | /// When the [slot] value of an [Element] changes, its |
4048 | /// associated [renderObject] needs to move to a new position in the child |
4049 | /// list of its parents. If that [RenderObject] organizes its children in a |
4050 | /// linked list (as is done by the [ContainerRenderObjectMixin]) this can |
4051 | /// be implemented by re-inserting the child [RenderObject] into the |
4052 | /// list after the [RenderObject] associated with the [Element] provided as |
4053 | /// [IndexedSlot.value] in the [slot] object. |
4054 | /// |
4055 | /// Using the previous sibling as a [slot] is not enough, though, because |
4056 | /// child [RenderObject]s are only moved around when the [slot] of their |
4057 | /// associated [RenderObjectElement]s is updated. When the order of child |
4058 | /// [Element]s is changed, some elements in the list may move to a new index |
4059 | /// but still have the same previous sibling. For example, when |
4060 | /// `[e1, e2, e3, e4]` is changed to `[e1, e3, e4, e2]` the element e4 |
4061 | /// continues to have e3 as a previous sibling even though its index in the list |
4062 | /// has changed and its [RenderObject] needs to move to come before e2's |
4063 | /// [RenderObject]. In order to trigger this move, a new [slot] value needs to |
4064 | /// be assigned to its [Element] whenever its index in its |
4065 | /// parent's child list changes. Using an [IndexedSlot<Element>] achieves |
4066 | /// exactly that and also ensures that the underlying parent [RenderObject] |
4067 | /// knows where a child needs to move to in a linked list by providing its new |
4068 | /// previous sibling. |
4069 | @protected |
4070 | List<Element> updateChildren( |
4071 | List<Element> oldChildren, |
4072 | List<Widget> newWidgets, { |
4073 | Set<Element>? forgottenChildren, |
4074 | List<Object?>? slots, |
4075 | }) { |
4076 | assert(slots == null || newWidgets.length == slots.length); |
4077 | |
4078 | Element? replaceWithNullIfForgotten(Element child) { |
4079 | return (forgottenChildren?.contains(child) ?? false) ? null : child; |
4080 | } |
4081 | |
4082 | Object? slotFor(int newChildIndex, Element? previousChild) { |
4083 | return slots != null |
4084 | ? slots[newChildIndex] |
4085 | : IndexedSlot<Element?>(newChildIndex, previousChild); |
4086 | } |
4087 | |
4088 | // This attempts to diff the new child list (newWidgets) with |
4089 | // the old child list (oldChildren), and produce a new list of elements to |
4090 | // be the new list of child elements of this element. The called of this |
4091 | // method is expected to update this render object accordingly. |
4092 | |
4093 | // The cases it tries to optimize for are: |
4094 | // - the old list is empty |
4095 | // - the lists are identical |
4096 | // - there is an insertion or removal of one or more widgets in |
4097 | // only one place in the list |
4098 | // If a widget with a key is in both lists, it will be synced. |
4099 | // Widgets without keys might be synced but there is no guarantee. |
4100 | |
4101 | // The general approach is to sync the entire new list backwards, as follows: |
4102 | // 1. Walk the lists from the top, syncing nodes, until you no longer have |
4103 | // matching nodes. |
4104 | // 2. Walk the lists from the bottom, without syncing nodes, until you no |
4105 | // longer have matching nodes. We'll sync these nodes at the end. We |
4106 | // don't sync them now because we want to sync all the nodes in order |
4107 | // from beginning to end. |
4108 | // At this point we narrowed the old and new lists to the point |
4109 | // where the nodes no longer match. |
4110 | // 3. Walk the narrowed part of the old list to get the list of |
4111 | // keys and sync null with non-keyed items. |
4112 | // 4. Walk the narrowed part of the new list forwards: |
4113 | // * Sync non-keyed items with null |
4114 | // * Sync keyed items with the source if it exists, else with null. |
4115 | // 5. Walk the bottom of the list again, syncing the nodes. |
4116 | // 6. Sync null with any items in the list of keys that are still |
4117 | // mounted. |
4118 | |
4119 | int newChildrenTop = 0; |
4120 | int oldChildrenTop = 0; |
4121 | int newChildrenBottom = newWidgets.length - 1; |
4122 | int oldChildrenBottom = oldChildren.length - 1; |
4123 | |
4124 | final List<Element> newChildren = List<Element>.filled( |
4125 | newWidgets.length, |
4126 | _NullElement.instance, |
4127 | ); |
4128 | |
4129 | Element? previousChild; |
4130 | |
4131 | // Update the top of the list. |
4132 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { |
4133 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]); |
4134 | final Widget newWidget = newWidgets[newChildrenTop]; |
4135 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active); |
4136 | if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget)) { |
4137 | break; |
4138 | } |
4139 | final Element newChild = |
4140 | updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!; |
4141 | assert(newChild._lifecycleState == _ElementLifecycle.active); |
4142 | newChildren[newChildrenTop] = newChild; |
4143 | previousChild = newChild; |
4144 | newChildrenTop += 1; |
4145 | oldChildrenTop += 1; |
4146 | } |
4147 | |
4148 | // Scan the bottom of the list. |
4149 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { |
4150 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]); |
4151 | final Widget newWidget = newWidgets[newChildrenBottom]; |
4152 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active); |
4153 | if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget)) { |
4154 | break; |
4155 | } |
4156 | oldChildrenBottom -= 1; |
4157 | newChildrenBottom -= 1; |
4158 | } |
4159 | |
4160 | // Scan the old children in the middle of the list. |
4161 | final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom; |
4162 | Map<Key, Element>? oldKeyedChildren; |
4163 | if (haveOldChildren) { |
4164 | oldKeyedChildren = <Key, Element>{}; |
4165 | while (oldChildrenTop <= oldChildrenBottom) { |
4166 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]); |
4167 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active); |
4168 | if (oldChild != null) { |
4169 | if (oldChild.widget.key != null) { |
4170 | oldKeyedChildren[oldChild.widget.key!] = oldChild; |
4171 | } else { |
4172 | deactivateChild(oldChild); |
4173 | } |
4174 | } |
4175 | oldChildrenTop += 1; |
4176 | } |
4177 | } |
4178 | |
4179 | // Update the middle of the list. |
4180 | while (newChildrenTop <= newChildrenBottom) { |
4181 | Element? oldChild; |
4182 | final Widget newWidget = newWidgets[newChildrenTop]; |
4183 | if (haveOldChildren) { |
4184 | final Key? key = newWidget.key; |
4185 | if (key != null) { |
4186 | oldChild = oldKeyedChildren![key]; |
4187 | if (oldChild != null) { |
4188 | if (Widget.canUpdate(oldChild.widget, newWidget)) { |
4189 | // we found a match! |
4190 | // remove it from oldKeyedChildren so we don't unsync it later |
4191 | oldKeyedChildren.remove(key); |
4192 | } else { |
4193 | // Not a match, let's pretend we didn't see it for now. |
4194 | oldChild = null; |
4195 | } |
4196 | } |
4197 | } |
4198 | } |
4199 | assert(oldChild == null || Widget.canUpdate(oldChild.widget, newWidget)); |
4200 | final Element newChild = |
4201 | updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!; |
4202 | assert(newChild._lifecycleState == _ElementLifecycle.active); |
4203 | assert( |
4204 | oldChild == newChild || |
4205 | oldChild == null || |
4206 | oldChild._lifecycleState != _ElementLifecycle.active, |
4207 | ); |
4208 | newChildren[newChildrenTop] = newChild; |
4209 | previousChild = newChild; |
4210 | newChildrenTop += 1; |
4211 | } |
4212 | |
4213 | // We've scanned the whole list. |
4214 | assert(oldChildrenTop == oldChildrenBottom + 1); |
4215 | assert(newChildrenTop == newChildrenBottom + 1); |
4216 | assert(newWidgets.length - newChildrenTop == oldChildren.length - oldChildrenTop); |
4217 | newChildrenBottom = newWidgets.length - 1; |
4218 | oldChildrenBottom = oldChildren.length - 1; |
4219 | |
4220 | // Update the bottom of the list. |
4221 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { |
4222 | final Element oldChild = oldChildren[oldChildrenTop]; |
4223 | assert(replaceWithNullIfForgotten(oldChild) != null); |
4224 | assert(oldChild._lifecycleState == _ElementLifecycle.active); |
4225 | final Widget newWidget = newWidgets[newChildrenTop]; |
4226 | assert(Widget.canUpdate(oldChild.widget, newWidget)); |
4227 | final Element newChild = |
4228 | updateChild(oldChild, newWidget, slotFor(newChildrenTop, previousChild))!; |
4229 | assert(newChild._lifecycleState == _ElementLifecycle.active); |
4230 | assert(oldChild == newChild || oldChild._lifecycleState != _ElementLifecycle.active); |
4231 | newChildren[newChildrenTop] = newChild; |
4232 | previousChild = newChild; |
4233 | newChildrenTop += 1; |
4234 | oldChildrenTop += 1; |
4235 | } |
4236 | |
4237 | // Clean up any of the remaining middle nodes from the old list. |
4238 | if (haveOldChildren && oldKeyedChildren!.isNotEmpty) { |
4239 | for (final Element oldChild in oldKeyedChildren.values) { |
4240 | if (forgottenChildren == null || !forgottenChildren.contains(oldChild)) { |
4241 | deactivateChild(oldChild); |
4242 | } |
4243 | } |
4244 | } |
4245 | assert(newChildren.every((Element element) => element is! _NullElement)); |
4246 | return newChildren; |
4247 | } |
4248 | |
4249 | /// Add this element to the tree in the given slot of the given parent. |
4250 | /// |
4251 | /// The framework calls this function when a newly created element is added to |
4252 | /// the tree for the first time. Use this method to initialize state that |
4253 | /// depends on having a parent. State that is independent of the parent can |
4254 | /// more easily be initialized in the constructor. |
4255 | /// |
4256 | /// This method transitions the element from the "initial" lifecycle state to |
4257 | /// the "active" lifecycle state. |
4258 | /// |
4259 | /// Subclasses that override this method are likely to want to also override |
4260 | /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild], |
4261 | /// [RenderObjectElement.moveRenderObjectChild], and |
4262 | /// [RenderObjectElement.removeRenderObjectChild]. |
4263 | /// |
4264 | /// Implementations of this method should start with a call to the inherited |
4265 | /// method, as in `super.mount(parent, newSlot)`. |
4266 | @mustCallSuper |
4267 | void mount(Element? parent, Object? newSlot) { |
4268 | assert( |
4269 | _lifecycleState == _ElementLifecycle.initial, |
4270 | 'This element is no longer in its initial state (${_lifecycleState.name})', |
4271 | ); |
4272 | assert( |
4273 | _parent == null, |
4274 | "This element already has a parent ($_parent) and it shouldn't have one yet.", |
4275 | ); |
4276 | assert( |
4277 | parent == null || parent._lifecycleState == _ElementLifecycle.active, |
4278 | 'Parent ($parent) should be null or in the active state (${parent._lifecycleState.name})', |
4279 | ); |
4280 | assert(slot == null,"This element already has a slot ($slot) and it shouldn't"); |
4281 | _parent = parent; |
4282 | _slot = newSlot; |
4283 | _lifecycleState = _ElementLifecycle.active; |
4284 | _depth = 1 + (_parent?.depth ?? 0); |
4285 | if (parent != null) { |
4286 | // Only assign ownership if the parent is non-null. If parent is null |
4287 | // (the root node), the owner should have already been assigned. |
4288 | // See RootRenderObjectElement.assignOwner(). |
4289 | _owner = parent.owner; |
4290 | _parentBuildScope = parent.buildScope; |
4291 | } |
4292 | assert(owner != null); |
4293 | final Key? key = widget.key; |
4294 | if (key is GlobalKey) { |
4295 | owner!._registerGlobalKey(key, this); |
4296 | } |
4297 | _updateInheritance(); |
4298 | attachNotificationTree(); |
4299 | } |
4300 | |
4301 | void _debugRemoveGlobalKeyReservation(Element child) { |
4302 | assert(owner != null); |
4303 | owner!._debugRemoveGlobalKeyReservationFor(this, child); |
4304 | } |
4305 | |
4306 | /// Change the widget used to configure this element. |
4307 | /// |
4308 | /// The framework calls this function when the parent wishes to use a |
4309 | /// different widget to configure this element. The new widget is guaranteed |
4310 | /// to have the same [runtimeType] as the old widget. |
4311 | /// |
4312 | /// This function is called only during the "active" lifecycle state. |
4313 | @mustCallSuper |
4314 | void update(covariant Widget newWidget) { |
4315 | // This code is hot when hot reloading, so we try to |
4316 | // only call _AssertionError._evaluateAssertion once. |
4317 | assert( |
4318 | _lifecycleState == _ElementLifecycle.active && |
4319 | newWidget != widget && |
4320 | Widget.canUpdate(widget, newWidget), |
4321 | ); |
4322 | // This Element was told to update and we can now release all the global key |
4323 | // reservations of forgotten children. We cannot do this earlier because the |
4324 | // forgotten children still represent global key duplications if the element |
4325 | // never updates (the forgotten children are not removed from the tree |
4326 | // until the call to update happens) |
4327 | assert(() { |
4328 | _debugForgottenChildrenWithGlobalKey?.forEach(_debugRemoveGlobalKeyReservation); |
4329 | _debugForgottenChildrenWithGlobalKey?.clear(); |
4330 | return true; |
4331 | }()); |
4332 | _widget = newWidget; |
4333 | } |
4334 | |
4335 | /// Change the slot that the given child occupies in its parent. |
4336 | /// |
4337 | /// Called by [MultiChildRenderObjectElement], and other [RenderObjectElement] |
4338 | /// subclasses that have multiple children, when child moves from one position |
4339 | /// to another in this element's child list. |
4340 | @protected |
4341 | void updateSlotForChild(Element child, Object? newSlot) { |
4342 | assert(_lifecycleState == _ElementLifecycle.active); |
4343 | assert(child._parent == this); |
4344 | void visit(Element element) { |
4345 | element.updateSlot(newSlot); |
4346 | final Element? descendant = element.renderObjectAttachingChild; |
4347 | if (descendant != null) { |
4348 | visit(descendant); |
4349 | } |
4350 | } |
4351 | |
4352 | visit(child); |
4353 | } |
4354 | |
4355 | /// Called by [updateSlotForChild] when the framework needs to change the slot |
4356 | /// that this [Element] occupies in its ancestor. |
4357 | @protected |
4358 | @mustCallSuper |
4359 | void updateSlot(Object? newSlot) { |
4360 | assert(_lifecycleState == _ElementLifecycle.active); |
4361 | assert(_parent != null); |
4362 | assert(_parent!._lifecycleState == _ElementLifecycle.active); |
4363 | _slot = newSlot; |
4364 | } |
4365 | |
4366 | void _updateDepth(int parentDepth) { |
4367 | final int expectedDepth = parentDepth + 1; |
4368 | if (_depth < expectedDepth) { |
4369 | _depth = expectedDepth; |
4370 | visitChildren((Element child) { |
4371 | child._updateDepth(expectedDepth); |
4372 | }); |
4373 | } |
4374 | } |
4375 | |
4376 | void _updateBuildScopeRecursively() { |
4377 | if (identical(buildScope, _parent?.buildScope)) { |
4378 | return; |
4379 | } |
4380 | // Unset the _inDirtyList flag so this Element can be added to the dirty list |
4381 | // of the new build scope if it's dirty. |
4382 | _inDirtyList = false; |
4383 | _parentBuildScope = _parent?.buildScope; |
4384 | visitChildren((Element child) { |
4385 | child._updateBuildScopeRecursively(); |
4386 | }); |
4387 | } |
4388 | |
4389 | /// Remove [renderObject] from the render tree. |
4390 | /// |
4391 | /// The default implementation of this function calls |
4392 | /// [detachRenderObject] recursively on each child. The |
4393 | /// [RenderObjectElement.detachRenderObject] override does the actual work of |
4394 | /// removing [renderObject] from the render tree. |
4395 | /// |
4396 | /// This is called by [deactivateChild]. |
4397 | void detachRenderObject() { |
4398 | visitChildren((Element child) { |
4399 | child.detachRenderObject(); |
4400 | }); |
4401 | _slot = null; |
4402 | } |
4403 | |
4404 | /// Add [renderObject] to the render tree at the location specified by `newSlot`. |
4405 | /// |
4406 | /// The default implementation of this function calls |
4407 | /// [attachRenderObject] recursively on each child. The |
4408 | /// [RenderObjectElement.attachRenderObject] override does the actual work of |
4409 | /// adding [renderObject] to the render tree. |
4410 | /// |
4411 | /// The `newSlot` argument specifies the new value for this element's [slot]. |
4412 | void attachRenderObject(Object? newSlot) { |
4413 | assert(slot == null); |
4414 | visitChildren((Element child) { |
4415 | child.attachRenderObject(newSlot); |
4416 | }); |
4417 | _slot = newSlot; |
4418 | } |
4419 | |
4420 | Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) { |
4421 | // The "inactivity" of the element being retaken here may be forward-looking: if |
4422 | // we are taking an element with a GlobalKey from an element that currently has |
4423 | // it as a child, then we know that element will soon no longer have that |
4424 | // element as a child. The only way that assumption could be false is if the |
4425 | // global key is being duplicated, and we'll try to track that using the |
4426 | // _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans call below. |
4427 | final Element? element = key._currentElement; |
4428 | if (element == null) { |
4429 | return null; |
4430 | } |
4431 | if (!Widget.canUpdate(element.widget, newWidget)) { |
4432 | return null; |
4433 | } |
4434 | assert(() { |
4435 | if (debugPrintGlobalKeyedWidgetLifecycle) { |
4436 | debugPrint( |
4437 | 'Attempting to take$elementfrom${element._parent ??"inactive elements list"}to put in$this.', |
4438 | ); |
4439 | } |
4440 | return true; |
4441 | }()); |
4442 | final Element? parent = element._parent; |
4443 | if (parent != null) { |
4444 | assert(() { |
4445 | if (parent == this) { |
4446 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4447 | ErrorSummary("A GlobalKey was used multiple times inside one widget's child list."), |
4448 | DiagnosticsProperty<GlobalKey>('The offending GlobalKey was', key), |
4449 | parent.describeElement('The parent of the widgets with that key was'), |
4450 | element.describeElement('The first child to get instantiated with that key became'), |
4451 | DiagnosticsProperty<Widget>( |
4452 | 'The second child that was to be instantiated with that key was', |
4453 | widget, |
4454 | style: DiagnosticsTreeStyle.errorProperty, |
4455 | ), |
4456 | ErrorDescription( |
4457 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
4458 | ), |
4459 | ]); |
4460 | } |
4461 | parent.owner!._debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans( |
4462 | parent, |
4463 | key, |
4464 | ); |
4465 | return true; |
4466 | }()); |
4467 | parent.forgetChild(element); |
4468 | parent.deactivateChild(element); |
4469 | } |
4470 | assert(element._parent == null); |
4471 | owner!._inactiveElements.remove(element); |
4472 | return element; |
4473 | } |
4474 | |
4475 | /// Create an element for the given widget and add it as a child of this |
4476 | /// element in the given slot. |
4477 | /// |
4478 | /// This method is typically called by [updateChild] but can be called |
4479 | /// directly by subclasses that need finer-grained control over creating |
4480 | /// elements. |
4481 | /// |
4482 | /// If the given widget has a global key and an element already exists that |
4483 | /// has a widget with that global key, this function will reuse that element |
4484 | /// (potentially grafting it from another location in the tree or reactivating |
4485 | /// it from the list of inactive elements) rather than creating a new element. |
4486 | /// |
4487 | /// The `newSlot` argument specifies the new value for this element's [slot]. |
4488 | /// |
4489 | /// The element returned by this function will already have been mounted and |
4490 | /// will be in the "active" lifecycle state. |
4491 | @protected |
4492 | @pragma('dart2js:tryInline') |
4493 | @pragma('vm:prefer-inline') |
4494 | @pragma('wasm:prefer-inline') |
4495 | Element inflateWidget(Widget newWidget, Object? newSlot) { |
4496 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget); |
4497 | if (isTimelineTracked) { |
4498 | Map<String, String>? debugTimelineArguments; |
4499 | assert(() { |
4500 | if (kDebugMode && debugEnhanceBuildTimelineArguments) { |
4501 | debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments(); |
4502 | } |
4503 | return true; |
4504 | }()); |
4505 | FlutterTimeline.startSync('${newWidget.runtimeType}', arguments: debugTimelineArguments); |
4506 | } |
4507 | |
4508 | try { |
4509 | final Key? key = newWidget.key; |
4510 | if (key is GlobalKey) { |
4511 | final Element? newChild = _retakeInactiveElement(key, newWidget); |
4512 | if (newChild != null) { |
4513 | assert(newChild._parent == null); |
4514 | assert(() { |
4515 | _debugCheckForCycles(newChild); |
4516 | return true; |
4517 | }()); |
4518 | try { |
4519 | newChild._activateWithParent(this, newSlot); |
4520 | } catch (_) { |
4521 | // Attempt to do some clean-up if activation fails to leave tree in a reasonable state. |
4522 | try { |
4523 | deactivateChild(newChild); |
4524 | } catch (_) { |
4525 | // Clean-up failed. Only surface original exception. |
4526 | } |
4527 | rethrow; |
4528 | } |
4529 | final Element? updatedChild = updateChild(newChild, newWidget, newSlot); |
4530 | assert(newChild == updatedChild); |
4531 | return updatedChild!; |
4532 | } |
4533 | } |
4534 | final Element newChild = newWidget.createElement(); |
4535 | assert(() { |
4536 | _debugCheckForCycles(newChild); |
4537 | return true; |
4538 | }()); |
4539 | newChild.mount(this, newSlot); |
4540 | assert(newChild._lifecycleState == _ElementLifecycle.active); |
4541 | |
4542 | return newChild; |
4543 | } finally { |
4544 | if (isTimelineTracked) { |
4545 | FlutterTimeline.finishSync(); |
4546 | } |
4547 | } |
4548 | } |
4549 | |
4550 | void _debugCheckForCycles(Element newChild) { |
4551 | assert(newChild._parent == null); |
4552 | assert(() { |
4553 | Element node = this; |
4554 | while (node._parent != null) { |
4555 | node = node._parent!; |
4556 | } |
4557 | assert(node != newChild); // indicates we are about to create a cycle |
4558 | return true; |
4559 | }()); |
4560 | } |
4561 | |
4562 | /// Move the given element to the list of inactive elements and detach its |
4563 | /// render object from the render tree. |
4564 | /// |
4565 | /// This method stops the given element from being a child of this element by |
4566 | /// detaching its render object from the render tree and moving the element to |
4567 | /// the list of inactive elements. |
4568 | /// |
4569 | /// This method (indirectly) calls [deactivate] on the child. |
4570 | /// |
4571 | /// The caller is responsible for removing the child from its child model. |
4572 | /// Typically [deactivateChild] is called by the element itself while it is |
4573 | /// updating its child model; however, during [GlobalKey] reparenting, the new |
4574 | /// parent proactively calls the old parent's [deactivateChild], first using |
4575 | /// [forgetChild] to cause the old parent to update its child model. |
4576 | @protected |
4577 | void deactivateChild(Element child) { |
4578 | assert(child._parent == this); |
4579 | child._parent = null; |
4580 | child.detachRenderObject(); |
4581 | owner!._inactiveElements.add(child); // this eventually calls child.deactivate() |
4582 | assert(() { |
4583 | if (debugPrintGlobalKeyedWidgetLifecycle) { |
4584 | if (child.widget.key is GlobalKey) { |
4585 | debugPrint('Deactivated$child(keyed child of$this)'); |
4586 | } |
4587 | } |
4588 | return true; |
4589 | }()); |
4590 | } |
4591 | |
4592 | // The children that have been forgotten by forgetChild. This will be used in |
4593 | // [update] to remove the global key reservations of forgotten children. |
4594 | // |
4595 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can |
4596 | // eliminate unused fields, but not their initializers. |
4597 | @_debugOnly |
4598 | final Set<Element>? _debugForgottenChildrenWithGlobalKey = kDebugMode ? HashSet<Element>() : null; |
4599 | |
4600 | /// Remove the given child from the element's child list, in preparation for |
4601 | /// the child being reused elsewhere in the element tree. |
4602 | /// |
4603 | /// This updates the child model such that, e.g., [visitChildren] does not |
4604 | /// walk that child anymore. |
4605 | /// |
4606 | /// The element will still have a valid parent when this is called, and the |
4607 | /// child's [Element.slot] value will be valid in the context of that parent. |
4608 | /// After this is called, [deactivateChild] is called to sever the link to |
4609 | /// this object. |
4610 | /// |
4611 | /// The [update] is responsible for updating or creating the new child that |
4612 | /// will replace this [child]. |
4613 | @protected |
4614 | @mustCallSuper |
4615 | void forgetChild(Element child) { |
4616 | // This method is called on the old parent when the given child (with a |
4617 | // global key) is given a new parent. We cannot remove the global key |
4618 | // reservation directly in this method because the forgotten child is not |
4619 | // removed from the tree until this Element is updated in [update]. If |
4620 | // [update] is never called, the forgotten child still represents a global |
4621 | // key duplication that we need to catch. |
4622 | assert(() { |
4623 | if (child.widget.key is GlobalKey) { |
4624 | _debugForgottenChildrenWithGlobalKey?.add(child); |
4625 | } |
4626 | return true; |
4627 | }()); |
4628 | } |
4629 | |
4630 | void _activateWithParent(Element parent, Object? newSlot) { |
4631 | assert(_lifecycleState == _ElementLifecycle.inactive); |
4632 | _parent = parent; |
4633 | _owner = parent.owner; |
4634 | assert(() { |
4635 | if (debugPrintGlobalKeyedWidgetLifecycle) { |
4636 | debugPrint('Reactivating$this(now child of$_parent).'); |
4637 | } |
4638 | return true; |
4639 | }()); |
4640 | _updateDepth(_parent!.depth); |
4641 | _updateBuildScopeRecursively(); |
4642 | _activateRecursively(this); |
4643 | attachRenderObject(newSlot); |
4644 | assert(_lifecycleState == _ElementLifecycle.active); |
4645 | } |
4646 | |
4647 | static void _activateRecursively(Element element) { |
4648 | assert(element._lifecycleState == _ElementLifecycle.inactive); |
4649 | element.activate(); |
4650 | assert(element._lifecycleState == _ElementLifecycle.active); |
4651 | element.visitChildren(_activateRecursively); |
4652 | } |
4653 | |
4654 | /// Transition from the "inactive" to the "active" lifecycle state. |
4655 | /// |
4656 | /// The framework calls this method when a previously deactivated element has |
4657 | /// been reincorporated into the tree. The framework does not call this method |
4658 | /// the first time an element becomes active (i.e., from the "initial" |
4659 | /// lifecycle state). Instead, the framework calls [mount] in that situation. |
4660 | /// |
4661 | /// See the lifecycle documentation for [Element] for additional information. |
4662 | /// |
4663 | /// Implementations of this method should start with a call to the inherited |
4664 | /// method, as in `super.activate()`. |
4665 | @mustCallSuper |
4666 | void activate() { |
4667 | assert(_lifecycleState == _ElementLifecycle.inactive); |
4668 | assert(owner != null); |
4669 | final bool hadDependencies = |
4670 | (_dependencies?.isNotEmpty ?? false) || _hadUnsatisfiedDependencies; |
4671 | _lifecycleState = _ElementLifecycle.active; |
4672 | // We unregistered our dependencies in deactivate, but never cleared the list. |
4673 | // Since we're going to be reused, let's clear our list now. |
4674 | _dependencies?.clear(); |
4675 | _hadUnsatisfiedDependencies = false; |
4676 | _updateInheritance(); |
4677 | attachNotificationTree(); |
4678 | if (_dirty) { |
4679 | owner!.scheduleBuildFor(this); |
4680 | } |
4681 | if (hadDependencies) { |
4682 | didChangeDependencies(); |
4683 | } |
4684 | } |
4685 | |
4686 | /// Transition from the "active" to the "inactive" lifecycle state. |
4687 | /// |
4688 | /// The framework calls this method when a previously active element is moved |
4689 | /// to the list of inactive elements. While in the inactive state, the element |
4690 | /// will not appear on screen. The element can remain in the inactive state |
4691 | /// only until the end of the current animation frame. At the end of the |
4692 | /// animation frame, if the element has not be reactivated, the framework will |
4693 | /// unmount the element. |
4694 | /// |
4695 | /// This is (indirectly) called by [deactivateChild]. |
4696 | /// |
4697 | /// See the lifecycle documentation for [Element] for additional information. |
4698 | /// |
4699 | /// Implementations of this method should end with a call to the inherited |
4700 | /// method, as in `super.deactivate()`. |
4701 | @mustCallSuper |
4702 | void deactivate() { |
4703 | assert(_lifecycleState == _ElementLifecycle.active); |
4704 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload. |
4705 | if (_dependencies?.isNotEmpty ?? false) { |
4706 | for (final InheritedElement dependency in _dependencies!) { |
4707 | dependency.removeDependent(this); |
4708 | } |
4709 | // For expediency, we don't actually clear the list here, even though it's |
4710 | // no longer representative of what we are registered with. If we never |
4711 | // get re-used, it doesn't matter. If we do, then we'll clear the list in |
4712 | // activate(). The benefit of this is that it allows Element's activate() |
4713 | // implementation to decide whether to rebuild based on whether we had |
4714 | // dependencies here. |
4715 | } |
4716 | _inheritedElements = null; |
4717 | _lifecycleState = _ElementLifecycle.inactive; |
4718 | } |
4719 | |
4720 | /// Called, in debug mode, after children have been deactivated (see [deactivate]). |
4721 | /// |
4722 | /// This method is not called in release builds. |
4723 | @mustCallSuper |
4724 | void debugDeactivated() { |
4725 | assert(_lifecycleState == _ElementLifecycle.inactive); |
4726 | } |
4727 | |
4728 | /// Transition from the "inactive" to the "defunct" lifecycle state. |
4729 | /// |
4730 | /// Called when the framework determines that an inactive element will never |
4731 | /// be reactivated. At the end of each animation frame, the framework calls |
4732 | /// [unmount] on any remaining inactive elements, preventing inactive elements |
4733 | /// from remaining inactive for longer than a single animation frame. |
4734 | /// |
4735 | /// After this function is called, the element will not be incorporated into |
4736 | /// the tree again. |
4737 | /// |
4738 | /// Any resources this element holds should be released at this point. For |
4739 | /// example, [RenderObjectElement.unmount] calls [RenderObject.dispose] and |
4740 | /// nulls out its reference to the render object. |
4741 | /// |
4742 | /// See the lifecycle documentation for [Element] for additional information. |
4743 | /// |
4744 | /// Implementations of this method should end with a call to the inherited |
4745 | /// method, as in `super.unmount()`. |
4746 | @mustCallSuper |
4747 | void unmount() { |
4748 | assert(_lifecycleState == _ElementLifecycle.inactive); |
4749 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload. |
4750 | assert(owner != null); |
4751 | assert(debugMaybeDispatchDisposed(this)); |
4752 | // Use the private property to avoid a CastError during hot reload. |
4753 | final Key? key = _widget?.key; |
4754 | if (key is GlobalKey) { |
4755 | owner!._unregisterGlobalKey(key, this); |
4756 | } |
4757 | // Release resources to reduce the severity of memory leaks caused by |
4758 | // defunct, but accidentally retained Elements. |
4759 | _widget = null; |
4760 | _dependencies = null; |
4761 | _lifecycleState = _ElementLifecycle.defunct; |
4762 | } |
4763 | |
4764 | /// Whether the child in the provided `slot` (or one of its descendants) must |
4765 | /// insert a [RenderObject] into its ancestor [RenderObjectElement] by calling |
4766 | /// [RenderObjectElement.insertRenderObjectChild] on it. |
4767 | /// |
4768 | /// This method is used to define non-rendering zones in the element tree (see |
4769 | /// [WidgetsBinding] for an explanation of rendering and non-rendering zones): |
4770 | /// |
4771 | /// Most branches of the [Element] tree are expected to eventually insert a |
4772 | /// [RenderObject] into their [RenderObjectElement] ancestor to construct the |
4773 | /// render tree. However, there is a notable exception: an [Element] may |
4774 | /// expect that the occupant of a certain child slot creates a new independent |
4775 | /// render tree and therefore is not allowed to insert a render object into |
4776 | /// the existing render tree. Those elements must return false from this |
4777 | /// method for the slot in question to signal to the child in that slot that |
4778 | /// it must not call [RenderObjectElement.insertRenderObjectChild] on its |
4779 | /// ancestor. |
4780 | /// |
4781 | /// As an example, the element backing the [ViewAnchor] returns false from |
4782 | /// this method for the [ViewAnchor.view] slot to enforce that it is occupied |
4783 | /// by e.g. a [View] widget, which will ultimately bootstrap a separate |
4784 | /// render tree for that view. Another example is the [ViewCollection] widget, |
4785 | /// which returns false for all its slots for the same reason. |
4786 | /// |
4787 | /// Overriding this method is not common, as elements behaving in the way |
4788 | /// described above are rare. |
4789 | bool debugExpectsRenderObjectForSlot(Object? slot) => true; |
4790 | |
4791 | @override |
4792 | RenderObject? findRenderObject() { |
4793 | assert(() { |
4794 | if (_lifecycleState != _ElementLifecycle.active) { |
4795 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4796 | ErrorSummary('Cannot get renderObject of inactive element.'), |
4797 | ErrorDescription( |
4798 | 'In order for an element to have a valid renderObject, it must be ' |
4799 | 'active, which means it is part of the tree.\n' |
4800 | 'Instead, this element is in the$_lifecycleStatestate.\n' |
4801 | 'If you called this method from a State object, consider guarding ' |
4802 | 'it with State.mounted.', |
4803 | ), |
4804 | describeElement('The findRenderObject() method was called for the following element'), |
4805 | ]); |
4806 | } |
4807 | return true; |
4808 | }()); |
4809 | return renderObject; |
4810 | } |
4811 | |
4812 | @override |
4813 | Size? get size { |
4814 | assert(() { |
4815 | if (_lifecycleState != _ElementLifecycle.active) { |
4816 | // TODO(jacobr): is this a good separation into contract and violation? |
4817 | // I have added a line of white space. |
4818 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4819 | ErrorSummary('Cannot get size of inactive element.'), |
4820 | ErrorDescription( |
4821 | 'In order for an element to have a valid size, the element must be ' |
4822 | 'active, which means it is part of the tree.\n' |
4823 | 'Instead, this element is in the$_lifecycleStatestate.', |
4824 | ), |
4825 | describeElement('The size getter was called for the following element'), |
4826 | ]); |
4827 | } |
4828 | if (owner!._debugBuilding) { |
4829 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4830 | ErrorSummary('Cannot get size during build.'), |
4831 | ErrorDescription( |
4832 | 'The size of this render object has not yet been determined because ' |
4833 | 'the framework is still in the process of building widgets, which ' |
4834 | 'means the render tree for this frame has not yet been determined. ' |
4835 | 'The size getter should only be called from paint callbacks or ' |
4836 | 'interaction event handlers (e.g. gesture callbacks).', |
4837 | ), |
4838 | ErrorSpacer(), |
4839 | ErrorHint( |
4840 | 'If you need some sizing information during build to decide which ' |
4841 | 'widgets to build, consider using a LayoutBuilder widget, which can ' |
4842 | 'tell you the layout constraints at a given location in the tree. See ' |
4843 | '<https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html> ' |
4844 | 'for more details.', |
4845 | ), |
4846 | ErrorSpacer(), |
4847 | describeElement('The size getter was called for the following element'), |
4848 | ]); |
4849 | } |
4850 | return true; |
4851 | }()); |
4852 | final RenderObject? renderObject = findRenderObject(); |
4853 | assert(() { |
4854 | if (renderObject == null) { |
4855 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4856 | ErrorSummary('Cannot get size without a render object.'), |
4857 | ErrorHint( |
4858 | 'In order for an element to have a valid size, the element must have ' |
4859 | 'an associated render object. This element does not have an associated ' |
4860 | 'render object, which typically means that the size getter was called ' |
4861 | 'too early in the pipeline (e.g., during the build phase) before the ' |
4862 | 'framework has created the render tree.', |
4863 | ), |
4864 | describeElement('The size getter was called for the following element'), |
4865 | ]); |
4866 | } |
4867 | if (renderObject is RenderSliver) { |
4868 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4869 | ErrorSummary('Cannot get size from a RenderSliver.'), |
4870 | ErrorHint( |
4871 | 'The render object associated with this element is a ' |
4872 | '${renderObject.runtimeType}, which is a subtype of RenderSliver. ' |
4873 | 'Slivers do not have a size per se. They have a more elaborate ' |
4874 | 'geometry description, which can be accessed by calling ' |
4875 | 'findRenderObject and then using the "geometry" getter on the ' |
4876 | 'resulting object.', |
4877 | ), |
4878 | describeElement('The size getter was called for the following element'), |
4879 | renderObject.describeForError('The associated render sliver was'), |
4880 | ]); |
4881 | } |
4882 | if (renderObject is! RenderBox) { |
4883 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4884 | ErrorSummary('Cannot get size from a render object that is not a RenderBox.'), |
4885 | ErrorHint( |
4886 | 'Instead of being a subtype of RenderBox, the render object associated ' |
4887 | 'with this element is a${renderObject.runtimeType}. If this type of ' |
4888 | 'render object does have a size, consider calling findRenderObject ' |
4889 | 'and extracting its size manually.', |
4890 | ), |
4891 | describeElement('The size getter was called for the following element'), |
4892 | renderObject.describeForError('The associated render object was'), |
4893 | ]); |
4894 | } |
4895 | final RenderBox box = renderObject; |
4896 | if (!box.hasSize) { |
4897 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4898 | ErrorSummary('Cannot get size from a render object that has not been through layout.'), |
4899 | ErrorHint( |
4900 | 'The size of this render object has not yet been determined because ' |
4901 | 'this render object has not yet been through layout, which typically ' |
4902 | 'means that the size getter was called too early in the pipeline ' |
4903 | '(e.g., during the build phase) before the framework has determined ' |
4904 | 'the size and position of the render objects during layout.', |
4905 | ), |
4906 | describeElement('The size getter was called for the following element'), |
4907 | box.describeForError('The render object from which the size was to be obtained was'), |
4908 | ]); |
4909 | } |
4910 | if (box.debugNeedsLayout) { |
4911 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4912 | ErrorSummary( |
4913 | 'Cannot get size from a render object that has been marked dirty for layout.', |
4914 | ), |
4915 | ErrorHint( |
4916 | 'The size of this render object is ambiguous because this render object has ' |
4917 | 'been modified since it was last laid out, which typically means that the size ' |
4918 | 'getter was called too early in the pipeline (e.g., during the build phase) ' |
4919 | 'before the framework has determined the size and position of the render ' |
4920 | 'objects during layout.', |
4921 | ), |
4922 | describeElement('The size getter was called for the following element'), |
4923 | box.describeForError('The render object from which the size was to be obtained was'), |
4924 | ErrorHint( |
4925 | 'Consider using debugPrintMarkNeedsLayoutStacks to determine why the render ' |
4926 | 'object in question is dirty, if you did not expect this.', |
4927 | ), |
4928 | ]); |
4929 | } |
4930 | return true; |
4931 | }()); |
4932 | if (renderObject is RenderBox) { |
4933 | return renderObject.size; |
4934 | } |
4935 | return null; |
4936 | } |
4937 | |
4938 | PersistentHashMap<Type, InheritedElement>? _inheritedElements; |
4939 | Set<InheritedElement>? _dependencies; |
4940 | bool _hadUnsatisfiedDependencies = false; |
4941 | |
4942 | bool _debugCheckStateIsActiveForAncestorLookup() { |
4943 | assert(() { |
4944 | if (_lifecycleState != _ElementLifecycle.active) { |
4945 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
4946 | ErrorSummary("Looking up a deactivated widget's ancestor is unsafe."), |
4947 | ErrorDescription( |
4948 | "At this point the state of the widget's element tree is no longer " |
4949 | 'stable.', |
4950 | ), |
4951 | ErrorHint( |
4952 | "To safely refer to a widget's ancestor in its dispose() method, " |
4953 | 'save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() ' |
4954 | "in the widget's didChangeDependencies() method.", |
4955 | ), |
4956 | ]); |
4957 | } |
4958 | return true; |
4959 | }()); |
4960 | return true; |
4961 | } |
4962 | |
4963 | /// Returns `true` if [dependOnInheritedElement] was previously called with [ancestor]. |
4964 | @protected |
4965 | bool doesDependOnInheritedElement(InheritedElement ancestor) { |
4966 | return _dependencies?.contains(ancestor) ?? false; |
4967 | } |
4968 | |
4969 | @override |
4970 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect}) { |
4971 | _dependencies ??= HashSet<InheritedElement>(); |
4972 | _dependencies!.add(ancestor); |
4973 | ancestor.updateDependencies(this, aspect); |
4974 | return ancestor.widget as InheritedWidget; |
4975 | } |
4976 | |
4977 | @override |
4978 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) { |
4979 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
4980 | final InheritedElement? ancestor = _inheritedElements?[T]; |
4981 | if (ancestor != null) { |
4982 | return dependOnInheritedElement(ancestor, aspect: aspect) as T; |
4983 | } |
4984 | _hadUnsatisfiedDependencies = true; |
4985 | return null; |
4986 | } |
4987 | |
4988 | @override |
4989 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>() { |
4990 | return getElementForInheritedWidgetOfExactType<T>()?.widget as T?; |
4991 | } |
4992 | |
4993 | @override |
4994 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() { |
4995 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
4996 | return _inheritedElements?[T]; |
4997 | } |
4998 | |
4999 | /// Called in [Element.mount] and [Element.activate] to register this element in |
5000 | /// the notification tree. |
5001 | /// |
5002 | /// This method is only exposed so that [NotifiableElementMixin] can be implemented. |
5003 | /// Subclasses of [Element] that wish to respond to notifications should mix that |
5004 | /// in instead. |
5005 | /// |
5006 | /// See also: |
5007 | /// * [NotificationListener], a widget that allows listening to notifications. |
5008 | @protected |
5009 | void attachNotificationTree() { |
5010 | _notificationTree = _parent?._notificationTree; |
5011 | } |
5012 | |
5013 | void _updateInheritance() { |
5014 | assert(_lifecycleState == _ElementLifecycle.active); |
5015 | _inheritedElements = _parent?._inheritedElements; |
5016 | } |
5017 | |
5018 | @override |
5019 | T? findAncestorWidgetOfExactType<T extends Widget>() { |
5020 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
5021 | Element? ancestor = _parent; |
5022 | while (ancestor != null && ancestor.widget.runtimeType != T) { |
5023 | ancestor = ancestor._parent; |
5024 | } |
5025 | return ancestor?.widget as T?; |
5026 | } |
5027 | |
5028 | @override |
5029 | T? findAncestorStateOfType<T extends State<StatefulWidget>>() { |
5030 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
5031 | Element? ancestor = _parent; |
5032 | while (ancestor != null) { |
5033 | if (ancestor is StatefulElement && ancestor.state is T) { |
5034 | break; |
5035 | } |
5036 | ancestor = ancestor._parent; |
5037 | } |
5038 | final StatefulElement? statefulAncestor = ancestor as StatefulElement?; |
5039 | return statefulAncestor?.state as T?; |
5040 | } |
5041 | |
5042 | @override |
5043 | T? findRootAncestorStateOfType<T extends State<StatefulWidget>>() { |
5044 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
5045 | Element? ancestor = _parent; |
5046 | StatefulElement? statefulAncestor; |
5047 | while (ancestor != null) { |
5048 | if (ancestor is StatefulElement && ancestor.state is T) { |
5049 | statefulAncestor = ancestor; |
5050 | } |
5051 | ancestor = ancestor._parent; |
5052 | } |
5053 | return statefulAncestor?.state as T?; |
5054 | } |
5055 | |
5056 | @override |
5057 | T? findAncestorRenderObjectOfType<T extends RenderObject>() { |
5058 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
5059 | Element? ancestor = _parent; |
5060 | while (ancestor != null) { |
5061 | if (ancestor is RenderObjectElement && ancestor.renderObject is T) { |
5062 | return ancestor.renderObject as T; |
5063 | } |
5064 | ancestor = ancestor._parent; |
5065 | } |
5066 | return null; |
5067 | } |
5068 | |
5069 | @override |
5070 | void visitAncestorElements(ConditionalElementVisitor visitor) { |
5071 | assert(_debugCheckStateIsActiveForAncestorLookup()); |
5072 | Element? ancestor = _parent; |
5073 | while (ancestor != null && visitor(ancestor)) { |
5074 | ancestor = ancestor._parent; |
5075 | } |
5076 | } |
5077 | |
5078 | /// Called when a dependency of this element changes. |
5079 | /// |
5080 | /// The [dependOnInheritedWidgetOfExactType] registers this element as depending on |
5081 | /// inherited information of the given type. When the information of that type |
5082 | /// changes at this location in the tree (e.g., because the [InheritedElement] |
5083 | /// updated to a new [InheritedWidget] and |
5084 | /// [InheritedWidget.updateShouldNotify] returned true), the framework calls |
5085 | /// this function to notify this element of the change. |
5086 | @mustCallSuper |
5087 | void didChangeDependencies() { |
5088 | assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op |
5089 | assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies')); |
5090 | markNeedsBuild(); |
5091 | } |
5092 | |
5093 | bool _debugCheckOwnerBuildTargetExists(String methodName) { |
5094 | assert(() { |
5095 | if (owner!._debugCurrentBuildTarget == null) { |
5096 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5097 | ErrorSummary( |
5098 | '$methodNamefor${widget.runtimeType}was called at an ' |
5099 | 'inappropriate time.', |
5100 | ), |
5101 | ErrorDescription('It may only be called while the widgets are being built.'), |
5102 | ErrorHint( |
5103 | 'A possible cause of this error is when$methodNameis called during ' |
5104 | 'one of:\n' |
5105 | ' * network I/O event\n' |
5106 | ' * file I/O event\n' |
5107 | ' * timer\n' |
5108 | ' * microtask (caused by Future.then, async/await, scheduleMicrotask)', |
5109 | ), |
5110 | ]); |
5111 | } |
5112 | return true; |
5113 | }()); |
5114 | return true; |
5115 | } |
5116 | |
5117 | /// Returns a description of what caused this element to be created. |
5118 | /// |
5119 | /// Useful for debugging the source of an element. |
5120 | String debugGetCreatorChain(int limit) { |
5121 | final List<String> chain = <String>[]; |
5122 | Element? node = this; |
5123 | while (chain.length < limit && node != null) { |
5124 | chain.add(node.toStringShort()); |
5125 | node = node._parent; |
5126 | } |
5127 | if (node != null) { |
5128 | chain.add('\u22EF'); |
5129 | } |
5130 | return chain.join(' \u2190 '); |
5131 | } |
5132 | |
5133 | /// Returns the parent chain from this element back to the root of the tree. |
5134 | /// |
5135 | /// Useful for debug display of a tree of Elements with only nodes in the path |
5136 | /// from the root to this Element expanded. |
5137 | List<Element> debugGetDiagnosticChain() { |
5138 | final List<Element> chain = <Element>[this]; |
5139 | Element? node = _parent; |
5140 | while (node != null) { |
5141 | chain.add(node); |
5142 | node = node._parent; |
5143 | } |
5144 | return chain; |
5145 | } |
5146 | |
5147 | @override |
5148 | void dispatchNotification(Notification notification) { |
5149 | _notificationTree?.dispatchNotification(notification); |
5150 | } |
5151 | |
5152 | /// A short, textual description of this element. |
5153 | @override |
5154 | String toStringShort() => _widget?.toStringShort() ??'${describeIdentity(this)}(DEFUNCT)'; |
5155 | |
5156 | @override |
5157 | DiagnosticsNode toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) { |
5158 | return _ElementDiagnosticableTreeNode(name: name, value: this, style: style); |
5159 | } |
5160 | |
5161 | @override |
5162 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
5163 | super.debugFillProperties(properties); |
5164 | properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; |
5165 | if (_lifecycleState != _ElementLifecycle.initial) { |
5166 | properties.add(ObjectFlagProperty<int>('depth', depth, ifNull:'no depth')); |
5167 | } |
5168 | properties.add(ObjectFlagProperty<Widget>('widget', _widget, ifNull:'no widget')); |
5169 | properties.add( |
5170 | DiagnosticsProperty<Key>( |
5171 | 'key', |
5172 | _widget?.key, |
5173 | showName: false, |
5174 | defaultValue: null, |
5175 | level: DiagnosticLevel.hidden, |
5176 | ), |
5177 | ); |
5178 | _widget?.debugFillProperties(properties); |
5179 | properties.add(FlagProperty('dirty', value: dirty, ifTrue:'dirty')); |
5180 | final Set<InheritedElement>? deps = _dependencies; |
5181 | if (deps != null && deps.isNotEmpty) { |
5182 | final List<InheritedElement> sortedDependencies = |
5183 | deps.toList()..sort( |
5184 | (InheritedElement a, InheritedElement b) => |
5185 | a.toStringShort().compareTo(b.toStringShort()), |
5186 | ); |
5187 | final List<DiagnosticsNode> diagnosticsDependencies = |
5188 | sortedDependencies |
5189 | .map( |
5190 | (InheritedElement element) => |
5191 | element.widget.toDiagnosticsNode(style: DiagnosticsTreeStyle.sparse), |
5192 | ) |
5193 | .toList(); |
5194 | properties.add( |
5195 | DiagnosticsProperty<Set<InheritedElement>>( |
5196 | 'dependencies', |
5197 | deps, |
5198 | description: diagnosticsDependencies.toString(), |
5199 | ), |
5200 | ); |
5201 | } |
5202 | } |
5203 | |
5204 | @override |
5205 | List<DiagnosticsNode> debugDescribeChildren() { |
5206 | final List<DiagnosticsNode> children = <DiagnosticsNode>[]; |
5207 | visitChildren((Element child) { |
5208 | children.add(child.toDiagnosticsNode()); |
5209 | }); |
5210 | return children; |
5211 | } |
5212 | |
5213 | /// Returns true if the element has been marked as needing rebuilding. |
5214 | /// |
5215 | /// The flag is true when the element is first created and after |
5216 | /// [markNeedsBuild] has been called. The flag is typically reset to false in |
5217 | /// the [performRebuild] implementation, but certain elements (that of the |
5218 | /// [LayoutBuilder] widget, for example) may choose to override [markNeedsBuild] |
5219 | /// such that it does not set the [dirty] flag to `true` when called. |
5220 | bool get dirty => _dirty; |
5221 | bool _dirty = true; |
5222 | |
5223 | // Whether this is in _buildScope._dirtyElements. This is used to know whether |
5224 | // we should be adding the element back into the list when it's reactivated. |
5225 | bool _inDirtyList = false; |
5226 | |
5227 | // Whether we've already built or not. Set in [rebuild]. |
5228 | bool _debugBuiltOnce = false; |
5229 | |
5230 | /// Marks the element as dirty and adds it to the global list of widgets to |
5231 | /// rebuild in the next frame. |
5232 | /// |
5233 | /// Since it is inefficient to build an element twice in one frame, |
5234 | /// applications and widgets should be structured so as to only mark |
5235 | /// widgets dirty during event handlers before the frame begins, not during |
5236 | /// the build itself. |
5237 | void markNeedsBuild() { |
5238 | assert(_lifecycleState != _ElementLifecycle.defunct); |
5239 | if (_lifecycleState != _ElementLifecycle.active) { |
5240 | return; |
5241 | } |
5242 | assert(owner != null); |
5243 | assert(_lifecycleState == _ElementLifecycle.active); |
5244 | assert(() { |
5245 | if (owner!._debugBuilding) { |
5246 | assert(owner!._debugCurrentBuildTarget != null); |
5247 | assert(owner!._debugStateLocked); |
5248 | if (_debugIsDescendantOf(owner!._debugCurrentBuildTarget!)) { |
5249 | return true; |
5250 | } |
5251 | final List<DiagnosticsNode> information = <DiagnosticsNode>[ |
5252 | ErrorSummary('setState() or markNeedsBuild() called during build.'), |
5253 | ErrorDescription( |
5254 | 'This${widget.runtimeType}widget cannot be marked as needing to build because the framework ' |
5255 | 'is already in the process of building widgets. A widget can be marked as ' |
5256 | 'needing to be built during the build phase only if one of its ancestors ' |
5257 | 'is currently building. This exception is allowed because the framework ' |
5258 | 'builds parent widgets before children, which means a dirty descendant ' |
5259 | 'will always be built. Otherwise, the framework might not visit this ' |
5260 | 'widget during this build phase.', |
5261 | ), |
5262 | describeElement('The widget on which setState() or markNeedsBuild() was called was'), |
5263 | ]; |
5264 | if (owner!._debugCurrentBuildTarget != null) { |
5265 | information.add( |
5266 | owner!._debugCurrentBuildTarget!.describeWidget( |
5267 | 'The widget which was currently being built when the offending call was made was', |
5268 | ), |
5269 | ); |
5270 | } |
5271 | throw FlutterError.fromParts(information); |
5272 | } else if (owner!._debugStateLocked) { |
5273 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5274 | ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'), |
5275 | ErrorDescription( |
5276 | 'This${widget.runtimeType}widget cannot be marked as needing to build ' |
5277 | 'because the framework is locked.', |
5278 | ), |
5279 | describeElement('The widget on which setState() or markNeedsBuild() was called was'), |
5280 | ]); |
5281 | } |
5282 | return true; |
5283 | }()); |
5284 | if (dirty) { |
5285 | return; |
5286 | } |
5287 | _dirty = true; |
5288 | owner!.scheduleBuildFor(this); |
5289 | } |
5290 | |
5291 | /// Cause the widget to update itself. In debug builds, also verify various |
5292 | /// invariants. |
5293 | /// |
5294 | /// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been |
5295 | /// called to mark this element dirty, by [mount] when the element is first |
5296 | /// built, and by [update] when the widget has changed. |
5297 | /// |
5298 | /// The method will only rebuild if [dirty] is true. To rebuild regardless |
5299 | /// of the [dirty] flag, set `force` to true. Forcing a rebuild is convenient |
5300 | /// from [update], during which [dirty] is false. |
5301 | /// |
5302 | /// ## When rebuilds happen |
5303 | /// |
5304 | /// ### Terminology |
5305 | /// |
5306 | /// [Widget]s represent the configuration of [Element]s. Each [Element] has a |
5307 | /// widget, specified in [Element.widget]. The term "widget" is often used |
5308 | /// when strictly speaking "element" would be more correct. |
5309 | /// |
5310 | /// While an [Element] has a current [Widget], over time, that widget may be |
5311 | /// replaced by others. For example, the element backing a [ColoredBox] may |
5312 | /// first have as its widget a [ColoredBox] whose [ColoredBox.color] is blue, |
5313 | /// then later be given a new [ColoredBox] whose color is green. |
5314 | /// |
5315 | /// At any particular time, multiple [Element]s in the same tree may have the |
5316 | /// same [Widget]. For example, the same [ColoredBox] with the green color may |
5317 | /// be used in multiple places in the widget tree at the same time, each being |
5318 | /// backed by a different [Element]. |
5319 | /// |
5320 | /// ### Marking an element dirty |
5321 | /// |
5322 | /// An [Element] can be marked dirty between frames. This can happen for various |
5323 | /// reasons, including the following: |
5324 | /// |
5325 | /// * The [State] of a [StatefulWidget] can cause its [Element] to be marked |
5326 | /// dirty by calling the [State.setState] method. |
5327 | /// |
5328 | /// * When an [InheritedWidget] changes, descendants that have previously |
5329 | /// subscribed to it will be marked dirty. |
5330 | /// |
5331 | /// * During a hot reload, every element is marked dirty (using [Element.reassemble]). |
5332 | /// |
5333 | /// ### Rebuilding |
5334 | /// |
5335 | /// Dirty elements are rebuilt during the next frame. Precisely how this is |
5336 | /// done depends on the kind of element. A [StatelessElement] rebuilds by |
5337 | /// using its widget's [StatelessWidget.build] method. A [StatefulElement] |
5338 | /// rebuilds by using its widget's state's [State.build] method. A |
5339 | /// [RenderObjectElement] rebuilds by updating its [RenderObject]. |
5340 | /// |
5341 | /// In many cases, the end result of rebuilding is a single child widget |
5342 | /// or (for [MultiChildRenderObjectElement]s) a list of children widgets. |
5343 | /// |
5344 | /// These child widgets are used to update the [widget] property of the |
5345 | /// element's child (or children) elements. The new [Widget] is considered to |
5346 | /// correspond to an existing [Element] if it has the same [Type] and [Key]. |
5347 | /// (In the case of [MultiChildRenderObjectElement]s, some effort is put into |
5348 | /// tracking widgets even when they change order; see |
5349 | /// [RenderObjectElement.updateChildren].) |
5350 | /// |
5351 | /// If there was no corresponding previous child, this results in a new |
5352 | /// [Element] being created (using [Widget.createElement]); that element is |
5353 | /// then itself built, recursively. |
5354 | /// |
5355 | /// If there was a child previously but the build did not provide a |
5356 | /// corresponding child to update it, then the old child is discarded (or, in |
5357 | /// cases involving [GlobalKey] reparenting, reused elsewhere in the element |
5358 | /// tree). |
5359 | /// |
5360 | /// The most common case, however, is that there was a corresponding previous |
5361 | /// child. This is handled by asking the child [Element] to update itself |
5362 | /// using the new child [Widget]. In the case of [StatefulElement]s, this |
5363 | /// is what triggers [State.didUpdateWidget]. |
5364 | /// |
5365 | /// ### Not rebuilding |
5366 | /// |
5367 | /// Before an [Element] is told to update itself with a new [Widget], the old |
5368 | /// and new objects are compared using `operator ==`. |
5369 | /// |
5370 | /// In general, this is equivalent to doing a comparison using [identical] to |
5371 | /// see if the two objects are in fact the exact same instance. If they are, |
5372 | /// and if the element is not already marked dirty for other reasons, then the |
5373 | /// element skips updating itself as it can determine with certainty that |
5374 | /// there would be no value in updating itself or its descendants. |
5375 | /// |
5376 | /// It is strongly advised to avoid overriding `operator ==` on [Widget] |
5377 | /// objects. While doing so seems like it could improve performance, in |
5378 | /// practice, for non-leaf widgets, it results in O(N²) behavior. This is |
5379 | /// because by necessity the comparison would have to include comparing child |
5380 | /// widgets, and if those child widgets also implement `operator ==`, it |
5381 | /// ultimately results in a complete walk of the widget tree... which is then |
5382 | /// repeated at each level of the tree. In practice, just rebuilding is |
5383 | /// cheaper. (Additionally, if _any_ subclass of [Widget] used in an |
5384 | /// application implements `operator ==`, then the compiler cannot inline the |
5385 | /// comparison anywhere, because it has to treat the call as virtual just in |
5386 | /// case the instance happens to be one that has an overridden operator.) |
5387 | /// |
5388 | /// Instead, the best way to avoid unnecessary rebuilds is to cache the |
5389 | /// widgets that are returned from [State.build], so that each frame the same |
5390 | /// widgets are used until such time as they change. Several mechanisms exist |
5391 | /// to encourage this: `const` widgets, for example, are a form of automatic |
5392 | /// caching (if a widget is constructed using the `const` keyword, the same |
5393 | /// instance is returned each time it is constructed with the same arguments). |
5394 | /// |
5395 | /// Another example is the [AnimatedBuilder.child] property, which allows the |
5396 | /// non-animating parts of a subtree to remain static even as the |
5397 | /// [AnimatedBuilder.builder] callback recreates the other components. |
5398 | @pragma('dart2js:tryInline') |
5399 | @pragma('vm:prefer-inline') |
5400 | @pragma('wasm:prefer-inline') |
5401 | void rebuild({bool force = false}) { |
5402 | assert(_lifecycleState != _ElementLifecycle.initial); |
5403 | if (_lifecycleState != _ElementLifecycle.active || (!_dirty && !force)) { |
5404 | return; |
5405 | } |
5406 | assert(() { |
5407 | debugOnRebuildDirtyWidget?.call(this, _debugBuiltOnce); |
5408 | if (debugPrintRebuildDirtyWidgets) { |
5409 | if (!_debugBuiltOnce) { |
5410 | debugPrint('Building$this'); |
5411 | _debugBuiltOnce = true; |
5412 | } else { |
5413 | debugPrint('Rebuilding$this'); |
5414 | } |
5415 | } |
5416 | return true; |
5417 | }()); |
5418 | assert(_lifecycleState == _ElementLifecycle.active); |
5419 | assert(owner!._debugStateLocked); |
5420 | Element? debugPreviousBuildTarget; |
5421 | assert(() { |
5422 | debugPreviousBuildTarget = owner!._debugCurrentBuildTarget; |
5423 | owner!._debugCurrentBuildTarget = this; |
5424 | return true; |
5425 | }()); |
5426 | try { |
5427 | performRebuild(); |
5428 | } finally { |
5429 | assert(() { |
5430 | owner!._debugElementWasRebuilt(this); |
5431 | assert(owner!._debugCurrentBuildTarget == this); |
5432 | owner!._debugCurrentBuildTarget = debugPreviousBuildTarget; |
5433 | return true; |
5434 | }()); |
5435 | } |
5436 | assert(!_dirty); |
5437 | } |
5438 | |
5439 | /// Cause the widget to update itself. |
5440 | /// |
5441 | /// Called by [rebuild] after the appropriate checks have been made. |
5442 | /// |
5443 | /// The base implementation only clears the [dirty] flag. |
5444 | @protected |
5445 | @mustCallSuper |
5446 | void performRebuild() { |
5447 | _dirty = false; |
5448 | } |
5449 | } |
5450 | |
5451 | class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode { |
5452 | _ElementDiagnosticableTreeNode({ |
5453 | super.name, |
5454 | required Element super.value, |
5455 | required super.style, |
5456 | this.stateful = false, |
5457 | }); |
5458 | |
5459 | final bool stateful; |
5460 | |
5461 | @override |
5462 | Map<String, Object?> toJsonMap(DiagnosticsSerializationDelegate delegate) { |
5463 | final Map<String, Object?> json = super.toJsonMap(delegate); |
5464 | final Element element = value as Element; |
5465 | if (!element.debugIsDefunct) { |
5466 | json['widgetRuntimeType'] = element.widget.runtimeType.toString(); |
5467 | } |
5468 | json['stateful'] = stateful; |
5469 | return json; |
5470 | } |
5471 | } |
5472 | |
5473 | /// Signature for the constructor that is called when an error occurs while |
5474 | /// building a widget. |
5475 | /// |
5476 | /// The argument provides information regarding the cause of the error. |
5477 | /// |
5478 | /// See also: |
5479 | /// |
5480 | /// * [ErrorWidget.builder], which can be set to override the default |
5481 | /// [ErrorWidget] builder. |
5482 | /// * [FlutterError.reportError], which is typically called with the same |
5483 | /// [FlutterErrorDetails] object immediately prior to [ErrorWidget.builder] |
5484 | /// being called. |
5485 | typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details); |
5486 | |
5487 | /// A widget that renders an exception's message. |
5488 | /// |
5489 | /// This widget is used when a build method fails, to help with determining |
5490 | /// where the problem lies. Exceptions are also logged to the console, which you |
5491 | /// can read using `flutter logs`. The console will also include additional |
5492 | /// information such as the stack trace for the exception. |
5493 | /// |
5494 | /// It is possible to override this widget. |
5495 | /// |
5496 | /// {@tool dartpad} |
5497 | /// This example shows how to override the standard error widget builder in release |
5498 | /// mode, but use the standard one in debug mode. |
5499 | /// |
5500 | /// The error occurs when you click the "Error Prone" button. |
5501 | /// |
5502 | /// ** See code in examples/api/lib/widgets/framework/error_widget.0.dart ** |
5503 | /// {@end-tool} |
5504 | /// |
5505 | /// See also: |
5506 | /// |
5507 | /// * [FlutterError.onError], which can be set to a method that exits the |
5508 | /// application if that is preferable to showing an error message. |
5509 | /// * <https://docs.flutter.dev/testing/errors>, more information about error |
5510 | /// handling in Flutter. |
5511 | class ErrorWidget extends LeafRenderObjectWidget { |
5512 | /// Creates a widget that displays the given exception. |
5513 | /// |
5514 | /// The message will be the stringification of the given exception, unless |
5515 | /// computing that value itself throws an exception, in which case it will |
5516 | /// be the string "Error". |
5517 | /// |
5518 | /// If this object is inspected from an IDE or the devtools, and the original |
5519 | /// exception is a [FlutterError] object, the original exception itself will |
5520 | /// be shown in the inspection output. |
5521 | ErrorWidget(Object exception) |
5522 | : message = _stringify(exception), |
5523 | _flutterError = exception is FlutterError ? exception : null, |
5524 | super(key: UniqueKey()); |
5525 | |
5526 | /// Creates a widget that displays the given error message. |
5527 | /// |
5528 | /// An explicit [FlutterError] can be provided to be reported to inspection |
5529 | /// tools. It need not match the message. |
5530 | ErrorWidget.withDetails({this.message ='', FlutterError? error}) |
5531 | : _flutterError = error, |
5532 | super(key: UniqueKey()); |
5533 | |
5534 | /// The configurable factory for [ErrorWidget]. |
5535 | /// |
5536 | /// When an error occurs while building a widget, the broken widget is |
5537 | /// replaced by the widget returned by this function. By default, an |
5538 | /// [ErrorWidget] is returned. |
5539 | /// |
5540 | /// The system is typically in an unstable state when this function is called. |
5541 | /// An exception has just been thrown in the middle of build (and possibly |
5542 | /// layout), so surrounding widgets and render objects may be in a rather |
5543 | /// fragile state. The framework itself (especially the [BuildOwner]) may also |
5544 | /// be confused, and additional exceptions are quite likely to be thrown. |
5545 | /// |
5546 | /// Because of this, it is highly recommended that the widget returned from |
5547 | /// this function perform the least amount of work possible. A |
5548 | /// [LeafRenderObjectWidget] is the best choice, especially one that |
5549 | /// corresponds to a [RenderBox] that can handle the most absurd of incoming |
5550 | /// constraints. The default constructor maps to a [RenderErrorBox]. |
5551 | /// |
5552 | /// The default behavior is to show the exception's message in debug mode, |
5553 | /// and to show nothing but a gray background in release builds. |
5554 | /// |
5555 | /// See also: |
5556 | /// |
5557 | /// * [FlutterError.onError], which is typically called with the same |
5558 | /// [FlutterErrorDetails] object immediately prior to this callback being |
5559 | /// invoked, and which can also be configured to control how errors are |
5560 | /// reported. |
5561 | /// * <https://docs.flutter.dev/testing/errors>, more information about error |
5562 | /// handling in Flutter. |
5563 | static ErrorWidgetBuilder builder = _defaultErrorWidgetBuilder; |
5564 | |
5565 | static Widget _defaultErrorWidgetBuilder(FlutterErrorDetails details) { |
5566 | String message =''; |
5567 | assert(() { |
5568 | message = |
5569 | '${_stringify(details.exception)}\nSee also: https://docs.flutter.dev/testing/errors'; |
5570 | return true; |
5571 | }()); |
5572 | final Object exception = details.exception; |
5573 | return ErrorWidget.withDetails( |
5574 | message: message, |
5575 | error: exception is FlutterError ? exception : null, |
5576 | ); |
5577 | } |
5578 | |
5579 | static String _stringify(Object? exception) { |
5580 | try { |
5581 | return exception.toString(); |
5582 | } catch (error) { |
5583 | // If we get here, it means things have really gone off the rails, and we're better |
5584 | // off just returning a simple string and letting the developer find out what the |
5585 | // root cause of all their problems are by looking at the console logs. |
5586 | } |
5587 | return'Error'; |
5588 | } |
5589 | |
5590 | /// The message to display. |
5591 | final String message; |
5592 | final FlutterError? _flutterError; |
5593 | |
5594 | @override |
5595 | RenderBox createRenderObject(BuildContext context) => RenderErrorBox(message); |
5596 | |
5597 | @override |
5598 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
5599 | super.debugFillProperties(properties); |
5600 | if (_flutterError == null) { |
5601 | properties.add(StringProperty('message', message, quoted: false)); |
5602 | } else { |
5603 | properties.add(_flutterError.toDiagnosticsNode(style: DiagnosticsTreeStyle.whitespace)); |
5604 | } |
5605 | } |
5606 | } |
5607 | |
5608 | /// Signature for a function that creates a widget, e.g. [StatelessWidget.build] |
5609 | /// or [State.build]. |
5610 | /// |
5611 | /// Used by [Builder.builder], [OverlayEntry.builder], etc. |
5612 | /// |
5613 | /// See also: |
5614 | /// |
5615 | /// * [IndexedWidgetBuilder], which is similar but also takes an index. |
5616 | /// * [TransitionBuilder], which is similar but also takes a child. |
5617 | /// * [ValueWidgetBuilder], which is similar but takes a value and a child. |
5618 | typedef WidgetBuilder = Widget Function(BuildContext context); |
5619 | |
5620 | /// Signature for a function that creates a widget for a given index, e.g., in a |
5621 | /// list. |
5622 | /// |
5623 | /// Used by [ListView.builder] and other APIs that use lazily-generated widgets. |
5624 | /// |
5625 | /// See also: |
5626 | /// |
5627 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext]. |
5628 | /// * [TransitionBuilder], which is similar but also takes a child. |
5629 | /// * [NullableIndexedWidgetBuilder], which is similar but may return null. |
5630 | typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index); |
5631 | |
5632 | /// Signature for a function that creates a widget for a given index, e.g., in a |
5633 | /// list, but may return null. |
5634 | /// |
5635 | /// Used by [SliverChildBuilderDelegate.builder] and other APIs that |
5636 | /// use lazily-generated widgets where the child count is not known |
5637 | /// ahead of time. |
5638 | /// |
5639 | /// Unlike most builders, this callback can return null, indicating the index |
5640 | /// is out of range. Whether and when this is valid depends on the semantics |
5641 | /// of the builder. For example, [SliverChildBuilderDelegate.builder] returns |
5642 | /// null when the index is out of range, where the range is defined by the |
5643 | /// [SliverChildBuilderDelegate.childCount]; so in that case the `index` |
5644 | /// parameter's value may determine whether returning null is valid or not. |
5645 | /// |
5646 | /// See also: |
5647 | /// |
5648 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext]. |
5649 | /// * [TransitionBuilder], which is similar but also takes a child. |
5650 | /// * [IndexedWidgetBuilder], which is similar but not nullable. |
5651 | typedef NullableIndexedWidgetBuilder = Widget? Function(BuildContext context, int index); |
5652 | |
5653 | /// A builder that builds a widget given a child. |
5654 | /// |
5655 | /// The child should typically be part of the returned widget tree. |
5656 | /// |
5657 | /// Used by [AnimatedBuilder.builder], [ListenableBuilder.builder], |
5658 | /// [WidgetsApp.builder], and [MaterialApp.builder]. |
5659 | /// |
5660 | /// See also: |
5661 | /// |
5662 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext]. |
5663 | /// * [IndexedWidgetBuilder], which is similar but also takes an index. |
5664 | /// * [ValueWidgetBuilder], which is similar but takes a value and a child. |
5665 | typedef TransitionBuilder = Widget Function(BuildContext context, Widget? child); |
5666 | |
5667 | /// An [Element] that composes other [Element]s. |
5668 | /// |
5669 | /// Rather than creating a [RenderObject] directly, a [ComponentElement] creates |
5670 | /// [RenderObject]s indirectly by creating other [Element]s. |
5671 | /// |
5672 | /// Contrast with [RenderObjectElement]. |
5673 | abstract class ComponentElement extends Element { |
5674 | /// Creates an element that uses the given widget as its configuration. |
5675 | ComponentElement(super.widget); |
5676 | |
5677 | Element? _child; |
5678 | |
5679 | bool _debugDoingBuild = false; |
5680 | @override |
5681 | bool get debugDoingBuild => _debugDoingBuild; |
5682 | |
5683 | @override |
5684 | Element? get renderObjectAttachingChild => _child; |
5685 | |
5686 | @override |
5687 | void mount(Element? parent, Object? newSlot) { |
5688 | super.mount(parent, newSlot); |
5689 | assert(_child == null); |
5690 | assert(_lifecycleState == _ElementLifecycle.active); |
5691 | _firstBuild(); |
5692 | assert(_child != null); |
5693 | } |
5694 | |
5695 | void _firstBuild() { |
5696 | // StatefulElement overrides this to also call state.didChangeDependencies. |
5697 | rebuild(); // This eventually calls performRebuild. |
5698 | } |
5699 | |
5700 | /// Calls the [StatelessWidget.build] method of the [StatelessWidget] object |
5701 | /// (for stateless widgets) or the [State.build] method of the [State] object |
5702 | /// (for stateful widgets) and then updates the widget tree. |
5703 | /// |
5704 | /// Called automatically during [mount] to generate the first build, and by |
5705 | /// [rebuild] when the element needs updating. |
5706 | @override |
5707 | @pragma('vm:notify-debugger-on-exception') |
5708 | void performRebuild() { |
5709 | Widget? built; |
5710 | try { |
5711 | assert(() { |
5712 | _debugDoingBuild = true; |
5713 | return true; |
5714 | }()); |
5715 | built = build(); |
5716 | assert(() { |
5717 | _debugDoingBuild = false; |
5718 | return true; |
5719 | }()); |
5720 | debugWidgetBuilderValue(widget, built); |
5721 | } catch (e, stack) { |
5722 | _debugDoingBuild = false; |
5723 | built = ErrorWidget.builder( |
5724 | _reportException( |
5725 | ErrorDescription('building$this'), |
5726 | e, |
5727 | stack, |
5728 | informationCollector: |
5729 | () => <DiagnosticsNode>[if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this))], |
5730 | ), |
5731 | ); |
5732 | } finally { |
5733 | // We delay marking the element as clean until after calling build() so |
5734 | // that attempts to markNeedsBuild() during build() will be ignored. |
5735 | super.performRebuild(); // clears the "dirty" flag |
5736 | } |
5737 | try { |
5738 | _child = updateChild(_child, built, slot); |
5739 | assert(_child != null); |
5740 | } catch (e, stack) { |
5741 | built = ErrorWidget.builder( |
5742 | _reportException( |
5743 | ErrorDescription('building$this'), |
5744 | e, |
5745 | stack, |
5746 | informationCollector: |
5747 | () => <DiagnosticsNode>[if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this))], |
5748 | ), |
5749 | ); |
5750 | _child = updateChild(null, built, slot); |
5751 | } |
5752 | } |
5753 | |
5754 | /// Subclasses should override this function to actually call the appropriate |
5755 | /// `build` function (e.g., [StatelessWidget.build] or [State.build]) for |
5756 | /// their widget. |
5757 | @protected |
5758 | Widget build(); |
5759 | |
5760 | @override |
5761 | void visitChildren(ElementVisitor visitor) { |
5762 | if (_child != null) { |
5763 | visitor(_child!); |
5764 | } |
5765 | } |
5766 | |
5767 | @override |
5768 | void forgetChild(Element child) { |
5769 | assert(child == _child); |
5770 | _child = null; |
5771 | super.forgetChild(child); |
5772 | } |
5773 | } |
5774 | |
5775 | /// An [Element] that uses a [StatelessWidget] as its configuration. |
5776 | class StatelessElement extends ComponentElement { |
5777 | /// Creates an element that uses the given widget as its configuration. |
5778 | StatelessElement(StatelessWidget super.widget); |
5779 | |
5780 | @override |
5781 | Widget build() => (widget as StatelessWidget).build(this); |
5782 | |
5783 | @override |
5784 | void update(StatelessWidget newWidget) { |
5785 | super.update(newWidget); |
5786 | assert(widget == newWidget); |
5787 | rebuild(force: true); |
5788 | } |
5789 | } |
5790 | |
5791 | /// An [Element] that uses a [StatefulWidget] as its configuration. |
5792 | class StatefulElement extends ComponentElement { |
5793 | /// Creates an element that uses the given widget as its configuration. |
5794 | StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) { |
5795 | assert(() { |
5796 | if (!state._debugTypesAreRight(widget)) { |
5797 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5798 | ErrorSummary( |
5799 | 'StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>', |
5800 | ), |
5801 | ErrorDescription( |
5802 | 'The createState function for${widget.runtimeType}returned a state ' |
5803 | 'of type${state.runtimeType}, which is not a subtype of ' |
5804 | 'State<${widget.runtimeType}>, violating the contract for createState.', |
5805 | ), |
5806 | ]); |
5807 | } |
5808 | return true; |
5809 | }()); |
5810 | assert(state._element == null); |
5811 | state._element = this; |
5812 | assert( |
5813 | state._widget == null, |
5814 | 'The createState function for$widgetreturned an old or invalid state ' |
5815 | 'instance:${state._widget}, which is not null, violating the contract ' |
5816 | 'for createState.', |
5817 | ); |
5818 | state._widget = widget; |
5819 | assert(state._debugLifecycleState == _StateLifecycle.created); |
5820 | } |
5821 | |
5822 | @override |
5823 | Widget build() => state.build(this); |
5824 | |
5825 | /// The [State] instance associated with this location in the tree. |
5826 | /// |
5827 | /// There is a one-to-one relationship between [State] objects and the |
5828 | /// [StatefulElement] objects that hold them. The [State] objects are created |
5829 | /// by [StatefulElement] in [mount]. |
5830 | State<StatefulWidget> get state => _state!; |
5831 | State<StatefulWidget>? _state; |
5832 | |
5833 | @override |
5834 | void reassemble() { |
5835 | state.reassemble(); |
5836 | super.reassemble(); |
5837 | } |
5838 | |
5839 | @override |
5840 | void _firstBuild() { |
5841 | assert(state._debugLifecycleState == _StateLifecycle.created); |
5842 | final Object? debugCheckForReturnedFuture = state.initState() as dynamic; |
5843 | assert(() { |
5844 | if (debugCheckForReturnedFuture is Future) { |
5845 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5846 | ErrorSummary('${state.runtimeType}.initState() returned a Future.'), |
5847 | ErrorDescription('State.initState() must be a void method without an `async` keyword.'), |
5848 | ErrorHint( |
5849 | 'Rather than awaiting on asynchronous work directly inside of initState, ' |
5850 | 'call a separate method to do this work without awaiting it.', |
5851 | ), |
5852 | ]); |
5853 | } |
5854 | return true; |
5855 | }()); |
5856 | assert(() { |
5857 | state._debugLifecycleState = _StateLifecycle.initialized; |
5858 | return true; |
5859 | }()); |
5860 | state.didChangeDependencies(); |
5861 | assert(() { |
5862 | state._debugLifecycleState = _StateLifecycle.ready; |
5863 | return true; |
5864 | }()); |
5865 | super._firstBuild(); |
5866 | } |
5867 | |
5868 | @override |
5869 | void performRebuild() { |
5870 | if (_didChangeDependencies) { |
5871 | state.didChangeDependencies(); |
5872 | _didChangeDependencies = false; |
5873 | } |
5874 | super.performRebuild(); |
5875 | } |
5876 | |
5877 | @override |
5878 | void update(StatefulWidget newWidget) { |
5879 | super.update(newWidget); |
5880 | assert(widget == newWidget); |
5881 | final StatefulWidget oldWidget = state._widget!; |
5882 | state._widget = widget as StatefulWidget; |
5883 | final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; |
5884 | assert(() { |
5885 | if (debugCheckForReturnedFuture is Future) { |
5886 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5887 | ErrorSummary('${state.runtimeType}.didUpdateWidget() returned a Future.'), |
5888 | ErrorDescription( |
5889 | 'State.didUpdateWidget() must be a void method without an `async` keyword.', |
5890 | ), |
5891 | ErrorHint( |
5892 | 'Rather than awaiting on asynchronous work directly inside of didUpdateWidget, ' |
5893 | 'call a separate method to do this work without awaiting it.', |
5894 | ), |
5895 | ]); |
5896 | } |
5897 | return true; |
5898 | }()); |
5899 | rebuild(force: true); |
5900 | } |
5901 | |
5902 | @override |
5903 | void activate() { |
5904 | super.activate(); |
5905 | state.activate(); |
5906 | // Since the State could have observed the deactivate() and thus disposed of |
5907 | // resources allocated in the build method, we have to rebuild the widget |
5908 | // so that its State can reallocate its resources. |
5909 | assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op |
5910 | markNeedsBuild(); |
5911 | } |
5912 | |
5913 | @override |
5914 | void deactivate() { |
5915 | state.deactivate(); |
5916 | super.deactivate(); |
5917 | } |
5918 | |
5919 | @override |
5920 | void unmount() { |
5921 | super.unmount(); |
5922 | state.dispose(); |
5923 | assert(() { |
5924 | if (state._debugLifecycleState == _StateLifecycle.defunct) { |
5925 | return true; |
5926 | } |
5927 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5928 | ErrorSummary('${state.runtimeType}.dispose failed to call super.dispose.'), |
5929 | ErrorDescription( |
5930 | 'dispose() implementations must always call their superclass dispose() method, to ensure ' |
5931 | 'that all the resources used by the widget are fully released.', |
5932 | ), |
5933 | ]); |
5934 | }()); |
5935 | state._element = null; |
5936 | // Release resources to reduce the severity of memory leaks caused by |
5937 | // defunct, but accidentally retained Elements. |
5938 | _state = null; |
5939 | } |
5940 | |
5941 | @override |
5942 | InheritedWidget dependOnInheritedElement(Element ancestor, {Object? aspect}) { |
5943 | assert(() { |
5944 | final Type targetType = ancestor.widget.runtimeType; |
5945 | if (state._debugLifecycleState == _StateLifecycle.created) { |
5946 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5947 | ErrorSummary( |
5948 | 'dependOnInheritedWidgetOfExactType<$targetType>() or dependOnInheritedElement() was called before${state.runtimeType}.initState() completed.', |
5949 | ), |
5950 | ErrorDescription( |
5951 | 'When an inherited widget changes, for example if the value of Theme.of() changes, ' |
5952 | "its dependent widgets are rebuilt. If the dependent widget's reference to " |
5953 | 'the inherited widget is in a constructor or an initState() method, ' |
5954 | 'then the rebuilt dependent widget will not reflect the changes in the ' |
5955 | 'inherited widget.', |
5956 | ), |
5957 | ErrorHint( |
5958 | 'Typically references to inherited widgets should occur in widget build() methods. Alternatively, ' |
5959 | 'initialization based on inherited widgets can be placed in the didChangeDependencies method, which ' |
5960 | 'is called after initState and whenever the dependencies change thereafter.', |
5961 | ), |
5962 | ]); |
5963 | } |
5964 | if (state._debugLifecycleState == _StateLifecycle.defunct) { |
5965 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
5966 | ErrorSummary( |
5967 | 'dependOnInheritedWidgetOfExactType<$targetType>() or dependOnInheritedElement() was called after dispose():$this', |
5968 | ), |
5969 | ErrorDescription( |
5970 | 'This error happens if you call dependOnInheritedWidgetOfExactType() on the ' |
5971 | 'BuildContext for a widget that no longer appears in the widget tree ' |
5972 | '(e.g., whose parent widget no longer includes the widget in its ' |
5973 | 'build). This error can occur when code calls ' |
5974 | 'dependOnInheritedWidgetOfExactType() from a timer or an animation callback.', |
5975 | ), |
5976 | ErrorHint( |
5977 | 'The preferred solution is to cancel the timer or stop listening to the ' |
5978 | 'animation in the dispose() callback. Another solution is to check the ' |
5979 | '"mounted" property of this object before calling ' |
5980 | 'dependOnInheritedWidgetOfExactType() to ensure the object is still in the ' |
5981 | 'tree.', |
5982 | ), |
5983 | ErrorHint( |
5984 | 'This error might indicate a memory leak if ' |
5985 | 'dependOnInheritedWidgetOfExactType() is being called because another object ' |
5986 | 'is retaining a reference to this State object after it has been ' |
5987 | 'removed from the tree. To avoid memory leaks, consider breaking the ' |
5988 | 'reference to this object during dispose().', |
5989 | ), |
5990 | ]); |
5991 | } |
5992 | return true; |
5993 | }()); |
5994 | return super.dependOnInheritedElement(ancestor as InheritedElement, aspect: aspect); |
5995 | } |
5996 | |
5997 | /// This controls whether we should call [State.didChangeDependencies] from |
5998 | /// the start of [build], to avoid calls when the [State] will not get built. |
5999 | /// This can happen when the widget has dropped out of the tree, but depends |
6000 | /// on an [InheritedWidget] that is still in the tree. |
6001 | /// |
6002 | /// It is set initially to false, since [_firstBuild] makes the initial call |
6003 | /// on the [state]. When it is true, [build] will call |
6004 | /// `state.didChangeDependencies` and then sets it to false. Subsequent calls |
6005 | /// to [didChangeDependencies] set it to true. |
6006 | bool _didChangeDependencies = false; |
6007 | |
6008 | @override |
6009 | void didChangeDependencies() { |
6010 | super.didChangeDependencies(); |
6011 | _didChangeDependencies = true; |
6012 | } |
6013 | |
6014 | @override |
6015 | DiagnosticsNode toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) { |
6016 | return _ElementDiagnosticableTreeNode(name: name, value: this, style: style, stateful: true); |
6017 | } |
6018 | |
6019 | @override |
6020 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6021 | super.debugFillProperties(properties); |
6022 | properties.add(DiagnosticsProperty<State<StatefulWidget>>('state', _state, defaultValue: null)); |
6023 | } |
6024 | } |
6025 | |
6026 | /// An [Element] that uses a [ProxyWidget] as its configuration. |
6027 | abstract class ProxyElement extends ComponentElement { |
6028 | /// Initializes fields for subclasses. |
6029 | ProxyElement(ProxyWidget super.widget); |
6030 | |
6031 | @override |
6032 | Widget build() => (widget as ProxyWidget).child; |
6033 | |
6034 | @override |
6035 | void update(ProxyWidget newWidget) { |
6036 | final ProxyWidget oldWidget = widget as ProxyWidget; |
6037 | assert(widget != newWidget); |
6038 | super.update(newWidget); |
6039 | assert(widget == newWidget); |
6040 | updated(oldWidget); |
6041 | rebuild(force: true); |
6042 | } |
6043 | |
6044 | /// Called during build when the [widget] has changed. |
6045 | /// |
6046 | /// By default, calls [notifyClients]. Subclasses may override this method to |
6047 | /// avoid calling [notifyClients] unnecessarily (e.g. if the old and new |
6048 | /// widgets are equivalent). |
6049 | @protected |
6050 | void updated(covariant ProxyWidget oldWidget) { |
6051 | notifyClients(oldWidget); |
6052 | } |
6053 | |
6054 | /// Notify other objects that the widget associated with this element has |
6055 | /// changed. |
6056 | /// |
6057 | /// Called during [update] (via [updated]) after changing the widget |
6058 | /// associated with this element but before rebuilding this element. |
6059 | @protected |
6060 | void notifyClients(covariant ProxyWidget oldWidget); |
6061 | } |
6062 | |
6063 | /// An [Element] that uses a [ParentDataWidget] as its configuration. |
6064 | class ParentDataElement<T extends ParentData> extends ProxyElement { |
6065 | /// Creates an element that uses the given widget as its configuration. |
6066 | ParentDataElement(ParentDataWidget<T> super.widget); |
6067 | |
6068 | /// Returns the [Type] of [ParentData] that this element has been configured |
6069 | /// for. |
6070 | /// |
6071 | /// This is only available in debug mode. It will throw in profile and |
6072 | /// release modes. |
6073 | Type get debugParentDataType { |
6074 | Type? type; |
6075 | assert(() { |
6076 | type = T; |
6077 | return true; |
6078 | }()); |
6079 | if (type != null) { |
6080 | return type!; |
6081 | } |
6082 | throw UnsupportedError('debugParentDataType is only supported in debug builds'); |
6083 | } |
6084 | |
6085 | void _applyParentData(ParentDataWidget<T> widget) { |
6086 | void applyParentDataToChild(Element child) { |
6087 | if (child is RenderObjectElement) { |
6088 | child._updateParentData(widget); |
6089 | } else if (child.renderObjectAttachingChild != null) { |
6090 | applyParentDataToChild(child.renderObjectAttachingChild!); |
6091 | } |
6092 | } |
6093 | |
6094 | if (renderObjectAttachingChild != null) { |
6095 | applyParentDataToChild(renderObjectAttachingChild!); |
6096 | } |
6097 | } |
6098 | |
6099 | /// Calls [ParentDataWidget.applyParentData] on the given widget, passing it |
6100 | /// the [RenderObject] whose parent data this element is ultimately |
6101 | /// responsible for. |
6102 | /// |
6103 | /// This allows a render object's [RenderObject.parentData] to be modified |
6104 | /// without triggering a build. This is generally ill-advised, but makes sense |
6105 | /// in situations such as the following: |
6106 | /// |
6107 | /// * Build and layout are currently under way, but the [ParentData] in question |
6108 | /// does not affect layout, and the value to be applied could not be |
6109 | /// determined before build and layout (e.g. it depends on the layout of a |
6110 | /// descendant). |
6111 | /// |
6112 | /// * Paint is currently under way, but the [ParentData] in question does not |
6113 | /// affect layout or paint, and the value to be applied could not be |
6114 | /// determined before paint (e.g. it depends on the compositing phase). |
6115 | /// |
6116 | /// In either case, the next build is expected to cause this element to be |
6117 | /// configured with the given new widget (or a widget with equivalent data). |
6118 | /// |
6119 | /// Only [ParentDataWidget]s that return true for |
6120 | /// [ParentDataWidget.debugCanApplyOutOfTurn] can be applied this way. |
6121 | /// |
6122 | /// The new widget must have the same child as the current widget. |
6123 | /// |
6124 | /// An example of when this is used is the [AutomaticKeepAlive] widget. If it |
6125 | /// receives a notification during the build of one of its descendants saying |
6126 | /// that its child must be kept alive, it will apply a [KeepAlive] widget out |
6127 | /// of turn. This is safe, because by definition the child is already alive, |
6128 | /// and therefore this will not change the behavior of the parent this frame. |
6129 | /// It is more efficient than requesting an additional frame just for the |
6130 | /// purpose of updating the [KeepAlive] widget. |
6131 | void applyWidgetOutOfTurn(ParentDataWidget<T> newWidget) { |
6132 | assert(newWidget.debugCanApplyOutOfTurn()); |
6133 | assert(newWidget.child == (widget as ParentDataWidget<T>).child); |
6134 | _applyParentData(newWidget); |
6135 | } |
6136 | |
6137 | @override |
6138 | void notifyClients(ParentDataWidget<T> oldWidget) { |
6139 | _applyParentData(widget as ParentDataWidget<T>); |
6140 | } |
6141 | } |
6142 | |
6143 | /// An [Element] that uses an [InheritedWidget] as its configuration. |
6144 | class InheritedElement extends ProxyElement { |
6145 | /// Creates an element that uses the given widget as its configuration. |
6146 | InheritedElement(InheritedWidget super.widget); |
6147 | |
6148 | final Map<Element, Object?> _dependents = HashMap<Element, Object?>(); |
6149 | |
6150 | @override |
6151 | void _updateInheritance() { |
6152 | assert(_lifecycleState == _ElementLifecycle.active); |
6153 | final PersistentHashMap<Type, InheritedElement> incomingWidgets = |
6154 | _parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty(); |
6155 | _inheritedElements = incomingWidgets.put(widget.runtimeType, this); |
6156 | } |
6157 | |
6158 | @override |
6159 | void debugDeactivated() { |
6160 | assert(() { |
6161 | assert(_dependents.isEmpty); |
6162 | return true; |
6163 | }()); |
6164 | super.debugDeactivated(); |
6165 | } |
6166 | |
6167 | /// Returns the dependencies value recorded for [dependent] |
6168 | /// with [setDependencies]. |
6169 | /// |
6170 | /// Each dependent element is mapped to a single object value |
6171 | /// which represents how the element depends on this |
6172 | /// [InheritedElement]. This value is null by default and by default |
6173 | /// dependent elements are rebuilt unconditionally. |
6174 | /// |
6175 | /// Subclasses can manage these values with [updateDependencies] |
6176 | /// so that they can selectively rebuild dependents in |
6177 | /// [notifyDependent]. |
6178 | /// |
6179 | /// This method is typically only called in overrides of [updateDependencies]. |
6180 | /// |
6181 | /// See also: |
6182 | /// |
6183 | /// * [updateDependencies], which is called each time a dependency is |
6184 | /// created with [dependOnInheritedWidgetOfExactType]. |
6185 | /// * [setDependencies], which sets dependencies value for a dependent |
6186 | /// element. |
6187 | /// * [notifyDependent], which can be overridden to use a dependent's |
6188 | /// dependencies value to decide if the dependent needs to be rebuilt. |
6189 | /// * [InheritedModel], which is an example of a class that uses this method |
6190 | /// to manage dependency values. |
6191 | @protected |
6192 | Object? getDependencies(Element dependent) { |
6193 | return _dependents[dependent]; |
6194 | } |
6195 | |
6196 | /// Sets the value returned by [getDependencies] value for [dependent]. |
6197 | /// |
6198 | /// Each dependent element is mapped to a single object value |
6199 | /// which represents how the element depends on this |
6200 | /// [InheritedElement]. The [updateDependencies] method sets this value to |
6201 | /// null by default so that dependent elements are rebuilt unconditionally. |
6202 | /// |
6203 | /// Subclasses can manage these values with [updateDependencies] |
6204 | /// so that they can selectively rebuild dependents in [notifyDependent]. |
6205 | /// |
6206 | /// This method is typically only called in overrides of [updateDependencies]. |
6207 | /// |
6208 | /// See also: |
6209 | /// |
6210 | /// * [updateDependencies], which is called each time a dependency is |
6211 | /// created with [dependOnInheritedWidgetOfExactType]. |
6212 | /// * [getDependencies], which returns the current value for a dependent |
6213 | /// element. |
6214 | /// * [notifyDependent], which can be overridden to use a dependent's |
6215 | /// [getDependencies] value to decide if the dependent needs to be rebuilt. |
6216 | /// * [InheritedModel], which is an example of a class that uses this method |
6217 | /// to manage dependency values. |
6218 | @protected |
6219 | void setDependencies(Element dependent, Object? value) { |
6220 | _dependents[dependent] = value; |
6221 | } |
6222 | |
6223 | /// Called by [dependOnInheritedWidgetOfExactType] when a new [dependent] is added. |
6224 | /// |
6225 | /// Each dependent element can be mapped to a single object value with |
6226 | /// [setDependencies]. This method can lookup the existing dependencies with |
6227 | /// [getDependencies]. |
6228 | /// |
6229 | /// By default this method sets the inherited dependencies for [dependent] |
6230 | /// to null. This only serves to record an unconditional dependency on |
6231 | /// [dependent]. |
6232 | /// |
6233 | /// Subclasses can manage their own dependencies values so that they |
6234 | /// can selectively rebuild dependents in [notifyDependent]. |
6235 | /// |
6236 | /// See also: |
6237 | /// |
6238 | /// * [getDependencies], which returns the current value for a dependent |
6239 | /// element. |
6240 | /// * [setDependencies], which sets the value for a dependent element. |
6241 | /// * [notifyDependent], which can be overridden to use a dependent's |
6242 | /// dependencies value to decide if the dependent needs to be rebuilt. |
6243 | /// * [InheritedModel], which is an example of a class that uses this method |
6244 | /// to manage dependency values. |
6245 | @protected |
6246 | void updateDependencies(Element dependent, Object? aspect) { |
6247 | setDependencies(dependent, null); |
6248 | } |
6249 | |
6250 | /// Called by [notifyClients] for each dependent. |
6251 | /// |
6252 | /// Calls `dependent.didChangeDependencies()` by default. |
6253 | /// |
6254 | /// Subclasses can override this method to selectively call |
6255 | /// [didChangeDependencies] based on the value of [getDependencies]. |
6256 | /// |
6257 | /// See also: |
6258 | /// |
6259 | /// * [updateDependencies], which is called each time a dependency is |
6260 | /// created with [dependOnInheritedWidgetOfExactType]. |
6261 | /// * [getDependencies], which returns the current value for a dependent |
6262 | /// element. |
6263 | /// * [setDependencies], which sets the value for a dependent element. |
6264 | /// * [InheritedModel], which is an example of a class that uses this method |
6265 | /// to manage dependency values. |
6266 | @protected |
6267 | void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) { |
6268 | dependent.didChangeDependencies(); |
6269 | } |
6270 | |
6271 | /// Called by [Element.deactivate] to remove the provided `dependent` [Element] from this [InheritedElement]. |
6272 | /// |
6273 | /// After the dependent is removed, [Element.didChangeDependencies] will no |
6274 | /// longer be called on it when this [InheritedElement] notifies its dependents. |
6275 | /// |
6276 | /// Subclasses can override this method to release any resources retained for |
6277 | /// a given [dependent]. |
6278 | @protected |
6279 | @mustCallSuper |
6280 | void removeDependent(Element dependent) { |
6281 | _dependents.remove(dependent); |
6282 | } |
6283 | |
6284 | /// Calls [Element.didChangeDependencies] of all dependent elements, if |
6285 | /// [InheritedWidget.updateShouldNotify] returns true. |
6286 | /// |
6287 | /// Called by [update], immediately prior to [build]. |
6288 | /// |
6289 | /// Calls [notifyClients] to actually trigger the notifications. |
6290 | @override |
6291 | void updated(InheritedWidget oldWidget) { |
6292 | if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) { |
6293 | super.updated(oldWidget); |
6294 | } |
6295 | } |
6296 | |
6297 | /// Notifies all dependent elements that this inherited widget has changed, by |
6298 | /// calling [Element.didChangeDependencies]. |
6299 | /// |
6300 | /// This method must only be called during the build phase. Usually this |
6301 | /// method is called automatically when an inherited widget is rebuilt, e.g. |
6302 | /// as a result of calling [State.setState] above the inherited widget. |
6303 | /// |
6304 | /// See also: |
6305 | /// |
6306 | /// * [InheritedNotifier], a subclass of [InheritedWidget] that also calls |
6307 | /// this method when its [Listenable] sends a notification. |
6308 | @override |
6309 | void notifyClients(InheritedWidget oldWidget) { |
6310 | assert(_debugCheckOwnerBuildTargetExists('notifyClients')); |
6311 | for (final Element dependent in _dependents.keys) { |
6312 | assert(() { |
6313 | // check that it really is our descendant |
6314 | Element? ancestor = dependent._parent; |
6315 | while (ancestor != this && ancestor != null) { |
6316 | ancestor = ancestor._parent; |
6317 | } |
6318 | return ancestor == this; |
6319 | }()); |
6320 | // check that it really depends on us |
6321 | assert(dependent._dependencies!.contains(this)); |
6322 | notifyDependent(oldWidget, dependent); |
6323 | } |
6324 | } |
6325 | } |
6326 | |
6327 | /// An [Element] that uses a [RenderObjectWidget] as its configuration. |
6328 | /// |
6329 | /// [RenderObjectElement] objects have an associated [RenderObject] widget in |
6330 | /// the render tree, which handles concrete operations like laying out, |
6331 | /// painting, and hit testing. |
6332 | /// |
6333 | /// Contrast with [ComponentElement]. |
6334 | /// |
6335 | /// For details on the lifecycle of an element, see the discussion at [Element]. |
6336 | /// |
6337 | /// ## Writing a RenderObjectElement subclass |
6338 | /// |
6339 | /// There are three common child models used by most [RenderObject]s: |
6340 | /// |
6341 | /// * Leaf render objects, with no children: The [LeafRenderObjectElement] class |
6342 | /// handles this case. |
6343 | /// |
6344 | /// * A single child: The [SingleChildRenderObjectElement] class handles this |
6345 | /// case. |
6346 | /// |
6347 | /// * A linked list of children: The [MultiChildRenderObjectElement] class |
6348 | /// handles this case. |
6349 | /// |
6350 | /// Sometimes, however, a render object's child model is more complicated. Maybe |
6351 | /// it has a two-dimensional array of children. Maybe it constructs children on |
6352 | /// demand. Maybe it features multiple lists. In such situations, the |
6353 | /// corresponding [Element] for the [Widget] that configures that [RenderObject] |
6354 | /// will be a new subclass of [RenderObjectElement]. |
6355 | /// |
6356 | /// Such a subclass is responsible for managing children, specifically the |
6357 | /// [Element] children of this object, and the [RenderObject] children of its |
6358 | /// corresponding [RenderObject]. |
6359 | /// |
6360 | /// ### Specializing the getters |
6361 | /// |
6362 | /// [RenderObjectElement] objects spend much of their time acting as |
6363 | /// intermediaries between their [widget] and their [renderObject]. It is |
6364 | /// generally recommended against specializing the [widget] getter and |
6365 | /// instead casting at the various call sites to avoid adding overhead |
6366 | /// outside of this particular implementation. |
6367 | /// |
6368 | /// ```dart |
6369 | /// class FooElement extends RenderObjectElement { |
6370 | /// FooElement(super.widget); |
6371 | /// |
6372 | /// // Specializing the renderObject getter is fine because |
6373 | /// // it is not performance sensitive. |
6374 | /// @override |
6375 | /// RenderFoo get renderObject => super.renderObject as RenderFoo; |
6376 | /// |
6377 | /// void _foo() { |
6378 | /// // For the widget getter, though, we prefer to cast locally |
6379 | /// // since that results in better overall performance where the |
6380 | /// // casting isn't needed: |
6381 | /// final Foo foo = widget as Foo; |
6382 | /// // ... |
6383 | /// } |
6384 | /// |
6385 | /// // ... |
6386 | /// } |
6387 | /// ``` |
6388 | /// |
6389 | /// ### Slots |
6390 | /// |
6391 | /// Each child [Element] corresponds to a [RenderObject] which should be |
6392 | /// attached to this element's render object as a child. |
6393 | /// |
6394 | /// However, the immediate children of the element may not be the ones that |
6395 | /// eventually produce the actual [RenderObject] that they correspond to. For |
6396 | /// example, a [StatelessElement] (the element of a [StatelessWidget]) |
6397 | /// corresponds to whatever [RenderObject] its child (the element returned by |
6398 | /// its [StatelessWidget.build] method) corresponds to. |
6399 | /// |
6400 | /// Each child is therefore assigned a _[slot]_ token. This is an identifier whose |
6401 | /// meaning is private to this [RenderObjectElement] node. When the descendant |
6402 | /// that finally produces the [RenderObject] is ready to attach it to this |
6403 | /// node's render object, it passes that slot token back to this node, and that |
6404 | /// allows this node to cheaply identify where to put the child render object |
6405 | /// relative to the others in the parent render object. |
6406 | /// |
6407 | /// A child's [slot] is determined when the parent calls [updateChild] to |
6408 | /// inflate the child (see the next section). It can be updated by calling |
6409 | /// [updateSlotForChild]. |
6410 | /// |
6411 | /// ### Updating children |
6412 | /// |
6413 | /// Early in the lifecycle of an element, the framework calls the [mount] |
6414 | /// method. This method should call [updateChild] for each child, passing in |
6415 | /// the widget for that child, and the slot for that child, thus obtaining a |
6416 | /// list of child [Element]s. |
6417 | /// |
6418 | /// Subsequently, the framework will call the [update] method. In this method, |
6419 | /// the [RenderObjectElement] should call [updateChild] for each child, passing |
6420 | /// in the [Element] that was obtained during [mount] or the last time [update] |
6421 | /// was run (whichever happened most recently), the new [Widget], and the slot. |
6422 | /// This provides the object with a new list of [Element] objects. |
6423 | /// |
6424 | /// Where possible, the [update] method should attempt to map the elements from |
6425 | /// the last pass to the widgets in the new pass. For example, if one of the |
6426 | /// elements from the last pass was configured with a particular [Key], and one |
6427 | /// of the widgets in this new pass has that same key, they should be paired up, |
6428 | /// and the old element should be updated with the widget (and the slot |
6429 | /// corresponding to the new widget's new position, also). The [updateChildren] |
6430 | /// method may be useful in this regard. |
6431 | /// |
6432 | /// [updateChild] should be called for children in their logical order. The |
6433 | /// order can matter; for example, if two of the children use [PageStorage]'s |
6434 | /// `writeState` feature in their build method (and neither has a [Widget.key]), |
6435 | /// then the state written by the first will be overwritten by the second. |
6436 | /// |
6437 | /// #### Dynamically determining the children during the build phase |
6438 | /// |
6439 | /// The child widgets need not necessarily come from this element's widget |
6440 | /// verbatim. They could be generated dynamically from a callback, or generated |
6441 | /// in other more creative ways. |
6442 | /// |
6443 | /// #### Dynamically determining the children during layout |
6444 | /// |
6445 | /// If the widgets are to be generated at layout time, then generating them in |
6446 | /// the [mount] and [update] methods won't work: layout of this element's render |
6447 | /// object hasn't started yet at that point. Instead, the [update] method can |
6448 | /// mark the render object as needing layout (see |
6449 | /// [RenderObject.markNeedsLayout]), and then the render object's |
6450 | /// [RenderObject.performLayout] method can call back to the element to have it |
6451 | /// generate the widgets and call [updateChild] accordingly. |
6452 | /// |
6453 | /// For a render object to call an element during layout, it must use |
6454 | /// [RenderObject.invokeLayoutCallback]. For an element to call [updateChild] |
6455 | /// outside of its [update] method, it must use [BuildOwner.buildScope]. |
6456 | /// |
6457 | /// The framework provides many more checks in normal operation than it does |
6458 | /// when doing a build during layout. For this reason, creating widgets with |
6459 | /// layout-time build semantics should be done with great care. |
6460 | /// |
6461 | /// #### Handling errors when building |
6462 | /// |
6463 | /// If an element calls a builder function to obtain widgets for its children, |
6464 | /// it may find that the build throws an exception. Such exceptions should be |
6465 | /// caught and reported using [FlutterError.reportError]. If a child is needed |
6466 | /// but a builder has failed in this way, an instance of [ErrorWidget] can be |
6467 | /// used instead. |
6468 | /// |
6469 | /// ### Detaching children |
6470 | /// |
6471 | /// It is possible, when using [GlobalKey]s, for a child to be proactively |
6472 | /// removed by another element before this element has been updated. |
6473 | /// (Specifically, this happens when the subtree rooted at a widget with a |
6474 | /// particular [GlobalKey] is being moved from this element to an element |
6475 | /// processed earlier in the build phase.) When this happens, this element's |
6476 | /// [forgetChild] method will be called with a reference to the affected child |
6477 | /// element. |
6478 | /// |
6479 | /// The [forgetChild] method of a [RenderObjectElement] subclass must remove the |
6480 | /// child element from its child list, so that when it next [update]s its |
6481 | /// children, the removed child is not considered. |
6482 | /// |
6483 | /// For performance reasons, if there are many elements, it may be quicker to |
6484 | /// track which elements were forgotten by storing them in a [Set], rather than |
6485 | /// proactively mutating the local record of the child list and the identities |
6486 | /// of all the slots. For example, see the implementation of |
6487 | /// [MultiChildRenderObjectElement]. |
6488 | /// |
6489 | /// ### Maintaining the render object tree |
6490 | /// |
6491 | /// Once a descendant produces a render object, it will call |
6492 | /// [insertRenderObjectChild]. If the descendant's slot changes identity, it |
6493 | /// will call [moveRenderObjectChild]. If a descendant goes away, it will call |
6494 | /// [removeRenderObjectChild]. |
6495 | /// |
6496 | /// These three methods should update the render tree accordingly, attaching, |
6497 | /// moving, and detaching the given child render object from this element's own |
6498 | /// render object respectively. |
6499 | /// |
6500 | /// ### Walking the children |
6501 | /// |
6502 | /// If a [RenderObjectElement] object has any children [Element]s, it must |
6503 | /// expose them in its implementation of the [visitChildren] method. This method |
6504 | /// is used by many of the framework's internal mechanisms, and so should be |
6505 | /// fast. It is also used by the test framework and [debugDumpApp]. |
6506 | abstract class RenderObjectElement extends Element { |
6507 | /// Creates an element that uses the given widget as its configuration. |
6508 | RenderObjectElement(RenderObjectWidget super.widget); |
6509 | |
6510 | /// The underlying [RenderObject] for this element. |
6511 | /// |
6512 | /// If this element has been [unmount]ed, this getter will throw. |
6513 | @override |
6514 | RenderObject get renderObject { |
6515 | assert(_renderObject != null,'$runtimeTypeunmounted'); |
6516 | return _renderObject!; |
6517 | } |
6518 | |
6519 | RenderObject? _renderObject; |
6520 | |
6521 | @override |
6522 | Element? get renderObjectAttachingChild => null; |
6523 | |
6524 | bool _debugDoingBuild = false; |
6525 | @override |
6526 | bool get debugDoingBuild => _debugDoingBuild; |
6527 | |
6528 | RenderObjectElement? _ancestorRenderObjectElement; |
6529 | |
6530 | RenderObjectElement? _findAncestorRenderObjectElement() { |
6531 | Element? ancestor = _parent; |
6532 | while (ancestor != null && ancestor is! RenderObjectElement) { |
6533 | // In debug mode we check whether the ancestor accepts RenderObjects to |
6534 | // produce a better error message in attachRenderObject. In release mode, |
6535 | // we assume only correct trees are built (i.e. |
6536 | // debugExpectsRenderObjectForSlot always returns true) and don't check |
6537 | // explicitly. |
6538 | assert(() { |
6539 | if (!ancestor!.debugExpectsRenderObjectForSlot(slot)) { |
6540 | ancestor = null; |
6541 | } |
6542 | return true; |
6543 | }()); |
6544 | ancestor = ancestor?._parent; |
6545 | } |
6546 | assert(() { |
6547 | if (ancestor?.debugExpectsRenderObjectForSlot(slot) == false) { |
6548 | ancestor = null; |
6549 | } |
6550 | return true; |
6551 | }()); |
6552 | return ancestor as RenderObjectElement?; |
6553 | } |
6554 | |
6555 | void _debugCheckCompetingAncestors( |
6556 | List<ParentDataElement<ParentData>> result, |
6557 | Set<Type> debugAncestorTypes, |
6558 | Set<Type> debugParentDataTypes, |
6559 | List<Type> debugAncestorCulprits, |
6560 | ) { |
6561 | assert(() { |
6562 | // Check that no other ParentDataWidgets of the same |
6563 | // type want to provide parent data. |
6564 | if (debugAncestorTypes.length != result.length || |
6565 | debugParentDataTypes.length != result.length) { |
6566 | // This can only occur if the Sets of ancestors and parent data types was |
6567 | // provided a dupe and did not add it. |
6568 | assert( |
6569 | debugAncestorTypes.length < result.length || debugParentDataTypes.length < result.length, |
6570 | ); |
6571 | try { |
6572 | // We explicitly throw here (even though we immediately redirect the |
6573 | // exception elsewhere) so that debuggers will notice it when they |
6574 | // have "break on exception" enabled. |
6575 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
6576 | ErrorSummary('Incorrect use of ParentDataWidget.'), |
6577 | ErrorDescription( |
6578 | 'Competing ParentDataWidgets are providing parent data to the ' |
6579 | 'same RenderObject:', |
6580 | ), |
6581 | for (final ParentDataElement<ParentData> ancestor in result.where(( |
6582 | ParentDataElement<ParentData> ancestor, |
6583 | ) { |
6584 | return debugAncestorCulprits.contains(ancestor.runtimeType); |
6585 | })) |
6586 | ErrorDescription( |
6587 | '-${ancestor.widget}, which writes ParentData of type ' |
6588 | '${ancestor.debugParentDataType}, (typically placed directly ' |
6589 | 'inside a ' |
6590 | '${(ancestor.widget as ParentDataWidget<ParentData>).debugTypicalAncestorWidgetClass}' |
6591 | 'widget)', |
6592 | ), |
6593 | ErrorDescription( |
6594 | 'A RenderObject can receive parent data from multiple ' |
6595 | 'ParentDataWidgets, but the Type of ParentData must be unique to ' |
6596 | 'prevent one overwriting another.', |
6597 | ), |
6598 | ErrorHint( |
6599 | 'Usually, this indicates that one or more of the offending ' |
6600 | "ParentDataWidgets listed above isn't placed inside a dedicated " |
6601 | "compatible ancestor widget that it isn't sharing with another " |
6602 | 'ParentDataWidget of the same type.', |
6603 | ), |
6604 | ErrorHint( |
6605 | 'Otherwise, separating aspects of ParentData to prevent ' |
6606 | 'conflicts can be done using mixins, mixing them all in on the ' |
6607 | 'full ParentData Object, such as KeepAlive does with ' |
6608 | 'KeepAliveParentDataMixin.', |
6609 | ), |
6610 | ErrorDescription( |
6611 | 'The ownership chain for the RenderObject that received the ' |
6612 | 'parent data was:\n${debugGetCreatorChain(10)}', |
6613 | ), |
6614 | ]); |
6615 | } on FlutterError catch (error) { |
6616 | _reportException(ErrorSummary('while looking for parent data.'), error, error.stackTrace); |
6617 | } |
6618 | } |
6619 | return true; |
6620 | }()); |
6621 | } |
6622 | |
6623 | List<ParentDataElement<ParentData>> _findAncestorParentDataElements() { |
6624 | Element? ancestor = _parent; |
6625 | final List<ParentDataElement<ParentData>> result = <ParentDataElement<ParentData>>[]; |
6626 | final Set<Type> debugAncestorTypes = <Type>{}; |
6627 | final Set<Type> debugParentDataTypes = <Type>{}; |
6628 | final List<Type> debugAncestorCulprits = <Type>[]; |
6629 | |
6630 | // More than one ParentDataWidget can contribute ParentData, but there are |
6631 | // some constraints. |
6632 | // 1. ParentData can only be written by unique ParentDataWidget types. |
6633 | // For example, two KeepAlive ParentDataWidgets trying to write to the |
6634 | // same child is not allowed. |
6635 | // 2. Each contributing ParentDataWidget must contribute to a unique |
6636 | // ParentData type, less ParentData be overwritten. |
6637 | // For example, there cannot be two ParentDataWidgets that both write |
6638 | // ParentData of type KeepAliveParentDataMixin, if the first check was |
6639 | // subverted by a subclassing of the KeepAlive ParentDataWidget. |
6640 | // 3. The ParentData itself must be compatible with all ParentDataWidgets |
6641 | // writing to it. |
6642 | // For example, TwoDimensionalViewportParentData uses the |
6643 | // KeepAliveParentDataMixin, so it could be compatible with both |
6644 | // KeepAlive, and another ParentDataWidget with ParentData type |
6645 | // TwoDimensionalViewportParentData or a subclass thereof. |
6646 | // The first and second cases are verified here. The third is verified in |
6647 | // debugIsValidRenderObject. |
6648 | |
6649 | while (ancestor != null && ancestor is! RenderObjectElement) { |
6650 | if (ancestor is ParentDataElement<ParentData>) { |
6651 | assert((ParentDataElement<ParentData> ancestor) { |
6652 | if (!debugAncestorTypes.add(ancestor.runtimeType) || |
6653 | !debugParentDataTypes.add(ancestor.debugParentDataType)) { |
6654 | debugAncestorCulprits.add(ancestor.runtimeType); |
6655 | } |
6656 | return true; |
6657 | }(ancestor)); |
6658 | result.add(ancestor); |
6659 | } |
6660 | ancestor = ancestor._parent; |
6661 | } |
6662 | assert(() { |
6663 | if (result.isEmpty || ancestor == null) { |
6664 | return true; |
6665 | } |
6666 | // Validate points 1 and 2 from above. |
6667 | _debugCheckCompetingAncestors( |
6668 | result, |
6669 | debugAncestorTypes, |
6670 | debugParentDataTypes, |
6671 | debugAncestorCulprits, |
6672 | ); |
6673 | return true; |
6674 | }()); |
6675 | return result; |
6676 | } |
6677 | |
6678 | @override |
6679 | void mount(Element? parent, Object? newSlot) { |
6680 | super.mount(parent, newSlot); |
6681 | assert(() { |
6682 | _debugDoingBuild = true; |
6683 | return true; |
6684 | }()); |
6685 | _renderObject = (widget as RenderObjectWidget).createRenderObject(this); |
6686 | assert(!_renderObject!.debugDisposed!); |
6687 | assert(() { |
6688 | _debugDoingBuild = false; |
6689 | return true; |
6690 | }()); |
6691 | assert(() { |
6692 | _debugUpdateRenderObjectOwner(); |
6693 | return true; |
6694 | }()); |
6695 | assert(slot == newSlot); |
6696 | attachRenderObject(newSlot); |
6697 | super.performRebuild(); // clears the "dirty" flag |
6698 | } |
6699 | |
6700 | @override |
6701 | void update(covariant RenderObjectWidget newWidget) { |
6702 | super.update(newWidget); |
6703 | assert(widget == newWidget); |
6704 | assert(() { |
6705 | _debugUpdateRenderObjectOwner(); |
6706 | return true; |
6707 | }()); |
6708 | _performRebuild(); // calls widget.updateRenderObject() |
6709 | } |
6710 | |
6711 | void _debugUpdateRenderObjectOwner() { |
6712 | assert(() { |
6713 | renderObject.debugCreator = DebugCreator(this); |
6714 | return true; |
6715 | }()); |
6716 | } |
6717 | |
6718 | @override |
6719 | // ignore: must_call_super, _performRebuild calls super. |
6720 | void performRebuild() { |
6721 | _performRebuild(); // calls widget.updateRenderObject() |
6722 | } |
6723 | |
6724 | @pragma('dart2js:tryInline') |
6725 | @pragma('vm:prefer-inline') |
6726 | @pragma('wasm:prefer-inline') |
6727 | void _performRebuild() { |
6728 | assert(() { |
6729 | _debugDoingBuild = true; |
6730 | return true; |
6731 | }()); |
6732 | (widget as RenderObjectWidget).updateRenderObject(this, renderObject); |
6733 | assert(() { |
6734 | _debugDoingBuild = false; |
6735 | return true; |
6736 | }()); |
6737 | super.performRebuild(); // clears the "dirty" flag |
6738 | } |
6739 | |
6740 | @override |
6741 | void deactivate() { |
6742 | super.deactivate(); |
6743 | assert( |
6744 | !renderObject.attached, |
6745 | 'A RenderObject was still attached when attempting to deactivate its ' |
6746 | 'RenderObjectElement:$renderObject', |
6747 | ); |
6748 | } |
6749 | |
6750 | @override |
6751 | void unmount() { |
6752 | assert( |
6753 | !renderObject.debugDisposed!, |
6754 | 'A RenderObject was disposed prior to its owning element being unmounted: ' |
6755 | '$renderObject', |
6756 | ); |
6757 | final RenderObjectWidget oldWidget = widget as RenderObjectWidget; |
6758 | super.unmount(); |
6759 | assert( |
6760 | !renderObject.attached, |
6761 | 'A RenderObject was still attached when attempting to unmount its ' |
6762 | 'RenderObjectElement:$renderObject', |
6763 | ); |
6764 | oldWidget.didUnmountRenderObject(renderObject); |
6765 | _renderObject!.dispose(); |
6766 | _renderObject = null; |
6767 | } |
6768 | |
6769 | void _updateParentData(ParentDataWidget<ParentData> parentDataWidget) { |
6770 | bool applyParentData = true; |
6771 | assert(() { |
6772 | try { |
6773 | if (!parentDataWidget.debugIsValidRenderObject(renderObject)) { |
6774 | applyParentData = false; |
6775 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
6776 | ErrorSummary('Incorrect use of ParentDataWidget.'), |
6777 | ...parentDataWidget._debugDescribeIncorrectParentDataType( |
6778 | parentData: renderObject.parentData, |
6779 | parentDataCreator: _ancestorRenderObjectElement?.widget as RenderObjectWidget?, |
6780 | ownershipChain: ErrorDescription(debugGetCreatorChain(10)), |
6781 | ), |
6782 | ]); |
6783 | } |
6784 | } on FlutterError catch (e) { |
6785 | // We catch the exception directly to avoid activating the ErrorWidget, |
6786 | // while still allowing debuggers to break on exception. Since the tree |
6787 | // is in a broken state, adding the ErrorWidget would likely cause more |
6788 | // exceptions, which is not good for the debugging experience. |
6789 | _reportException(ErrorSummary('while applying parent data.'), e, e.stackTrace); |
6790 | } |
6791 | return true; |
6792 | }()); |
6793 | if (applyParentData) { |
6794 | parentDataWidget.applyParentData(renderObject); |
6795 | } |
6796 | } |
6797 | |
6798 | @override |
6799 | void updateSlot(Object? newSlot) { |
6800 | final Object? oldSlot = slot; |
6801 | assert(oldSlot != newSlot); |
6802 | super.updateSlot(newSlot); |
6803 | assert(slot == newSlot); |
6804 | assert(_ancestorRenderObjectElement == _findAncestorRenderObjectElement()); |
6805 | _ancestorRenderObjectElement?.moveRenderObjectChild(renderObject, oldSlot, slot); |
6806 | } |
6807 | |
6808 | @override |
6809 | void attachRenderObject(Object? newSlot) { |
6810 | assert(_ancestorRenderObjectElement == null); |
6811 | _slot = newSlot; |
6812 | _ancestorRenderObjectElement = _findAncestorRenderObjectElement(); |
6813 | assert(() { |
6814 | if (_ancestorRenderObjectElement == null) { |
6815 | FlutterError.reportError( |
6816 | FlutterErrorDetails( |
6817 | exception: FlutterError.fromParts(<DiagnosticsNode>[ |
6818 | ErrorSummary( |
6819 | 'The render object for${toStringShort()}cannot find ancestor render object to attach to.', |
6820 | ), |
6821 | ErrorDescription( |
6822 | 'The ownership chain for the RenderObject in question was:\n${debugGetCreatorChain(10)}', |
6823 | ), |
6824 | ErrorHint( |
6825 | 'Try wrapping your widget in a View widget or any other widget that is backed by ' |
6826 | 'a$RenderTreeRootElementto serve as the root of the render tree.', |
6827 | ), |
6828 | ]), |
6829 | ), |
6830 | ); |
6831 | } |
6832 | return true; |
6833 | }()); |
6834 | _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot); |
6835 | final List<ParentDataElement<ParentData>> parentDataElements = |
6836 | _findAncestorParentDataElements(); |
6837 | for (final ParentDataElement<ParentData> parentDataElement in parentDataElements) { |
6838 | _updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>); |
6839 | } |
6840 | } |
6841 | |
6842 | @override |
6843 | void detachRenderObject() { |
6844 | if (_ancestorRenderObjectElement != null) { |
6845 | _ancestorRenderObjectElement!.removeRenderObjectChild(renderObject, slot); |
6846 | _ancestorRenderObjectElement = null; |
6847 | } |
6848 | _slot = null; |
6849 | } |
6850 | |
6851 | /// Insert the given child into [renderObject] at the given slot. |
6852 | /// |
6853 | /// {@template flutter.widgets.RenderObjectElement.insertRenderObjectChild} |
6854 | /// The semantics of `slot` are determined by this element. For example, if |
6855 | /// this element has a single child, the slot should always be null. If this |
6856 | /// element has a list of children, the previous sibling element wrapped in an |
6857 | /// [IndexedSlot] is a convenient value for the slot. |
6858 | /// {@endtemplate} |
6859 | @protected |
6860 | void insertRenderObjectChild(covariant RenderObject child, covariant Object? slot); |
6861 | |
6862 | /// Move the given child from the given old slot to the given new slot. |
6863 | /// |
6864 | /// The given child is guaranteed to have [renderObject] as its parent. |
6865 | /// |
6866 | /// {@macro flutter.widgets.RenderObjectElement.insertRenderObjectChild} |
6867 | /// |
6868 | /// This method is only ever called if [updateChild] can end up being called |
6869 | /// with an existing [Element] child and a `slot` that differs from the slot |
6870 | /// that element was previously given. [MultiChildRenderObjectElement] does this, |
6871 | /// for example. [SingleChildRenderObjectElement] does not (since the `slot` is |
6872 | /// always null). An [Element] that has a specific set of slots with each child |
6873 | /// always having the same slot (and where children in different slots are never |
6874 | /// compared against each other for the purposes of updating one slot with the |
6875 | /// element from another slot) would never call this. |
6876 | @protected |
6877 | void moveRenderObjectChild( |
6878 | covariant RenderObject child, |
6879 | covariant Object? oldSlot, |
6880 | covariant Object? newSlot, |
6881 | ); |
6882 | |
6883 | /// Remove the given child from [renderObject]. |
6884 | /// |
6885 | /// The given child is guaranteed to have been inserted at the given `slot` |
6886 | /// and have [renderObject] as its parent. |
6887 | @protected |
6888 | void removeRenderObjectChild(covariant RenderObject child, covariant Object? slot); |
6889 | |
6890 | @override |
6891 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
6892 | super.debugFillProperties(properties); |
6893 | properties.add( |
6894 | DiagnosticsProperty<RenderObject>('renderObject', _renderObject, defaultValue: null), |
6895 | ); |
6896 | } |
6897 | } |
6898 | |
6899 | /// Deprecated. Unused in the framework and will be removed in a future version |
6900 | /// of Flutter. |
6901 | /// |
6902 | /// Classes that extend this class can extend [RenderObjectElement] and mixin |
6903 | /// [RootElementMixin] instead. |
6904 | @Deprecated( |
6905 | 'Use RootElementMixin instead. ' |
6906 | 'This feature was deprecated after v3.9.0-16.0.pre.', |
6907 | ) |
6908 | abstract class RootRenderObjectElement extends RenderObjectElement with RootElementMixin { |
6909 | /// Initializes fields for subclasses. |
6910 | @Deprecated( |
6911 | 'Use RootElementMixin instead. ' |
6912 | 'This feature was deprecated after v3.9.0-16.0.pre.', |
6913 | ) |
6914 | RootRenderObjectElement(super.widget); |
6915 | } |
6916 | |
6917 | /// Mixin for the element at the root of the tree. |
6918 | /// |
6919 | /// Only root elements may have their owner set explicitly. All other |
6920 | /// elements inherit their owner from their parent. |
6921 | mixin RootElementMixin on Element { |
6922 | /// Set the owner of the element. The owner will be propagated to all the |
6923 | /// descendants of this element. |
6924 | /// |
6925 | /// The owner manages the dirty elements list. |
6926 | /// |
6927 | /// The [WidgetsBinding] introduces the primary owner, |
6928 | /// [WidgetsBinding.buildOwner], and assigns it to the widget tree in the call |
6929 | /// to [runApp]. The binding is responsible for driving the build pipeline by |
6930 | /// calling the build owner's [BuildOwner.buildScope] method. See |
6931 | /// [WidgetsBinding.drawFrame]. |
6932 | void assignOwner(BuildOwner owner) { |
6933 | _owner = owner; |
6934 | _parentBuildScope = BuildScope(); |
6935 | } |
6936 | |
6937 | @override |
6938 | void mount(Element? parent, Object? newSlot) { |
6939 | // Root elements should never have parents. |
6940 | assert(parent == null); |
6941 | assert(newSlot == null); |
6942 | super.mount(parent, newSlot); |
6943 | } |
6944 | } |
6945 | |
6946 | /// An [Element] that uses a [LeafRenderObjectWidget] as its configuration. |
6947 | class LeafRenderObjectElement extends RenderObjectElement { |
6948 | /// Creates an element that uses the given widget as its configuration. |
6949 | LeafRenderObjectElement(LeafRenderObjectWidget super.widget); |
6950 | |
6951 | @override |
6952 | void forgetChild(Element child) { |
6953 | assert(false); |
6954 | super.forgetChild(child); |
6955 | } |
6956 | |
6957 | @override |
6958 | void insertRenderObjectChild(RenderObject child, Object? slot) { |
6959 | assert(false); |
6960 | } |
6961 | |
6962 | @override |
6963 | void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) { |
6964 | assert(false); |
6965 | } |
6966 | |
6967 | @override |
6968 | void removeRenderObjectChild(RenderObject child, Object? slot) { |
6969 | assert(false); |
6970 | } |
6971 | |
6972 | @override |
6973 | List<DiagnosticsNode> debugDescribeChildren() { |
6974 | return widget.debugDescribeChildren(); |
6975 | } |
6976 | } |
6977 | |
6978 | /// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration. |
6979 | /// |
6980 | /// The child is optional. |
6981 | /// |
6982 | /// This element subclass can be used for [RenderObjectWidget]s whose |
6983 | /// [RenderObject]s use the [RenderObjectWithChildMixin] mixin. Such widgets are |
6984 | /// expected to inherit from [SingleChildRenderObjectWidget]. |
6985 | class SingleChildRenderObjectElement extends RenderObjectElement { |
6986 | /// Creates an element that uses the given widget as its configuration. |
6987 | SingleChildRenderObjectElement(SingleChildRenderObjectWidget super.widget); |
6988 | |
6989 | Element? _child; |
6990 | |
6991 | @override |
6992 | void visitChildren(ElementVisitor visitor) { |
6993 | if (_child != null) { |
6994 | visitor(_child!); |
6995 | } |
6996 | } |
6997 | |
6998 | @override |
6999 | void forgetChild(Element child) { |
7000 | assert(child == _child); |
7001 | _child = null; |
7002 | super.forgetChild(child); |
7003 | } |
7004 | |
7005 | @override |
7006 | void mount(Element? parent, Object? newSlot) { |
7007 | super.mount(parent, newSlot); |
7008 | _child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null); |
7009 | } |
7010 | |
7011 | @override |
7012 | void update(SingleChildRenderObjectWidget newWidget) { |
7013 | super.update(newWidget); |
7014 | assert(widget == newWidget); |
7015 | _child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null); |
7016 | } |
7017 | |
7018 | @override |
7019 | void insertRenderObjectChild(RenderObject child, Object? slot) { |
7020 | final RenderObjectWithChildMixin<RenderObject> renderObject = |
7021 | this.renderObject as RenderObjectWithChildMixin<RenderObject>; |
7022 | assert(slot == null); |
7023 | assert(renderObject.debugValidateChild(child)); |
7024 | renderObject.child = child; |
7025 | assert(renderObject == this.renderObject); |
7026 | } |
7027 | |
7028 | @override |
7029 | void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) { |
7030 | assert(false); |
7031 | } |
7032 | |
7033 | @override |
7034 | void removeRenderObjectChild(RenderObject child, Object? slot) { |
7035 | final RenderObjectWithChildMixin<RenderObject> renderObject = |
7036 | this.renderObject as RenderObjectWithChildMixin<RenderObject>; |
7037 | assert(slot == null); |
7038 | assert(renderObject.child == child); |
7039 | renderObject.child = null; |
7040 | assert(renderObject == this.renderObject); |
7041 | } |
7042 | } |
7043 | |
7044 | /// An [Element] that uses a [MultiChildRenderObjectWidget] as its configuration. |
7045 | /// |
7046 | /// This element subclass can be used for [RenderObjectWidget]s whose |
7047 | /// [RenderObject]s use the [ContainerRenderObjectMixin] mixin with a parent data |
7048 | /// type that implements [ContainerParentDataMixin<RenderObject>]. Such widgets |
7049 | /// are expected to inherit from [MultiChildRenderObjectWidget]. |
7050 | /// |
7051 | /// See also: |
7052 | /// |
7053 | /// * [IndexedSlot], which is used as [Element.slot]s for the children of a |
7054 | /// [MultiChildRenderObjectElement]. |
7055 | /// * [RenderObjectElement.updateChildren], which discusses why [IndexedSlot] |
7056 | /// is used for the slots of the children. |
7057 | class MultiChildRenderObjectElement extends RenderObjectElement { |
7058 | /// Creates an element that uses the given widget as its configuration. |
7059 | MultiChildRenderObjectElement(MultiChildRenderObjectWidget super.widget) |
7060 | : assert(!debugChildrenHaveDuplicateKeys(widget, widget.children)); |
7061 | |
7062 | @override |
7063 | ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> |
7064 | get renderObject { |
7065 | return super.renderObject |
7066 | as ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>; |
7067 | } |
7068 | |
7069 | /// The current list of children of this element. |
7070 | /// |
7071 | /// This list is filtered to hide elements that have been forgotten (using |
7072 | /// [forgetChild]). |
7073 | @protected |
7074 | @visibleForTesting |
7075 | Iterable<Element> get children => |
7076 | _children.where((Element child) => !_forgottenChildren.contains(child)); |
7077 | |
7078 | late List<Element> _children; |
7079 | // We keep a set of forgotten children to avoid O(n^2) work walking _children |
7080 | // repeatedly to remove children. |
7081 | final Set<Element> _forgottenChildren = HashSet<Element>(); |
7082 | |
7083 | @override |
7084 | void insertRenderObjectChild(RenderObject child, IndexedSlot<Element?> slot) { |
7085 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> |
7086 | renderObject = this.renderObject; |
7087 | assert(renderObject.debugValidateChild(child)); |
7088 | renderObject.insert(child, after: slot.value?.renderObject); |
7089 | assert(renderObject == this.renderObject); |
7090 | } |
7091 | |
7092 | @override |
7093 | void moveRenderObjectChild( |
7094 | RenderObject child, |
7095 | IndexedSlot<Element?> oldSlot, |
7096 | IndexedSlot<Element?> newSlot, |
7097 | ) { |
7098 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> |
7099 | renderObject = this.renderObject; |
7100 | assert(child.parent == renderObject); |
7101 | renderObject.move(child, after: newSlot.value?.renderObject); |
7102 | assert(renderObject == this.renderObject); |
7103 | } |
7104 | |
7105 | @override |
7106 | void removeRenderObjectChild(RenderObject child, Object? slot) { |
7107 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> |
7108 | renderObject = this.renderObject; |
7109 | assert(child.parent == renderObject); |
7110 | renderObject.remove(child); |
7111 | assert(renderObject == this.renderObject); |
7112 | } |
7113 | |
7114 | @override |
7115 | void visitChildren(ElementVisitor visitor) { |
7116 | for (final Element child in _children) { |
7117 | if (!_forgottenChildren.contains(child)) { |
7118 | visitor(child); |
7119 | } |
7120 | } |
7121 | } |
7122 | |
7123 | @override |
7124 | void forgetChild(Element child) { |
7125 | assert(_children.contains(child)); |
7126 | assert(!_forgottenChildren.contains(child)); |
7127 | _forgottenChildren.add(child); |
7128 | super.forgetChild(child); |
7129 | } |
7130 | |
7131 | bool _debugCheckHasAssociatedRenderObject(Element newChild) { |
7132 | assert(() { |
7133 | if (newChild.renderObject == null) { |
7134 | FlutterError.reportError( |
7135 | FlutterErrorDetails( |
7136 | exception: FlutterError.fromParts(<DiagnosticsNode>[ |
7137 | ErrorSummary( |
7138 | 'The children of `MultiChildRenderObjectElement` must each has an associated render object.', |
7139 | ), |
7140 | ErrorHint( |
7141 | 'This typically means that the `${newChild.widget}` or its children\n' |
7142 | 'are not a subtype of `RenderObjectWidget`.', |
7143 | ), |
7144 | newChild.describeElement( |
7145 | 'The following element does not have an associated render object', |
7146 | ), |
7147 | DiagnosticsDebugCreator(DebugCreator(newChild)), |
7148 | ]), |
7149 | ), |
7150 | ); |
7151 | } |
7152 | return true; |
7153 | }()); |
7154 | return true; |
7155 | } |
7156 | |
7157 | @override |
7158 | Element inflateWidget(Widget newWidget, Object? newSlot) { |
7159 | final Element newChild = super.inflateWidget(newWidget, newSlot); |
7160 | assert(_debugCheckHasAssociatedRenderObject(newChild)); |
7161 | return newChild; |
7162 | } |
7163 | |
7164 | @override |
7165 | void mount(Element? parent, Object? newSlot) { |
7166 | super.mount(parent, newSlot); |
7167 | final MultiChildRenderObjectWidget multiChildRenderObjectWidget = |
7168 | widget as MultiChildRenderObjectWidget; |
7169 | final List<Element> children = List<Element>.filled( |
7170 | multiChildRenderObjectWidget.children.length, |
7171 | _NullElement.instance, |
7172 | ); |
7173 | Element? previousChild; |
7174 | for (int i = 0; i < children.length; i += 1) { |
7175 | final Element newChild = inflateWidget( |
7176 | multiChildRenderObjectWidget.children[i], |
7177 | IndexedSlot<Element?>(i, previousChild), |
7178 | ); |
7179 | children[i] = newChild; |
7180 | previousChild = newChild; |
7181 | } |
7182 | _children = children; |
7183 | } |
7184 | |
7185 | @override |
7186 | void update(MultiChildRenderObjectWidget newWidget) { |
7187 | super.update(newWidget); |
7188 | final MultiChildRenderObjectWidget multiChildRenderObjectWidget = |
7189 | widget as MultiChildRenderObjectWidget; |
7190 | assert(widget == newWidget); |
7191 | assert(!debugChildrenHaveDuplicateKeys(widget, multiChildRenderObjectWidget.children)); |
7192 | _children = updateChildren( |
7193 | _children, |
7194 | multiChildRenderObjectWidget.children, |
7195 | forgottenChildren: _forgottenChildren, |
7196 | ); |
7197 | _forgottenChildren.clear(); |
7198 | } |
7199 | } |
7200 | |
7201 | /// A [RenderObjectElement] used to manage the root of a render tree. |
7202 | /// |
7203 | /// Unlike any other render object element this element does not attempt to |
7204 | /// attach its [renderObject] to the closest ancestor [RenderObjectElement]. |
7205 | /// Instead, subclasses must override [attachRenderObject] and |
7206 | /// [detachRenderObject] to attach/detach the [renderObject] to whatever |
7207 | /// instance manages the render tree (e.g. by assigning it to |
7208 | /// [PipelineOwner.rootNode]). |
7209 | abstract class RenderTreeRootElement extends RenderObjectElement { |
7210 | /// Creates an element that uses the given widget as its configuration. |
7211 | RenderTreeRootElement(super.widget); |
7212 | |
7213 | @override |
7214 | @mustCallSuper |
7215 | void attachRenderObject(Object? newSlot) { |
7216 | _slot = newSlot; |
7217 | assert(_debugCheckMustNotAttachRenderObjectToAncestor()); |
7218 | } |
7219 | |
7220 | @override |
7221 | @mustCallSuper |
7222 | void detachRenderObject() { |
7223 | _slot = null; |
7224 | } |
7225 | |
7226 | @override |
7227 | void updateSlot(Object? newSlot) { |
7228 | super.updateSlot(newSlot); |
7229 | assert(_debugCheckMustNotAttachRenderObjectToAncestor()); |
7230 | } |
7231 | |
7232 | bool _debugCheckMustNotAttachRenderObjectToAncestor() { |
7233 | if (!kDebugMode) { |
7234 | return true; |
7235 | } |
7236 | if (_findAncestorRenderObjectElement() != null) { |
7237 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
7238 | ErrorSummary( |
7239 | 'The RenderObject for${toStringShort()}cannot maintain an independent render tree at its current location.', |
7240 | ), |
7241 | ErrorDescription( |
7242 | 'The ownership chain for the RenderObject in question was:\n${debugGetCreatorChain(10)}', |
7243 | ), |
7244 | ErrorDescription( |
7245 | 'This RenderObject is the root of an independent render tree and it cannot ' |
7246 | 'attach itself to an ancestor in an existing tree. The ancestor RenderObject, ' |
7247 | 'however, expects that a child will be attached.', |
7248 | ), |
7249 | ErrorHint( |
7250 | 'Try moving the subtree that contains the${toStringShort()}widget ' |
7251 | 'to a location where it is not expected to attach its RenderObject ' |
7252 | 'to a parent. This could mean moving the subtree into the view ' |
7253 | 'property of a "ViewAnchor" widget or - if the subtree is the root of ' |
7254 | 'your widget tree - passing it to "runWidget" instead of "runApp".', |
7255 | ), |
7256 | ErrorHint( |
7257 | 'If you are seeing this error in a test and the subtree containing ' |
7258 | 'the${toStringShort()}widget is passed to "WidgetTester.pumpWidget", ' |
7259 | 'consider setting the "wrapWithView" parameter of that method to false.', |
7260 | ), |
7261 | ]); |
7262 | } |
7263 | return true; |
7264 | } |
7265 | } |
7266 | |
7267 | /// A wrapper class for the [Element] that is the creator of a [RenderObject]. |
7268 | /// |
7269 | /// Setting a [DebugCreator] as [RenderObject.debugCreator] will lead to better |
7270 | /// error messages. |
7271 | class DebugCreator { |
7272 | /// Create a [DebugCreator] instance with input [Element]. |
7273 | DebugCreator(this.element); |
7274 | |
7275 | /// The creator of the [RenderObject]. |
7276 | final Element element; |
7277 | |
7278 | @override |
7279 | String toString() => element.debugGetCreatorChain(12); |
7280 | } |
7281 | |
7282 | FlutterErrorDetails _reportException( |
7283 | DiagnosticsNode context, |
7284 | Object exception, |
7285 | StackTrace? stack, { |
7286 | InformationCollector? informationCollector, |
7287 | }) { |
7288 | final FlutterErrorDetails details = FlutterErrorDetails( |
7289 | exception: exception, |
7290 | stack: stack, |
7291 | library:'widgets library', |
7292 | context: context, |
7293 | informationCollector: informationCollector, |
7294 | ); |
7295 | FlutterError.reportError(details); |
7296 | return details; |
7297 | } |
7298 | |
7299 | /// A value for [Element.slot] used for children of |
7300 | /// [MultiChildRenderObjectElement]s. |
7301 | /// |
7302 | /// A slot for a [MultiChildRenderObjectElement] consists of an [index] |
7303 | /// identifying where the child occupying this slot is located in the |
7304 | /// [MultiChildRenderObjectElement]'s child list and an arbitrary [value] that |
7305 | /// can further define where the child occupying this slot fits in its |
7306 | /// parent's child list. |
7307 | /// |
7308 | /// See also: |
7309 | /// |
7310 | /// * [RenderObjectElement.updateChildren], which discusses why this class is |
7311 | /// used as slot values for the children of a [MultiChildRenderObjectElement]. |
7312 | @immutable |
7313 | class IndexedSlot<T extends Element?> { |
7314 | /// Creates an [IndexedSlot] with the provided [index] and slot [value]. |
7315 | const IndexedSlot(this.index, this.value); |
7316 | |
7317 | /// Information to define where the child occupying this slot fits in its |
7318 | /// parent's child list. |
7319 | final T value; |
7320 | |
7321 | /// The index of this slot in the parent's child list. |
7322 | final int index; |
7323 | |
7324 | @override |
7325 | bool operator ==(Object other) { |
7326 | if (other.runtimeType != runtimeType) { |
7327 | return false; |
7328 | } |
7329 | return other is IndexedSlot && index == other.index && value == other.value; |
7330 | } |
7331 | |
7332 | @override |
7333 | int get hashCode => Object.hash(index, value); |
7334 | } |
7335 | |
7336 | /// Used as a placeholder in [List<Element>] objects when the actual |
7337 | /// elements are not yet determined. |
7338 | class _NullElement extends Element { |
7339 | _NullElement() : super(const _NullWidget()); |
7340 | |
7341 | static _NullElement instance = _NullElement(); |
7342 | |
7343 | @override |
7344 | bool get debugDoingBuild => throw UnimplementedError(); |
7345 | } |
7346 | |
7347 | class _NullWidget extends Widget { |
7348 | const _NullWidget(); |
7349 | |
7350 | @override |
7351 | Element createElement() => throw UnimplementedError(); |
7352 | } |
7353 |
Definitions
- _DebugOnly
- _DebugOnly
- _debugOnly
- ObjectKey
- ObjectKey
- ==
- hashCode
- toString
- GlobalKey
- GlobalKey
- constructor
- _currentElement
- currentContext
- currentWidget
- currentState
- LabeledGlobalKey
- LabeledGlobalKey
- toString
- GlobalObjectKey
- GlobalObjectKey
- ==
- hashCode
- toString
- Widget
- Widget
- createElement
- toStringShort
- debugFillProperties
- ==
- hashCode
- canUpdate
- _debugConcreteSubtype
- StatelessWidget
- StatelessWidget
- createElement
- build
- StatefulWidget
- StatefulWidget
- createElement
- createState
- _StateLifecycle
- State
- widget
- _debugTypesAreRight
- context
- mounted
- initState
- didUpdateWidget
- reassemble
- setState
- deactivate
- activate
- dispose
- build
- didChangeDependencies
- debugFillProperties
- ProxyWidget
- ProxyWidget
- ParentDataWidget
- ParentDataWidget
- createElement
- debugIsValidRenderObject
- debugTypicalAncestorWidgetClass
- debugTypicalAncestorWidgetDescription
- _debugDescribeIncorrectParentDataType
- applyParentData
- debugCanApplyOutOfTurn
- InheritedWidget
- InheritedWidget
- createElement
- updateShouldNotify
- RenderObjectWidget
- RenderObjectWidget
- createElement
- createRenderObject
- updateRenderObject
- didUnmountRenderObject
- LeafRenderObjectWidget
- LeafRenderObjectWidget
- createElement
- SingleChildRenderObjectWidget
- SingleChildRenderObjectWidget
- createElement
- MultiChildRenderObjectWidget
- MultiChildRenderObjectWidget
- createElement
- _ElementLifecycle
- _InactiveElements
- _unmount
- _unmountAll
- _deactivateRecursively
- add
- remove
- debugContains
- BuildContext
- widget
- owner
- mounted
- debugDoingBuild
- findRenderObject
- size
- dependOnInheritedElement
- dependOnInheritedWidgetOfExactType
- getInheritedWidgetOfExactType
- getElementForInheritedWidgetOfExactType
- findAncestorWidgetOfExactType
- findAncestorStateOfType
- findRootAncestorStateOfType
- findAncestorRenderObjectOfType
- visitAncestorElements
- visitChildElements
- dispatchNotification
- describeElement
- describeWidget
- describeMissingAncestor
- describeOwnershipChain
- BuildScope
- BuildScope
- _scheduleBuildFor
- _tryRebuild
- _debugAssertElementInScope
- _flushDirtyElements
- _dirtyElementIndexAfter
- BuildOwner
- BuildOwner
- scheduleBuildFor
- _debugStateLocked
- debugBuilding
- lockState
- buildScope
- _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans
- _debugElementWasRebuilt
- globalKeyCount
- _debugRemoveGlobalKeyReservationFor
- _registerGlobalKey
- _unregisterGlobalKey
- _debugReserveGlobalKeyFor
- _debugVerifyGlobalKeyReservation
- _debugVerifyIllFatedPopulation
- finalizeTree
- reassemble
- NotifiableElementMixin
- onNotification
- attachNotificationTree
- _NotificationNode
- _NotificationNode
- dispatchNotification
- _isProfileBuildsEnabledFor
- Element
- Element
- ==
- slot
- depth
- _sort
- _debugConcreteSubtype
- widget
- mounted
- debugIsDefunct
- debugIsActive
- owner
- buildScope
- reassemble
- _debugIsDescendantOf
- renderObject
- renderObjectAttachingChild
- describeMissingAncestor
- describeElements
- describeElement
- describeWidget
- describeOwnershipChain
- visitChildren
- debugVisitOnstageChildren
- visitChildElements
- updateChild
- updateChildren
- replaceWithNullIfForgotten
- slotFor
- mount
- _debugRemoveGlobalKeyReservation
- update
- updateSlotForChild
- visit
- updateSlot
- _updateDepth
- _updateBuildScopeRecursively
- detachRenderObject
- attachRenderObject
- _retakeInactiveElement
- inflateWidget
- _debugCheckForCycles
- deactivateChild
- forgetChild
- _activateWithParent
- _activateRecursively
- activate
- deactivate
- debugDeactivated
- unmount
- debugExpectsRenderObjectForSlot
- findRenderObject
- size
- _debugCheckStateIsActiveForAncestorLookup
- doesDependOnInheritedElement
- dependOnInheritedElement
- dependOnInheritedWidgetOfExactType
- getInheritedWidgetOfExactType
- getElementForInheritedWidgetOfExactType
- attachNotificationTree
- _updateInheritance
- findAncestorWidgetOfExactType
- findAncestorStateOfType
- findRootAncestorStateOfType
- findAncestorRenderObjectOfType
- visitAncestorElements
- didChangeDependencies
- _debugCheckOwnerBuildTargetExists
- debugGetCreatorChain
- debugGetDiagnosticChain
- dispatchNotification
- toStringShort
- toDiagnosticsNode
- debugFillProperties
- debugDescribeChildren
- dirty
- markNeedsBuild
- rebuild
- performRebuild
- _ElementDiagnosticableTreeNode
- _ElementDiagnosticableTreeNode
- toJsonMap
- ErrorWidget
- ErrorWidget
- withDetails
- _defaultErrorWidgetBuilder
- _stringify
- createRenderObject
- debugFillProperties
- ComponentElement
- ComponentElement
- debugDoingBuild
- renderObjectAttachingChild
- mount
- _firstBuild
- performRebuild
- build
- visitChildren
- forgetChild
- StatelessElement
- StatelessElement
- build
- update
- StatefulElement
- StatefulElement
- build
- state
- reassemble
- _firstBuild
- performRebuild
- update
- activate
- deactivate
- unmount
- dependOnInheritedElement
- didChangeDependencies
- toDiagnosticsNode
- debugFillProperties
- ProxyElement
- ProxyElement
- build
- update
- updated
- notifyClients
- ParentDataElement
- ParentDataElement
- debugParentDataType
- _applyParentData
- applyParentDataToChild
- applyWidgetOutOfTurn
- notifyClients
- InheritedElement
- InheritedElement
- _updateInheritance
- debugDeactivated
- getDependencies
- setDependencies
- updateDependencies
- notifyDependent
- removeDependent
- updated
- notifyClients
- RenderObjectElement
- RenderObjectElement
- renderObject
- renderObjectAttachingChild
- debugDoingBuild
- _findAncestorRenderObjectElement
- _debugCheckCompetingAncestors
- _findAncestorParentDataElements
- mount
- update
- _debugUpdateRenderObjectOwner
- performRebuild
- _performRebuild
- deactivate
- unmount
- _updateParentData
- updateSlot
- attachRenderObject
- detachRenderObject
- insertRenderObjectChild
- moveRenderObjectChild
- removeRenderObjectChild
- debugFillProperties
- RootRenderObjectElement
- RootRenderObjectElement
- RootElementMixin
- assignOwner
- mount
- LeafRenderObjectElement
- LeafRenderObjectElement
- forgetChild
- insertRenderObjectChild
- moveRenderObjectChild
- removeRenderObjectChild
- debugDescribeChildren
- SingleChildRenderObjectElement
- SingleChildRenderObjectElement
- visitChildren
- forgetChild
- mount
- update
- insertRenderObjectChild
- moveRenderObjectChild
- removeRenderObjectChild
- MultiChildRenderObjectElement
- MultiChildRenderObjectElement
- renderObject
- children
- insertRenderObjectChild
- moveRenderObjectChild
- removeRenderObjectChild
- visitChildren
- forgetChild
- _debugCheckHasAssociatedRenderObject
- inflateWidget
- mount
- update
- RenderTreeRootElement
- RenderTreeRootElement
- attachRenderObject
- detachRenderObject
- updateSlot
- _debugCheckMustNotAttachRenderObjectToAncestor
- DebugCreator
- DebugCreator
- toString
- _reportException
- IndexedSlot
- IndexedSlot
- ==
- hashCode
- _NullElement
- _NullElement
- debugDoingBuild
- _NullWidget
- _NullWidget
Learn more about Flutter for embedded and desktop on industrialflutter.com