| 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 { @override Widget build(BuildContext context) => widget; } |
| 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 getApplicationDocumentsDirectory() async => Directory(''); |
| 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 $this was 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 $this wants to apply ParentData of type $T to 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 $runtimeType widget has the wrong ancestor RenderObjectWidget. '
|
| 1663 | 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetDescription widgets.' ,
|
| 1664 | ),
|
| 1665 | if (parentDataCreator != null)
|
| 1666 | ErrorHint(
|
| 1667 | 'The offending $runtimeType is 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 | /// This method is called when a RenderObject that was previously
|
| 1927 | /// associated with this widget is removed from the render tree.
|
| 1928 | /// The provided [RenderObject] will be of the same type as the one created by
|
| 1929 | /// this widget's [createRenderObject] method.
|
| 1930 | @protected
|
| 1931 | void didUnmountRenderObject(covariant RenderObject renderObject) {}
|
| 1932 | }
|
| 1933 |
|
| 1934 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
| 1935 | /// that have no children.
|
| 1936 | ///
|
| 1937 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
| 1938 | abstract class LeafRenderObjectWidget extends RenderObjectWidget {
|
| 1939 | /// Abstract const constructor. This constructor enables subclasses to provide
|
| 1940 | /// const constructors so that they can be used in const expressions.
|
| 1941 | const LeafRenderObjectWidget({super.key});
|
| 1942 |
|
| 1943 | @override
|
| 1944 | LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
|
| 1945 | }
|
| 1946 |
|
| 1947 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
| 1948 | /// that have a single child slot.
|
| 1949 | ///
|
| 1950 | /// The render object assigned to this widget should make use of
|
| 1951 | /// [RenderObjectWithChildMixin] to implement a single-child model. The mixin
|
| 1952 | /// exposes a [RenderObjectWithChildMixin.child] property that allows retrieving
|
| 1953 | /// the render object belonging to the [child] widget.
|
| 1954 | ///
|
| 1955 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
| 1956 | abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
|
| 1957 | /// Abstract const constructor. This constructor enables subclasses to provide
|
| 1958 | /// const constructors so that they can be used in const expressions.
|
| 1959 | const SingleChildRenderObjectWidget({super.key, this.child});
|
| 1960 |
|
| 1961 | /// The widget below this widget in the tree.
|
| 1962 | ///
|
| 1963 | /// {@macro flutter.widgets.ProxyWidget.child}
|
| 1964 | final Widget? child;
|
| 1965 |
|
| 1966 | @override
|
| 1967 | SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
|
| 1968 | }
|
| 1969 |
|
| 1970 | /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
| 1971 | /// that have a single list of children. (This superclass only provides the
|
| 1972 | /// storage for that child list, it doesn't actually provide the updating
|
| 1973 | /// logic.)
|
| 1974 | ///
|
| 1975 | /// Subclasses must use a [RenderObject] that mixes in
|
| 1976 | /// [ContainerRenderObjectMixin], which provides the necessary functionality to
|
| 1977 | /// visit the children of the container render object (the render object
|
| 1978 | /// belonging to the [children] widgets). Typically, subclasses will use a
|
| 1979 | /// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and
|
| 1980 | /// [RenderBoxContainerDefaultsMixin].
|
| 1981 | ///
|
| 1982 | /// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
| 1983 | ///
|
| 1984 | /// See also:
|
| 1985 | ///
|
| 1986 | /// * [Stack], which uses [MultiChildRenderObjectWidget].
|
| 1987 | /// * [RenderStack], for an example implementation of the associated render
|
| 1988 | /// object.
|
| 1989 | /// * [SlottedMultiChildRenderObjectWidget], which configures a
|
| 1990 | /// [RenderObject] that instead of having a single list of children organizes
|
| 1991 | /// its children in named slots.
|
| 1992 | abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
|
| 1993 | /// Initializes fields for subclasses.
|
| 1994 | const MultiChildRenderObjectWidget({super.key, this.children = const <Widget>[]});
|
| 1995 |
|
| 1996 | /// The widgets below this widget in the tree.
|
| 1997 | ///
|
| 1998 | /// If this list is going to be mutated, it is usually wise to put a [Key] on
|
| 1999 | /// each of the child widgets, so that the framework can match old
|
| 2000 | /// configurations to new configurations and maintain the underlying render
|
| 2001 | /// objects.
|
| 2002 | ///
|
| 2003 | /// Also, a [Widget] in Flutter is immutable, so directly modifying the
|
| 2004 | /// [children] such as `someMultiChildRenderObjectWidget.children.add(...)` or
|
| 2005 | /// as the example code below will result in incorrect behaviors. Whenever the
|
| 2006 | /// children list is modified, a new list object should be provided.
|
| 2007 | ///
|
| 2008 | /// ```dart
|
| 2009 | /// // This code is incorrect.
|
| 2010 | /// class SomeWidgetState extends State<SomeWidget> {
|
| 2011 | /// final List<Widget> _children = <Widget>[];
|
| 2012 | ///
|
| 2013 | /// void someHandler() {
|
| 2014 | /// setState(() {
|
| 2015 | /// _children.add(const ChildWidget());
|
| 2016 | /// });
|
| 2017 | /// }
|
| 2018 | ///
|
| 2019 | /// @override
|
| 2020 | /// Widget build(BuildContext context) {
|
| 2021 | /// // Reusing `List _children` here is problematic.
|
| 2022 | /// return Row(children: _children);
|
| 2023 | /// }
|
| 2024 | /// }
|
| 2025 | /// ```
|
| 2026 | ///
|
| 2027 | /// The following code corrects the problem mentioned above.
|
| 2028 | ///
|
| 2029 | /// ```dart
|
| 2030 | /// class SomeWidgetState extends State<SomeWidget> {
|
| 2031 | /// final List<Widget> _children = <Widget>[];
|
| 2032 | ///
|
| 2033 | /// void someHandler() {
|
| 2034 | /// setState(() {
|
| 2035 | /// // The key here allows Flutter to reuse the underlying render
|
| 2036 | /// // objects even if the children list is recreated.
|
| 2037 | /// _children.add(ChildWidget(key: UniqueKey()));
|
| 2038 | /// });
|
| 2039 | /// }
|
| 2040 | ///
|
| 2041 | /// @override
|
| 2042 | /// Widget build(BuildContext context) {
|
| 2043 | /// // Always create a new list of children as a Widget is immutable.
|
| 2044 | /// return Row(children: _children.toList());
|
| 2045 | /// }
|
| 2046 | /// }
|
| 2047 | /// ```
|
| 2048 | final List<Widget> children;
|
| 2049 |
|
| 2050 | @override
|
| 2051 | MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);
|
| 2052 | }
|
| 2053 |
|
| 2054 | // ELEMENTS
|
| 2055 |
|
| 2056 | enum _ElementLifecycle { initial, active, inactive, defunct }
|
| 2057 |
|
| 2058 | class _InactiveElements {
|
| 2059 | bool _locked = false;
|
| 2060 | final Set<Element> _elements = HashSet<Element>();
|
| 2061 |
|
| 2062 | void _unmount(Element element) {
|
| 2063 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
| 2064 | assert(() {
|
| 2065 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
| 2066 | if (element.widget.key is GlobalKey) {
|
| 2067 | debugPrint('Discarding $element from inactive elements list.' );
|
| 2068 | }
|
| 2069 | }
|
| 2070 | return true;
|
| 2071 | }());
|
| 2072 | element.visitChildren((Element child) {
|
| 2073 | assert(child._parent == element);
|
| 2074 | _unmount(child);
|
| 2075 | });
|
| 2076 | element.unmount();
|
| 2077 | assert(element._lifecycleState == _ElementLifecycle.defunct);
|
| 2078 | }
|
| 2079 |
|
| 2080 | void _unmountAll() {
|
| 2081 | _locked = true;
|
| 2082 | final List<Element> elements = _elements.toList()..sort(Element._sort);
|
| 2083 | _elements.clear();
|
| 2084 | try {
|
| 2085 | elements.reversed.forEach(_unmount);
|
| 2086 | } finally {
|
| 2087 | assert(_elements.isEmpty);
|
| 2088 | _locked = false;
|
| 2089 | }
|
| 2090 | }
|
| 2091 |
|
| 2092 | static void _deactivateRecursively(Element element) {
|
| 2093 | assert(element._lifecycleState == _ElementLifecycle.active);
|
| 2094 | element.deactivate();
|
| 2095 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
| 2096 | element.visitChildren(_deactivateRecursively);
|
| 2097 | assert(() {
|
| 2098 | element.debugDeactivated();
|
| 2099 | return true;
|
| 2100 | }());
|
| 2101 | }
|
| 2102 |
|
| 2103 | void add(Element element) {
|
| 2104 | assert(!_locked);
|
| 2105 | assert(!_elements.contains(element));
|
| 2106 | assert(element._parent == null);
|
| 2107 | if (element._lifecycleState == _ElementLifecycle.active) {
|
| 2108 | _deactivateRecursively(element);
|
| 2109 | }
|
| 2110 | _elements.add(element);
|
| 2111 | }
|
| 2112 |
|
| 2113 | void remove(Element element) {
|
| 2114 | assert(!_locked);
|
| 2115 | assert(_elements.contains(element));
|
| 2116 | assert(element._parent == null);
|
| 2117 | _elements.remove(element);
|
| 2118 | assert(element._lifecycleState != _ElementLifecycle.active);
|
| 2119 | }
|
| 2120 |
|
| 2121 | bool debugContains(Element element) {
|
| 2122 | late bool result;
|
| 2123 | assert(() {
|
| 2124 | result = _elements.contains(element);
|
| 2125 | return true;
|
| 2126 | }());
|
| 2127 | return result;
|
| 2128 | }
|
| 2129 | }
|
| 2130 |
|
| 2131 | /// Signature for the callback to [BuildContext.visitChildElements].
|
| 2132 | ///
|
| 2133 | /// The argument is the child being visited.
|
| 2134 | ///
|
| 2135 | /// It is safe to call `element.visitChildElements` reentrantly within
|
| 2136 | /// this callback.
|
| 2137 | typedef ElementVisitor = void Function(Element element);
|
| 2138 |
|
| 2139 | /// Signature for the callback to [BuildContext.visitAncestorElements].
|
| 2140 | ///
|
| 2141 | /// The argument is the ancestor being visited.
|
| 2142 | ///
|
| 2143 | /// Return false to stop the walk.
|
| 2144 | typedef ConditionalElementVisitor = bool Function(Element element);
|
| 2145 |
|
| 2146 | /// A handle to the location of a widget in the widget tree.
|
| 2147 | ///
|
| 2148 | /// This class presents a set of methods that can be used from
|
| 2149 | /// [StatelessWidget.build] methods and from methods on [State] objects.
|
| 2150 | ///
|
| 2151 | /// [BuildContext] objects are passed to [WidgetBuilder] functions (such as
|
| 2152 | /// [StatelessWidget.build]), and are available from the [State.context] member.
|
| 2153 | /// Some static functions (e.g. [showDialog], [Theme.of], and so forth) also
|
| 2154 | /// take build contexts so that they can act on behalf of the calling widget, or
|
| 2155 | /// obtain data specifically for the given context.
|
| 2156 | ///
|
| 2157 | /// Each widget has its own [BuildContext], which becomes the parent of the
|
| 2158 | /// widget returned by the [StatelessWidget.build] or [State.build] function.
|
| 2159 | /// (And similarly, the parent of any children for [RenderObjectWidget]s.)
|
| 2160 | ///
|
| 2161 | /// In particular, this means that within a build method, the build context of
|
| 2162 | /// the widget of the build method is not the same as the build context of the
|
| 2163 | /// widgets returned by that build method. This can lead to some tricky cases.
|
| 2164 | /// For example, [Theme.of(context)] looks for the nearest enclosing [Theme] of
|
| 2165 | /// the given build context. If a build method for a widget Q includes a [Theme]
|
| 2166 | /// within its returned widget tree, and attempts to use [Theme.of] passing its
|
| 2167 | /// own context, the build method for Q will not find that [Theme] object. It
|
| 2168 | /// will instead find whatever [Theme] was an ancestor to the widget Q. If the
|
| 2169 | /// build context for a subpart of the returned tree is needed, a [Builder]
|
| 2170 | /// widget can be used: the build context passed to the [Builder.builder]
|
| 2171 | /// callback will be that of the [Builder] itself.
|
| 2172 | ///
|
| 2173 | /// For example, in the following snippet, the [ScaffoldState.showBottomSheet]
|
| 2174 | /// method is called on the [Scaffold] widget that the build method itself
|
| 2175 | /// creates. If a [Builder] had not been used, and instead the `context`
|
| 2176 | /// argument of the build method itself had been used, no [Scaffold] would have
|
| 2177 | /// been found, and the [Scaffold.of] function would have returned null.
|
| 2178 | ///
|
| 2179 | /// ```dart
|
| 2180 | /// @override
|
| 2181 | /// Widget build(BuildContext context) {
|
| 2182 | /// // here, Scaffold.of(context) returns null
|
| 2183 | /// return Scaffold(
|
| 2184 | /// appBar: AppBar(title: const Text('Demo')),
|
| 2185 | /// body: Builder(
|
| 2186 | /// builder: (BuildContext context) {
|
| 2187 | /// return TextButton(
|
| 2188 | /// child: const Text('BUTTON'),
|
| 2189 | /// onPressed: () {
|
| 2190 | /// Scaffold.of(context).showBottomSheet(
|
| 2191 | /// (BuildContext context) {
|
| 2192 | /// return Container(
|
| 2193 | /// alignment: Alignment.center,
|
| 2194 | /// height: 200,
|
| 2195 | /// color: Colors.amber,
|
| 2196 | /// child: Center(
|
| 2197 | /// child: Column(
|
| 2198 | /// mainAxisSize: MainAxisSize.min,
|
| 2199 | /// children: <Widget>[
|
| 2200 | /// const Text('BottomSheet'),
|
| 2201 | /// ElevatedButton(
|
| 2202 | /// child: const Text('Close BottomSheet'),
|
| 2203 | /// onPressed: () {
|
| 2204 | /// Navigator.pop(context);
|
| 2205 | /// },
|
| 2206 | /// )
|
| 2207 | /// ],
|
| 2208 | /// ),
|
| 2209 | /// ),
|
| 2210 | /// );
|
| 2211 | /// },
|
| 2212 | /// );
|
| 2213 | /// },
|
| 2214 | /// );
|
| 2215 | /// },
|
| 2216 | /// )
|
| 2217 | /// );
|
| 2218 | /// }
|
| 2219 | /// ```
|
| 2220 | ///
|
| 2221 | /// The [BuildContext] for a particular widget can change location over time as
|
| 2222 | /// the widget is moved around the tree. Because of this, values returned from
|
| 2223 | /// the methods on this class should not be cached beyond the execution of a
|
| 2224 | /// single synchronous function.
|
| 2225 | ///
|
| 2226 | /// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g}
|
| 2227 | ///
|
| 2228 | /// Avoid storing instances of [BuildContext]s because they may become invalid
|
| 2229 | /// if the widget they are associated with is unmounted from the widget tree.
|
| 2230 | /// {@template flutter.widgets.BuildContext.asynchronous_gap}
|
| 2231 | /// If a [BuildContext] is used across an asynchronous gap (i.e. after performing
|
| 2232 | /// an asynchronous operation), consider checking [mounted] to determine whether
|
| 2233 | /// the context is still valid before interacting with it:
|
| 2234 | ///
|
| 2235 | /// ```dart
|
| 2236 | /// @override
|
| 2237 | /// Widget build(BuildContext context) {
|
| 2238 | /// return OutlinedButton(
|
| 2239 | /// onPressed: () async {
|
| 2240 | /// await Future<void>.delayed(const Duration(seconds: 1));
|
| 2241 | /// if (context.mounted) {
|
| 2242 | /// Navigator.of(context).pop();
|
| 2243 | /// }
|
| 2244 | /// },
|
| 2245 | /// child: const Text('Delayed pop'),
|
| 2246 | /// );
|
| 2247 | /// }
|
| 2248 | /// ```
|
| 2249 | /// {@endtemplate}
|
| 2250 | ///
|
| 2251 | /// [BuildContext] objects are actually [Element] objects. The [BuildContext]
|
| 2252 | /// interface is used to discourage direct manipulation of [Element] objects.
|
| 2253 | abstract class BuildContext {
|
| 2254 | /// The current configuration of the [Element] that is this [BuildContext].
|
| 2255 | Widget get widget;
|
| 2256 |
|
| 2257 | /// The [BuildOwner] for this context. The [BuildOwner] is in charge of
|
| 2258 | /// managing the rendering pipeline for this context.
|
| 2259 | BuildOwner? get owner;
|
| 2260 |
|
| 2261 | /// Whether the [Widget] this context is associated with is currently
|
| 2262 | /// mounted in the widget tree.
|
| 2263 | ///
|
| 2264 | /// Accessing the properties of the [BuildContext] or calling any methods on
|
| 2265 | /// it is only valid while mounted is true. If mounted is false, assertions
|
| 2266 | /// will trigger.
|
| 2267 | ///
|
| 2268 | /// Once unmounted, a given [BuildContext] will never become mounted again.
|
| 2269 | ///
|
| 2270 | /// {@macro flutter.widgets.BuildContext.asynchronous_gap}
|
| 2271 | bool get mounted;
|
| 2272 |
|
| 2273 | /// Whether the [widget] is currently updating the widget or render tree.
|
| 2274 | ///
|
| 2275 | /// For [StatefulWidget]s and [StatelessWidget]s this flag is true while
|
| 2276 | /// their respective build methods are executing.
|
| 2277 | /// [RenderObjectWidget]s set this to true while creating or configuring their
|
| 2278 | /// associated [RenderObject]s.
|
| 2279 | /// Other [Widget] types may set this to true for conceptually similar phases
|
| 2280 | /// of their lifecycle.
|
| 2281 | ///
|
| 2282 | /// When this is true, it is safe for [widget] to establish a dependency to an
|
| 2283 | /// [InheritedWidget] by calling [dependOnInheritedElement] or
|
| 2284 | /// [dependOnInheritedWidgetOfExactType].
|
| 2285 | ///
|
| 2286 | /// Accessing this flag in release mode is not valid.
|
| 2287 | bool get debugDoingBuild;
|
| 2288 |
|
| 2289 | /// The current [RenderObject] for the widget. If the widget is a
|
| 2290 | /// [RenderObjectWidget], this is the render object that the widget created
|
| 2291 | /// for itself. Otherwise, it is the render object of the first descendant
|
| 2292 | /// [RenderObjectWidget].
|
| 2293 | ///
|
| 2294 | /// This method will only return a valid result after the build phase is
|
| 2295 | /// complete. It is therefore not valid to call this from a build method.
|
| 2296 | /// It should only be called from interaction event handlers (e.g.
|
| 2297 | /// gesture callbacks) or layout or paint callbacks. It is also not valid to
|
| 2298 | /// call if [State.mounted] returns false.
|
| 2299 | ///
|
| 2300 | /// If the render object is a [RenderBox], which is the common case, then the
|
| 2301 | /// size of the render object can be obtained from the [size] getter. This is
|
| 2302 | /// only valid after the layout phase, and should therefore only be examined
|
| 2303 | /// from paint callbacks or interaction event handlers (e.g. gesture
|
| 2304 | /// callbacks).
|
| 2305 | ///
|
| 2306 | /// For details on the different phases of a frame, see the discussion at
|
| 2307 | /// [WidgetsBinding.drawFrame].
|
| 2308 | ///
|
| 2309 | /// Calling this method is theoretically relatively expensive (O(N) in the
|
| 2310 | /// depth of the tree), but in practice is usually cheap because the tree
|
| 2311 | /// usually has many render objects and therefore the distance to the nearest
|
| 2312 | /// render object is usually short.
|
| 2313 | RenderObject? findRenderObject();
|
| 2314 |
|
| 2315 | /// The size of the [RenderBox] returned by [findRenderObject].
|
| 2316 | ///
|
| 2317 | /// This getter will only return a valid result after the layout phase is
|
| 2318 | /// complete. It is therefore not valid to call this from a build method.
|
| 2319 | /// It should only be called from paint callbacks or interaction event
|
| 2320 | /// handlers (e.g. gesture callbacks).
|
| 2321 | ///
|
| 2322 | /// For details on the different phases of a frame, see the discussion at
|
| 2323 | /// [WidgetsBinding.drawFrame].
|
| 2324 | ///
|
| 2325 | /// This getter will only return a valid result if [findRenderObject] actually
|
| 2326 | /// returns a [RenderBox]. If [findRenderObject] returns a render object that
|
| 2327 | /// is not a subtype of [RenderBox] (e.g., [RenderView]), this getter will
|
| 2328 | /// throw an exception in debug mode and will return null in release mode.
|
| 2329 | ///
|
| 2330 | /// Calling this getter is theoretically relatively expensive (O(N) in the
|
| 2331 | /// depth of the tree), but in practice is usually cheap because the tree
|
| 2332 | /// usually has many render objects and therefore the distance to the nearest
|
| 2333 | /// render object is usually short.
|
| 2334 | Size? get size;
|
| 2335 |
|
| 2336 | /// Registers this build context with [ancestor] such that when
|
| 2337 | /// [ancestor]'s widget changes this build context is rebuilt.
|
| 2338 | ///
|
| 2339 | /// Returns `ancestor.widget`.
|
| 2340 | ///
|
| 2341 | /// This method is rarely called directly. Most applications should use
|
| 2342 | /// [dependOnInheritedWidgetOfExactType], which calls this method after finding
|
| 2343 | /// the appropriate [InheritedElement] ancestor.
|
| 2344 | ///
|
| 2345 | /// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can
|
| 2346 | /// be called apply to this method as well.
|
| 2347 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect});
|
| 2348 |
|
| 2349 | /// Returns the nearest widget of the given type `T` and creates a dependency
|
| 2350 | /// on it, or null if no appropriate widget is found.
|
| 2351 | ///
|
| 2352 | /// The widget found will be a concrete [InheritedWidget] subclass, and
|
| 2353 | /// calling [dependOnInheritedWidgetOfExactType] registers this build context
|
| 2354 | /// with the returned widget. When that widget changes (or a new widget of
|
| 2355 | /// that type is introduced, or the widget goes away), this build context is
|
| 2356 | /// rebuilt so that it can obtain new values from that widget.
|
| 2357 | ///
|
| 2358 | /// {@template flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType}
|
| 2359 | /// This is typically called implicitly from `of()` static methods, e.g.
|
| 2360 | /// [Theme.of].
|
| 2361 | ///
|
| 2362 | /// This method should not be called from widget constructors or from
|
| 2363 | /// [State.initState] methods, because those methods would not get called
|
| 2364 | /// again if the inherited value were to change. To ensure that the widget
|
| 2365 | /// correctly updates itself when the inherited value changes, only call this
|
| 2366 | /// (directly or indirectly) from build methods, layout and paint callbacks,
|
| 2367 | /// or from [State.didChangeDependencies] (which is called immediately after
|
| 2368 | /// [State.initState]).
|
| 2369 | ///
|
| 2370 | /// This method should not be called from [State.dispose] because the element
|
| 2371 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
| 2372 | /// method, save a reference to the ancestor in [State.didChangeDependencies].
|
| 2373 | /// It is safe to use this method from [State.deactivate], which is called
|
| 2374 | /// whenever the widget is removed from the tree.
|
| 2375 | ///
|
| 2376 | /// It is also possible to call this method from interaction event handlers
|
| 2377 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as
|
| 2378 | /// that value is not cached and/or reused later.
|
| 2379 | ///
|
| 2380 | /// Calling this method is O(1) with a small constant factor, but will lead to
|
| 2381 | /// the widget being rebuilt more often.
|
| 2382 | ///
|
| 2383 | /// Once a widget registers a dependency on a particular type by calling this
|
| 2384 | /// method, it will be rebuilt, and [State.didChangeDependencies] will be
|
| 2385 | /// called, whenever changes occur relating to that widget until the next time
|
| 2386 | /// the widget or one of its ancestors is moved (for example, because an
|
| 2387 | /// ancestor is added or removed).
|
| 2388 | ///
|
| 2389 | /// The [aspect] parameter is only used when `T` is an
|
| 2390 | /// [InheritedWidget] subclasses that supports partial updates, like
|
| 2391 | /// [InheritedModel]. It specifies what "aspect" of the inherited
|
| 2392 | /// widget this context depends on.
|
| 2393 | /// {@endtemplate}
|
| 2394 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect});
|
| 2395 |
|
| 2396 | /// Returns the nearest widget of the given [InheritedWidget] subclass `T` or
|
| 2397 | /// null if an appropriate ancestor is not found.
|
| 2398 | ///
|
| 2399 | /// This method does not introduce a dependency the way that the more typical
|
| 2400 | /// [dependOnInheritedWidgetOfExactType] does, so this context will not be
|
| 2401 | /// rebuilt if the [InheritedWidget] changes. This function is meant for those
|
| 2402 | /// uncommon use cases where a dependency is undesirable.
|
| 2403 | ///
|
| 2404 | /// This method should not be called from [State.dispose] because the element
|
| 2405 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
| 2406 | /// method, save a reference to the ancestor in [State.didChangeDependencies].
|
| 2407 | /// It is safe to use this method from [State.deactivate], which is called
|
| 2408 | /// whenever the widget is removed from the tree.
|
| 2409 | ///
|
| 2410 | /// It is also possible to call this method from interaction event handlers
|
| 2411 | /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as
|
| 2412 | /// that value is not cached and/or reused later.
|
| 2413 | ///
|
| 2414 | /// Calling this method is O(1) with a small constant factor.
|
| 2415 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>();
|
| 2416 |
|
| 2417 | /// Obtains the element corresponding to the nearest widget of the given type `T`,
|
| 2418 | /// which must be the type of a concrete [InheritedWidget] subclass.
|
| 2419 | ///
|
| 2420 | /// Returns null if no such element is found.
|
| 2421 | ///
|
| 2422 | /// {@template flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType}
|
| 2423 | /// Calling this method is O(1) with a small constant factor.
|
| 2424 | ///
|
| 2425 | /// This method does not establish a relationship with the target in the way
|
| 2426 | /// that [dependOnInheritedWidgetOfExactType] does.
|
| 2427 | ///
|
| 2428 | /// This method should not be called from [State.dispose] because the element
|
| 2429 | /// tree is no longer stable at that time. To refer to an ancestor from that
|
| 2430 | /// method, save a reference to the ancestor by calling
|
| 2431 | /// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is
|
| 2432 | /// safe to use this method from [State.deactivate], which is called whenever
|
| 2433 | /// the widget is removed from the tree.
|
| 2434 | /// {@endtemplate}
|
| 2435 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
|
| 2436 |
|
| 2437 | /// Returns the nearest ancestor widget of the given type `T`, which must be the
|
| 2438 | /// type of a concrete [Widget] subclass.
|
| 2439 | ///
|
| 2440 | /// {@template flutter.widgets.BuildContext.findAncestorWidgetOfExactType}
|
| 2441 | /// In general, [dependOnInheritedWidgetOfExactType] is more useful, since
|
| 2442 | /// inherited widgets will trigger consumers to rebuild when they change. This
|
| 2443 | /// method is appropriate when used in interaction event handlers (e.g.
|
| 2444 | /// gesture callbacks) or for performing one-off tasks such as asserting that
|
| 2445 | /// you have or don't have a widget of a specific type as an ancestor. The
|
| 2446 | /// return value of a Widget's build method should not depend on the value
|
| 2447 | /// returned by this method, because the build context will not rebuild if the
|
| 2448 | /// return value of this method changes. This could lead to a situation where
|
| 2449 | /// data used in the build method changes, but the widget is not rebuilt.
|
| 2450 | ///
|
| 2451 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
| 2452 | /// tree). Only call this method if the distance from this widget to the
|
| 2453 | /// desired ancestor is known to be small and bounded.
|
| 2454 | ///
|
| 2455 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
| 2456 | /// because the widget tree is no longer stable at that time. To refer to
|
| 2457 | /// an ancestor from one of those methods, save a reference to the ancestor
|
| 2458 | /// by calling [findAncestorWidgetOfExactType] in [State.didChangeDependencies].
|
| 2459 | ///
|
| 2460 | /// Returns null if a widget of the requested type does not appear in the
|
| 2461 | /// ancestors of this context.
|
| 2462 | /// {@endtemplate}
|
| 2463 | T? findAncestorWidgetOfExactType<T extends Widget>();
|
| 2464 |
|
| 2465 | /// Returns the [State] object of the nearest ancestor [StatefulWidget] widget
|
| 2466 | /// that is an instance of the given type `T`.
|
| 2467 | ///
|
| 2468 | /// {@template flutter.widgets.BuildContext.findAncestorStateOfType}
|
| 2469 | /// This should not be used from build methods, because the build context will
|
| 2470 | /// not be rebuilt if the value that would be returned by this method changes.
|
| 2471 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
| 2472 | /// cases. This method is useful for changing the state of an ancestor widget in
|
| 2473 | /// a one-off manner, for example, to cause an ancestor scrolling list to
|
| 2474 | /// scroll this build context's widget into view, or to move the focus in
|
| 2475 | /// response to user interaction.
|
| 2476 | ///
|
| 2477 | /// In general, though, consider using a callback that triggers a stateful
|
| 2478 | /// change in the ancestor rather than using the imperative style implied by
|
| 2479 | /// this method. This will usually lead to more maintainable and reusable code
|
| 2480 | /// since it decouples widgets from each other.
|
| 2481 | ///
|
| 2482 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
| 2483 | /// tree). Only call this method if the distance from this widget to the
|
| 2484 | /// desired ancestor is known to be small and bounded.
|
| 2485 | ///
|
| 2486 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
| 2487 | /// because the widget tree is no longer stable at that time. To refer to
|
| 2488 | /// an ancestor from one of those methods, save a reference to the ancestor
|
| 2489 | /// by calling [findAncestorStateOfType] in [State.didChangeDependencies].
|
| 2490 | /// {@endtemplate}
|
| 2491 | ///
|
| 2492 | /// {@tool snippet}
|
| 2493 | ///
|
| 2494 | /// ```dart
|
| 2495 | /// ScrollableState? scrollable = context.findAncestorStateOfType<ScrollableState>();
|
| 2496 | /// ```
|
| 2497 | /// {@end-tool}
|
| 2498 | T? findAncestorStateOfType<T extends State>();
|
| 2499 |
|
| 2500 | /// Returns the [State] object of the furthest ancestor [StatefulWidget] widget
|
| 2501 | /// that is an instance of the given type `T`.
|
| 2502 | ///
|
| 2503 | /// {@template flutter.widgets.BuildContext.findRootAncestorStateOfType}
|
| 2504 | /// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent
|
| 2505 | /// ancestors until there are none of the type instance of `T` remaining.
|
| 2506 | /// Then returns the last one found.
|
| 2507 | ///
|
| 2508 | /// This operation is O(N) as well though N is the entire widget tree rather than
|
| 2509 | /// a subtree.
|
| 2510 | /// {@endtemplate}
|
| 2511 | T? findRootAncestorStateOfType<T extends State>();
|
| 2512 |
|
| 2513 | /// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget
|
| 2514 | /// that is an instance of the given type `T`.
|
| 2515 | ///
|
| 2516 | /// {@template flutter.widgets.BuildContext.findAncestorRenderObjectOfType}
|
| 2517 | /// This should not be used from build methods, because the build context will
|
| 2518 | /// not be rebuilt if the value that would be returned by this method changes.
|
| 2519 | /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such
|
| 2520 | /// cases. This method is useful only in esoteric cases where a widget needs
|
| 2521 | /// to cause an ancestor to change its layout or paint behavior. For example,
|
| 2522 | /// it is used by [Material] so that [InkWell] widgets can trigger the ink
|
| 2523 | /// splash on the [Material]'s actual render object.
|
| 2524 | ///
|
| 2525 | /// Calling this method is relatively expensive (O(N) in the depth of the
|
| 2526 | /// tree). Only call this method if the distance from this widget to the
|
| 2527 | /// desired ancestor is known to be small and bounded.
|
| 2528 | ///
|
| 2529 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
| 2530 | /// because the widget tree is no longer stable at that time. To refer to
|
| 2531 | /// an ancestor from one of those methods, save a reference to the ancestor
|
| 2532 | /// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies].
|
| 2533 | /// {@endtemplate}
|
| 2534 | T? findAncestorRenderObjectOfType<T extends RenderObject>();
|
| 2535 |
|
| 2536 | /// Walks the ancestor chain, starting with the parent of this build context's
|
| 2537 | /// widget, invoking the argument for each ancestor.
|
| 2538 | ///
|
| 2539 | /// {@template flutter.widgets.BuildContext.visitAncestorElements}
|
| 2540 | /// The callback is given a reference to the ancestor widget's corresponding
|
| 2541 | /// [Element] object. The walk stops when it reaches the root widget or when
|
| 2542 | /// the callback returns false. The callback must not return null.
|
| 2543 | ///
|
| 2544 | /// This is useful for inspecting the widget tree.
|
| 2545 | ///
|
| 2546 | /// Calling this method is relatively expensive (O(N) in the depth of the tree).
|
| 2547 | ///
|
| 2548 | /// This method should not be called from [State.deactivate] or [State.dispose]
|
| 2549 | /// because the element tree is no longer stable at that time. To refer to
|
| 2550 | /// an ancestor from one of those methods, save a reference to the ancestor
|
| 2551 | /// by calling [visitAncestorElements] in [State.didChangeDependencies].
|
| 2552 | /// {@endtemplate}
|
| 2553 | void visitAncestorElements(ConditionalElementVisitor visitor);
|
| 2554 |
|
| 2555 | /// Walks the children of this widget.
|
| 2556 | ///
|
| 2557 | /// {@template flutter.widgets.BuildContext.visitChildElements}
|
| 2558 | /// This is useful for applying changes to children after they are built
|
| 2559 | /// without waiting for the next frame, especially if the children are known,
|
| 2560 | /// and especially if there is exactly one child (as is always the case for
|
| 2561 | /// [StatefulWidget]s or [StatelessWidget]s).
|
| 2562 | ///
|
| 2563 | /// Calling this method is very cheap for build contexts that correspond to
|
| 2564 | /// [StatefulWidget]s or [StatelessWidget]s (O(1), since there's only one
|
| 2565 | /// child).
|
| 2566 | ///
|
| 2567 | /// Calling this method is potentially expensive for build contexts that
|
| 2568 | /// correspond to [RenderObjectWidget]s (O(N) in the number of children).
|
| 2569 | ///
|
| 2570 | /// Calling this method recursively is extremely expensive (O(N) in the number
|
| 2571 | /// of descendants), and should be avoided if possible. Generally it is
|
| 2572 | /// significantly cheaper to use an [InheritedWidget] and have the descendants
|
| 2573 | /// pull data down, than it is to use [visitChildElements] recursively to push
|
| 2574 | /// data down to them.
|
| 2575 | /// {@endtemplate}
|
| 2576 | void visitChildElements(ElementVisitor visitor);
|
| 2577 |
|
| 2578 | /// Start bubbling this notification at the given build context.
|
| 2579 | ///
|
| 2580 | /// The notification will be delivered to any [NotificationListener] widgets
|
| 2581 | /// with the appropriate type parameters that are ancestors of the given
|
| 2582 | /// [BuildContext].
|
| 2583 | void dispatchNotification(Notification notification);
|
| 2584 |
|
| 2585 | /// Returns a description of the [Element] associated with the current build context.
|
| 2586 | ///
|
| 2587 | /// The `name` is typically something like "The element being rebuilt was".
|
| 2588 | ///
|
| 2589 | /// See also:
|
| 2590 | ///
|
| 2591 | /// * [Element.describeElements], which can be used to describe a list of elements.
|
| 2592 | DiagnosticsNode describeElement(
|
| 2593 | String name, {
|
| 2594 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty,
|
| 2595 | });
|
| 2596 |
|
| 2597 | /// Returns a description of the [Widget] associated with the current build context.
|
| 2598 | ///
|
| 2599 | /// The `name` is typically something like "The widget being rebuilt was".
|
| 2600 | DiagnosticsNode describeWidget(
|
| 2601 | String name, {
|
| 2602 | DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty,
|
| 2603 | });
|
| 2604 |
|
| 2605 | /// Adds a description of a specific type of widget missing from the current
|
| 2606 | /// build context's ancestry tree.
|
| 2607 | ///
|
| 2608 | /// You can find an example of using this method in [debugCheckHasMaterial].
|
| 2609 | List<DiagnosticsNode> describeMissingAncestor({required Type expectedAncestorType});
|
| 2610 |
|
| 2611 | /// Adds a description of the ownership chain from a specific [Element]
|
| 2612 | /// to the error report.
|
| 2613 | ///
|
| 2614 | /// The ownership chain is useful for debugging the source of an element.
|
| 2615 | DiagnosticsNode describeOwnershipChain(String name);
|
| 2616 | }
|
| 2617 |
|
| 2618 | /// A class that determines the scope of a [BuildOwner.buildScope] operation.
|
| 2619 | ///
|
| 2620 | /// The [BuildOwner.buildScope] method rebuilds all dirty [Element]s who share
|
| 2621 | /// the same [Element.buildScope] as its `context` argument, and skips those
|
| 2622 | /// with a different [Element.buildScope].
|
| 2623 | ///
|
| 2624 | /// [Element]s by default have the same `buildScope` as their parents. Special
|
| 2625 | /// [Element]s may override [Element.buildScope] to create an isolated build scope
|
| 2626 | /// for its descendants. The [LayoutBuilder] widget, for example, establishes its
|
| 2627 | /// own [BuildScope] such that no descendant [Element]s may rebuild prematurely
|
| 2628 | /// until the incoming constraints are known.
|
| 2629 | final class BuildScope {
|
| 2630 | /// Creates a [BuildScope] with an optional [scheduleRebuild] callback.
|
| 2631 | BuildScope({this.scheduleRebuild});
|
| 2632 |
|
| 2633 | // Whether `scheduleRebuild` is called.
|
| 2634 | bool _buildScheduled = false;
|
| 2635 | // Whether [BuildOwner.buildScope] is actively running in this [BuildScope].
|
| 2636 | bool _building = false;
|
| 2637 |
|
| 2638 | /// An optional [VoidCallback] that will be called when [Element]s in this
|
| 2639 | /// [BuildScope] are marked as dirty for the first time.
|
| 2640 | ///
|
| 2641 | /// This callback usually signifies that the [BuildOwner.buildScope] method
|
| 2642 | /// must be called at a later time in this frame to rebuild dirty elements in
|
| 2643 | /// this [BuildScope]. It will **not** be called if this scope is actively being
|
| 2644 | /// built by [BuildOwner.buildScope], since the [BuildScope] will be clean when
|
| 2645 | /// [BuildOwner.buildScope] returns.
|
| 2646 | final VoidCallback? scheduleRebuild;
|
| 2647 |
|
| 2648 | /// Whether [_dirtyElements] need to be sorted again as a result of more
|
| 2649 | /// elements becoming dirty during the build.
|
| 2650 | ///
|
| 2651 | /// This is necessary to preserve the sort order defined by [Element._sort].
|
| 2652 | ///
|
| 2653 | /// This field is set to null when [BuildOwner.buildScope] is not actively
|
| 2654 | /// rebuilding the widget tree.
|
| 2655 | bool? _dirtyElementsNeedsResorting;
|
| 2656 | final List<Element> _dirtyElements = <Element>[];
|
| 2657 |
|
| 2658 | @pragma('dart2js:tryInline' )
|
| 2659 | @pragma('vm:prefer-inline' )
|
| 2660 | @pragma('wasm:prefer-inline' )
|
| 2661 | void _scheduleBuildFor(Element element) {
|
| 2662 | assert(identical(element.buildScope, this));
|
| 2663 | if (!element._inDirtyList) {
|
| 2664 | _dirtyElements.add(element);
|
| 2665 | element._inDirtyList = true;
|
| 2666 | }
|
| 2667 | if (!_buildScheduled && !_building) {
|
| 2668 | _buildScheduled = true;
|
| 2669 | scheduleRebuild?.call();
|
| 2670 | }
|
| 2671 | if (_dirtyElementsNeedsResorting != null) {
|
| 2672 | _dirtyElementsNeedsResorting = true;
|
| 2673 | }
|
| 2674 | }
|
| 2675 |
|
| 2676 | @pragma('dart2js:tryInline' )
|
| 2677 | @pragma('vm:prefer-inline' )
|
| 2678 | @pragma('wasm:prefer-inline' )
|
| 2679 | @pragma('vm:notify-debugger-on-exception' )
|
| 2680 | void _tryRebuild(Element element) {
|
| 2681 | assert(element._inDirtyList);
|
| 2682 | assert(identical(element.buildScope, this));
|
| 2683 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(element.widget);
|
| 2684 | if (isTimelineTracked) {
|
| 2685 | Map<String, String>? debugTimelineArguments;
|
| 2686 | assert(() {
|
| 2687 | if (kDebugMode && debugEnhanceBuildTimelineArguments) {
|
| 2688 | debugTimelineArguments = element.widget.toDiagnosticsNode().toTimelineArguments();
|
| 2689 | }
|
| 2690 | return true;
|
| 2691 | }());
|
| 2692 | FlutterTimeline.startSync(' ${element.widget.runtimeType}' , arguments: debugTimelineArguments);
|
| 2693 | }
|
| 2694 | try {
|
| 2695 | element.rebuild();
|
| 2696 | } catch (e, stack) {
|
| 2697 | _reportException(
|
| 2698 | ErrorDescription('while rebuilding dirty elements' ),
|
| 2699 | e,
|
| 2700 | stack,
|
| 2701 | informationCollector: () => <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 = kDebugMode
|
| 3109 | ? <Element, Map<Element, GlobalKey>>{}
|
| 3110 | : null;
|
| 3111 |
|
| 3112 | /// The number of [GlobalKey] instances that are currently associated with
|
| 3113 | /// [Element]s that have been built by this build owner.
|
| 3114 | int get globalKeyCount => _globalKeyRegistry.length;
|
| 3115 |
|
| 3116 | void _debugRemoveGlobalKeyReservationFor(Element parent, Element child) {
|
| 3117 | assert(() {
|
| 3118 | _debugGlobalKeyReservations?[parent]?.remove(child);
|
| 3119 | return true;
|
| 3120 | }());
|
| 3121 | }
|
| 3122 |
|
| 3123 | void _registerGlobalKey(GlobalKey key, Element element) {
|
| 3124 | assert(() {
|
| 3125 | if (_globalKeyRegistry.containsKey(key)) {
|
| 3126 | final Element oldElement = _globalKeyRegistry[key]!;
|
| 3127 | assert(element.widget.runtimeType != oldElement.widget.runtimeType);
|
| 3128 | _debugIllFatedElements?.add(oldElement);
|
| 3129 | }
|
| 3130 | return true;
|
| 3131 | }());
|
| 3132 | _globalKeyRegistry[key] = element;
|
| 3133 | }
|
| 3134 |
|
| 3135 | void _unregisterGlobalKey(GlobalKey key, Element element) {
|
| 3136 | assert(() {
|
| 3137 | if (_globalKeyRegistry.containsKey(key) && _globalKeyRegistry[key] != element) {
|
| 3138 | final Element oldElement = _globalKeyRegistry[key]!;
|
| 3139 | assert(element.widget.runtimeType != oldElement.widget.runtimeType);
|
| 3140 | }
|
| 3141 | return true;
|
| 3142 | }());
|
| 3143 | if (_globalKeyRegistry[key] == element) {
|
| 3144 | _globalKeyRegistry.remove(key);
|
| 3145 | }
|
| 3146 | }
|
| 3147 |
|
| 3148 | void _debugReserveGlobalKeyFor(Element parent, Element child, GlobalKey key) {
|
| 3149 | assert(() {
|
| 3150 | _debugGlobalKeyReservations?[parent] ??= <Element, GlobalKey>{};
|
| 3151 | _debugGlobalKeyReservations?[parent]![child] = key;
|
| 3152 | return true;
|
| 3153 | }());
|
| 3154 | }
|
| 3155 |
|
| 3156 | void _debugVerifyGlobalKeyReservation() {
|
| 3157 | assert(() {
|
| 3158 | final Map<GlobalKey, Element> keyToParent = <GlobalKey, Element>{};
|
| 3159 | _debugGlobalKeyReservations?.forEach((Element parent, Map<Element, GlobalKey> childToKey) {
|
| 3160 | // We ignore parent that are unmounted or detached.
|
| 3161 | if (parent._lifecycleState == _ElementLifecycle.defunct ||
|
| 3162 | parent.renderObject?.attached == false) {
|
| 3163 | return;
|
| 3164 | }
|
| 3165 | childToKey.forEach((Element child, GlobalKey key) {
|
| 3166 | // If parent = null, the node is deactivated by its parent and is
|
| 3167 | // not re-attached to other part of the tree. We should ignore this
|
| 3168 | // node.
|
| 3169 | if (child._parent == null) {
|
| 3170 | return;
|
| 3171 | }
|
| 3172 | // It is possible the same key registers to the same parent twice
|
| 3173 | // with different children. That is illegal, but it is not in the
|
| 3174 | // scope of this check. Such error will be detected in
|
| 3175 | // _debugVerifyIllFatedPopulation or
|
| 3176 | // _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans.
|
| 3177 | if (keyToParent.containsKey(key) && keyToParent[key] != parent) {
|
| 3178 | // We have duplication reservations for the same global key.
|
| 3179 | final Element older = keyToParent[key]!;
|
| 3180 | final Element newer = parent;
|
| 3181 | final FlutterError error;
|
| 3182 | if (older.toString() != newer.toString()) {
|
| 3183 | error = FlutterError.fromParts(<DiagnosticsNode>[
|
| 3184 | ErrorSummary('Multiple widgets used the same GlobalKey.' ),
|
| 3185 | ErrorDescription(
|
| 3186 | 'The key $key was used by multiple widgets. The parents of those widgets were:\n'
|
| 3187 | '- $older\n'
|
| 3188 | '- $newer\n'
|
| 3189 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
| 3190 | ),
|
| 3191 | ]);
|
| 3192 | } else {
|
| 3193 | error = FlutterError.fromParts(<DiagnosticsNode>[
|
| 3194 | ErrorSummary('Multiple widgets used the same GlobalKey.' ),
|
| 3195 | ErrorDescription(
|
| 3196 | 'The key $key was used by multiple widgets. The parents of those widgets were '
|
| 3197 | 'different widgets that both had the following description:\n'
|
| 3198 | ' $parent\n'
|
| 3199 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
| 3200 | ),
|
| 3201 | ]);
|
| 3202 | }
|
| 3203 | // Fix the tree by removing the duplicated child from one of its
|
| 3204 | // parents to resolve the duplicated key issue. This allows us to
|
| 3205 | // tear down the tree during testing without producing additional
|
| 3206 | // misleading exceptions.
|
| 3207 | if (child._parent != older) {
|
| 3208 | older.visitChildren((Element currentChild) {
|
| 3209 | if (currentChild == child) {
|
| 3210 | older.forgetChild(child);
|
| 3211 | }
|
| 3212 | });
|
| 3213 | }
|
| 3214 | if (child._parent != newer) {
|
| 3215 | newer.visitChildren((Element currentChild) {
|
| 3216 | if (currentChild == child) {
|
| 3217 | newer.forgetChild(child);
|
| 3218 | }
|
| 3219 | });
|
| 3220 | }
|
| 3221 | throw error;
|
| 3222 | } else {
|
| 3223 | keyToParent[key] = parent;
|
| 3224 | }
|
| 3225 | });
|
| 3226 | });
|
| 3227 | _debugGlobalKeyReservations?.clear();
|
| 3228 | return true;
|
| 3229 | }());
|
| 3230 | }
|
| 3231 |
|
| 3232 | void _debugVerifyIllFatedPopulation() {
|
| 3233 | assert(() {
|
| 3234 | Map<GlobalKey, Set<Element>>? duplicates;
|
| 3235 | for (final Element element in _debugIllFatedElements ?? const <Element>{}) {
|
| 3236 | if (element._lifecycleState != _ElementLifecycle.defunct) {
|
| 3237 | assert(element.widget.key != null);
|
| 3238 | final GlobalKey key = element.widget.key! as GlobalKey;
|
| 3239 | assert(_globalKeyRegistry.containsKey(key));
|
| 3240 | duplicates ??= <GlobalKey, Set<Element>>{};
|
| 3241 | // Uses ordered set to produce consistent error message.
|
| 3242 | final Set<Element> elements = duplicates.putIfAbsent(key, () => <Element>{});
|
| 3243 | elements.add(element);
|
| 3244 | elements.add(_globalKeyRegistry[key]!);
|
| 3245 | }
|
| 3246 | }
|
| 3247 | _debugIllFatedElements?.clear();
|
| 3248 | if (duplicates != null) {
|
| 3249 | final List<DiagnosticsNode> information = <DiagnosticsNode>[];
|
| 3250 | information.add(ErrorSummary('Multiple widgets used the same GlobalKey.' ));
|
| 3251 | for (final GlobalKey key in duplicates.keys) {
|
| 3252 | final Set<Element> elements = duplicates[key]!;
|
| 3253 | // TODO(jacobr): this will omit the '- ' before each widget name and
|
| 3254 | // use the more standard whitespace style instead. Please let me know
|
| 3255 | // if the '- ' style is a feature we want to maintain and we can add
|
| 3256 | // another tree style that supports it. I also see '* ' in some places
|
| 3257 | // so it would be nice to unify and normalize.
|
| 3258 | information.add(
|
| 3259 | Element.describeElements(
|
| 3260 | 'The key $key was used by ${elements.length} widgets' ,
|
| 3261 | elements,
|
| 3262 | ),
|
| 3263 | );
|
| 3264 | }
|
| 3265 | information.add(
|
| 3266 | ErrorDescription(
|
| 3267 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
| 3268 | ),
|
| 3269 | );
|
| 3270 | throw FlutterError.fromParts(information);
|
| 3271 | }
|
| 3272 | return true;
|
| 3273 | }());
|
| 3274 | }
|
| 3275 |
|
| 3276 | /// Complete the element build pass by unmounting any elements that are no
|
| 3277 | /// longer active.
|
| 3278 | ///
|
| 3279 | /// This is called by [WidgetsBinding.drawFrame].
|
| 3280 | ///
|
| 3281 | /// In debug mode, this also runs some sanity checks, for example checking for
|
| 3282 | /// duplicate global keys.
|
| 3283 | @pragma('vm:notify-debugger-on-exception' )
|
| 3284 | void finalizeTree() {
|
| 3285 | if (!kReleaseMode) {
|
| 3286 | FlutterTimeline.startSync('FINALIZE TREE' );
|
| 3287 | }
|
| 3288 | try {
|
| 3289 | lockState(_inactiveElements._unmountAll); // this unregisters the GlobalKeys
|
| 3290 | assert(() {
|
| 3291 | try {
|
| 3292 | _debugVerifyGlobalKeyReservation();
|
| 3293 | _debugVerifyIllFatedPopulation();
|
| 3294 | if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.isNotEmpty ?? false) {
|
| 3295 | final Set<GlobalKey> keys = HashSet<GlobalKey>();
|
| 3296 | for (final Element element
|
| 3297 | in _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys) {
|
| 3298 | if (element._lifecycleState != _ElementLifecycle.defunct) {
|
| 3299 | keys.addAll(
|
| 3300 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans![element]!,
|
| 3301 | );
|
| 3302 | }
|
| 3303 | }
|
| 3304 | if (keys.isNotEmpty) {
|
| 3305 | final Map<String, int> keyStringCount = HashMap<String, int>();
|
| 3306 | for (final String key in keys.map<String>((GlobalKey key) => key.toString())) {
|
| 3307 | if (keyStringCount.containsKey(key)) {
|
| 3308 | keyStringCount.update(key, (int value) => value + 1);
|
| 3309 | } else {
|
| 3310 | keyStringCount[key] = 1;
|
| 3311 | }
|
| 3312 | }
|
| 3313 | final List<String> keyLabels = <String>[
|
| 3314 | for (final MapEntry<String, int>(:String key, value: int count)
|
| 3315 | in keyStringCount.entries)
|
| 3316 | if (count == 1)
|
| 3317 | key
|
| 3318 | else
|
| 3319 | ' $key ( $count different affected keys had this toString representation)' ,
|
| 3320 | ];
|
| 3321 | final Iterable<Element> elements =
|
| 3322 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys;
|
| 3323 | final Map<String, int> elementStringCount = HashMap<String, int>();
|
| 3324 | for (final String element in elements.map<String>(
|
| 3325 | (Element element) => element.toString(),
|
| 3326 | )) {
|
| 3327 | if (elementStringCount.containsKey(element)) {
|
| 3328 | elementStringCount.update(element, (int value) => value + 1);
|
| 3329 | } else {
|
| 3330 | elementStringCount[element] = 1;
|
| 3331 | }
|
| 3332 | }
|
| 3333 | final List<String> elementLabels = <String>[
|
| 3334 | for (final MapEntry<String, int>(key: String element, value: int count)
|
| 3335 | in elementStringCount.entries)
|
| 3336 | if (count == 1)
|
| 3337 | element
|
| 3338 | else
|
| 3339 | ' $element ( $count different affected elements had this toString representation)' ,
|
| 3340 | ];
|
| 3341 | assert(keyLabels.isNotEmpty);
|
| 3342 | final String the = keys.length == 1 ? ' the' : '' ;
|
| 3343 | final String s = keys.length == 1 ? '' : 's' ;
|
| 3344 | final String were = keys.length == 1 ? 'was' : 'were' ;
|
| 3345 | final String their = keys.length == 1 ? 'its' : 'their' ;
|
| 3346 | final String respective = elementLabels.length == 1 ? '' : ' respective' ;
|
| 3347 | final String those = keys.length == 1 ? 'that' : 'those' ;
|
| 3348 | final String s2 = elementLabels.length == 1 ? '' : 's' ;
|
| 3349 | final String those2 = elementLabels.length == 1 ? 'that' : 'those' ;
|
| 3350 | final String they = elementLabels.length == 1 ? 'it' : 'they' ;
|
| 3351 | final String think = elementLabels.length == 1 ? 'thinks' : 'think' ;
|
| 3352 | final String are = elementLabels.length == 1 ? 'is' : 'are' ;
|
| 3353 | // TODO(jacobr): make this error more structured to better expose which widgets had problems.
|
| 3354 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 3355 | ErrorSummary('Duplicate GlobalKey $s detected in widget tree.' ),
|
| 3356 | // TODO(jacobr): refactor this code so the elements are clickable
|
| 3357 | // in GUI debug tools.
|
| 3358 | ErrorDescription(
|
| 3359 | 'The following GlobalKey $s $were specified multiple times in the widget tree. This will lead to '
|
| 3360 | 'parts of the widget tree being truncated unexpectedly, because the second time a key is seen, '
|
| 3361 | 'the previous instance is moved to the new location. The key $s $were:\n'
|
| 3362 | '- ${keyLabels.join("\n " )}\n'
|
| 3363 | 'This was determined by noticing that after $the widget $s with the above global key $s $were moved '
|
| 3364 | 'out of $their$respective previous parent $s2, $those2 previous parent $s2 never updated during this frame, meaning '
|
| 3365 | 'that $they either did not update at all or updated before the widget $s $were moved, in either case '
|
| 3366 | 'implying that $they still $think that $they should have a child with $those global key $s.\n'
|
| 3367 | 'The specific parent $s2 that did not update after having one or more children forcibly removed '
|
| 3368 | 'due to GlobalKey reparenting $are:\n'
|
| 3369 | '- ${elementLabels.join("\n " )}'
|
| 3370 | '\nA GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
| 3371 | ),
|
| 3372 | ]);
|
| 3373 | }
|
| 3374 | }
|
| 3375 | } finally {
|
| 3376 | _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear();
|
| 3377 | }
|
| 3378 | return true;
|
| 3379 | }());
|
| 3380 | } catch (e, stack) {
|
| 3381 | // Catching the exception directly to avoid activating the ErrorWidget.
|
| 3382 | // Since the tree is in a broken state, adding the ErrorWidget would
|
| 3383 | // cause more exceptions.
|
| 3384 | _reportException(ErrorSummary('while finalizing the widget tree' ), e, stack);
|
| 3385 | } finally {
|
| 3386 | if (!kReleaseMode) {
|
| 3387 | FlutterTimeline.finishSync();
|
| 3388 | }
|
| 3389 | }
|
| 3390 | }
|
| 3391 |
|
| 3392 | /// Cause the entire subtree rooted at the given [Element] to be entirely
|
| 3393 | /// rebuilt. This is used by development tools when the application code has
|
| 3394 | /// changed and is being hot-reloaded, to cause the widget tree to pick up any
|
| 3395 | /// changed implementations.
|
| 3396 | ///
|
| 3397 | /// This is expensive and should not be called except during development.
|
| 3398 | void reassemble(Element root) {
|
| 3399 | if (!kReleaseMode) {
|
| 3400 | FlutterTimeline.startSync('Preparing Hot Reload (widgets)' );
|
| 3401 | }
|
| 3402 | try {
|
| 3403 | assert(root._parent == null);
|
| 3404 | assert(root.owner == this);
|
| 3405 | root.reassemble();
|
| 3406 | } finally {
|
| 3407 | if (!kReleaseMode) {
|
| 3408 | FlutterTimeline.finishSync();
|
| 3409 | }
|
| 3410 | }
|
| 3411 | }
|
| 3412 | }
|
| 3413 |
|
| 3414 | /// Mixin this class to allow receiving [Notification] objects dispatched by
|
| 3415 | /// child elements.
|
| 3416 | ///
|
| 3417 | /// See also:
|
| 3418 | /// * [NotificationListener], for a widget that allows consuming notifications.
|
| 3419 | mixin NotifiableElementMixin on Element {
|
| 3420 | /// Called when a notification of the appropriate type arrives at this
|
| 3421 | /// location in the tree.
|
| 3422 | ///
|
| 3423 | /// Return true to cancel the notification bubbling. Return false to
|
| 3424 | /// allow the notification to continue to be dispatched to further ancestors.
|
| 3425 | bool onNotification(Notification notification);
|
| 3426 |
|
| 3427 | @override
|
| 3428 | void attachNotificationTree() {
|
| 3429 | _notificationTree = _NotificationNode(_parent?._notificationTree, this);
|
| 3430 | }
|
| 3431 | }
|
| 3432 |
|
| 3433 | class _NotificationNode {
|
| 3434 | _NotificationNode(this.parent, this.current);
|
| 3435 |
|
| 3436 | NotifiableElementMixin? current;
|
| 3437 | _NotificationNode? parent;
|
| 3438 |
|
| 3439 | void dispatchNotification(Notification notification) {
|
| 3440 | if (current?.onNotification(notification) ?? true) {
|
| 3441 | return;
|
| 3442 | }
|
| 3443 | parent?.dispatchNotification(notification);
|
| 3444 | }
|
| 3445 | }
|
| 3446 |
|
| 3447 | bool _isProfileBuildsEnabledFor(Widget widget) {
|
| 3448 | return debugProfileBuildsEnabled ||
|
| 3449 | (debugProfileBuildsEnabledUserWidgets && debugIsWidgetLocalCreation(widget));
|
| 3450 | }
|
| 3451 |
|
| 3452 | /// An instantiation of a [Widget] at a particular location in the tree.
|
| 3453 | ///
|
| 3454 | /// Widgets describe how to configure a subtree but the same widget can be used
|
| 3455 | /// to configure multiple subtrees simultaneously because widgets are immutable.
|
| 3456 | /// An [Element] represents the use of a widget to configure a specific location
|
| 3457 | /// in the tree. Over time, the widget associated with a given element can
|
| 3458 | /// change, for example, if the parent widget rebuilds and creates a new widget
|
| 3459 | /// for this location.
|
| 3460 | ///
|
| 3461 | /// Elements form a tree. Most elements have a unique child, but some widgets
|
| 3462 | /// (e.g., subclasses of [RenderObjectElement]) can have multiple children.
|
| 3463 | ///
|
| 3464 | /// Elements have the following lifecycle:
|
| 3465 | ///
|
| 3466 | /// * The framework creates an element by calling [Widget.createElement] on the
|
| 3467 | /// widget that will be used as the element's initial configuration.
|
| 3468 | /// * The framework calls [mount] to add the newly created element to the tree
|
| 3469 | /// at a given slot in a given parent. The [mount] method is responsible for
|
| 3470 | /// inflating any child widgets and calling [attachRenderObject] as
|
| 3471 | /// necessary to attach any associated render objects to the render tree.
|
| 3472 | /// * At this point, the element is considered "active" and might appear on
|
| 3473 | /// screen.
|
| 3474 | /// * At some point, the parent might decide to change the widget used to
|
| 3475 | /// configure this element, for example because the parent rebuilt with new
|
| 3476 | /// state. When this happens, the framework will call [update] with the new
|
| 3477 | /// widget. The new widget will always have the same [runtimeType] and key as
|
| 3478 | /// old widget. If the parent wishes to change the [runtimeType] or key of
|
| 3479 | /// the widget at this location in the tree, it can do so by unmounting this
|
| 3480 | /// element and inflating the new widget at this location.
|
| 3481 | /// * At some point, an ancestor might decide to remove this element (or an
|
| 3482 | /// intermediate ancestor) from the tree, which the ancestor does by calling
|
| 3483 | /// [deactivateChild] on itself. Deactivating the intermediate ancestor will
|
| 3484 | /// remove that element's render object from the render tree and add this
|
| 3485 | /// element to the [owner]'s list of inactive elements, causing the framework
|
| 3486 | /// to call [deactivate] on this element.
|
| 3487 | /// * At this point, the element is considered "inactive" and will not appear
|
| 3488 | /// on screen. An element can remain in the inactive state only until
|
| 3489 | /// the end of the current animation frame. At the end of the animation
|
| 3490 | /// frame, any elements that are still inactive will be unmounted.
|
| 3491 | /// * If the element gets reincorporated into the tree (e.g., because it or one
|
| 3492 | /// of its ancestors has a global key that is reused), the framework will
|
| 3493 | /// remove the element from the [owner]'s list of inactive elements, call
|
| 3494 | /// [activate] on the element, and reattach the element's render object to
|
| 3495 | /// the render tree. (At this point, the element is again considered "active"
|
| 3496 | /// and might appear on screen.)
|
| 3497 | /// * If the element does not get reincorporated into the tree by the end of
|
| 3498 | /// the current animation frame, the framework will call [unmount] on the
|
| 3499 | /// element.
|
| 3500 | /// * At this point, the element is considered "defunct" and will not be
|
| 3501 | /// incorporated into the tree in the future.
|
| 3502 | abstract class Element extends DiagnosticableTree implements BuildContext {
|
| 3503 | /// Creates an element that uses the given widget as its configuration.
|
| 3504 | ///
|
| 3505 | /// Typically called by an override of [Widget.createElement].
|
| 3506 | Element(Widget widget) : _widget = widget {
|
| 3507 | assert(debugMaybeDispatchCreated('widgets' , 'Element' , this));
|
| 3508 | }
|
| 3509 |
|
| 3510 | Element? _parent;
|
| 3511 | _NotificationNode? _notificationTree;
|
| 3512 |
|
| 3513 | /// Compare two widgets for equality.
|
| 3514 | ///
|
| 3515 | /// When a widget is rebuilt with another that compares equal according
|
| 3516 | /// to `operator ==`, it is assumed that the update is redundant and the
|
| 3517 | /// work to update that branch of the tree is skipped.
|
| 3518 | ///
|
| 3519 | /// It is generally discouraged to override `operator ==` on any widget that
|
| 3520 | /// has children, since a correct implementation would have to defer to the
|
| 3521 | /// children's equality operator also, and that is an O(N²) operation: each
|
| 3522 | /// child would need to itself walk all its children, each step of the tree.
|
| 3523 | ///
|
| 3524 | /// It is sometimes reasonable for a leaf widget (one with no children) to
|
| 3525 | /// implement this method, if rebuilding the widget is known to be much more
|
| 3526 | /// expensive than checking the widgets' parameters for equality and if the
|
| 3527 | /// widget is expected to often be rebuilt with identical parameters.
|
| 3528 | ///
|
| 3529 | /// In general, however, it is more efficient to cache the widgets used
|
| 3530 | /// in a build method if it is known that they will not change.
|
| 3531 | @nonVirtual
|
| 3532 | @override
|
| 3533 | // ignore: avoid_equals_and_hash_code_on_mutable_classes, hash_and_equals
|
| 3534 | bool operator ==(Object other) => identical(this, other);
|
| 3535 |
|
| 3536 | /// Information set by parent to define where this child fits in its parent's
|
| 3537 | /// child list.
|
| 3538 | ///
|
| 3539 | /// A child widget's slot is determined when the parent's [updateChild] method
|
| 3540 | /// is called to inflate the child widget. See [RenderObjectElement] for more
|
| 3541 | /// details on slots.
|
| 3542 | Object? get slot => _slot;
|
| 3543 | Object? _slot;
|
| 3544 |
|
| 3545 | /// An integer that is guaranteed to be greater than the parent's, if any.
|
| 3546 | /// The element at the root of the tree must have a depth greater than 0.
|
| 3547 | int get depth {
|
| 3548 | assert(() {
|
| 3549 | if (_lifecycleState == _ElementLifecycle.initial) {
|
| 3550 | throw FlutterError('Depth is only available when element has been mounted.' );
|
| 3551 | }
|
| 3552 | return true;
|
| 3553 | }());
|
| 3554 | return _depth;
|
| 3555 | }
|
| 3556 |
|
| 3557 | late int _depth;
|
| 3558 |
|
| 3559 | /// Returns result < 0 when [a] < [b], result == 0 when [a] == [b], result > 0
|
| 3560 | /// when [a] > [b].
|
| 3561 | static int _sort(Element a, Element b) {
|
| 3562 | final int diff = a.depth - b.depth;
|
| 3563 | // If depths are not equal, return the difference.
|
| 3564 | if (diff != 0) {
|
| 3565 | return diff;
|
| 3566 | }
|
| 3567 | // If the `dirty` values are not equal, sort with non-dirty elements being
|
| 3568 | // less than dirty elements.
|
| 3569 | final bool isBDirty = b.dirty;
|
| 3570 | if (a.dirty != isBDirty) {
|
| 3571 | return isBDirty ? -1 : 1;
|
| 3572 | }
|
| 3573 | // Otherwise, `depth`s and `dirty`s are equal.
|
| 3574 | return 0;
|
| 3575 | }
|
| 3576 |
|
| 3577 | // Return a numeric encoding of the specific `Element` concrete subtype.
|
| 3578 | // This is used in `Element.updateChild` to determine if a hot reload modified the
|
| 3579 | // superclass of a mounted element's configuration. The encoding of each `Element`
|
| 3580 | // must match the corresponding `Widget` encoding in `Widget._debugConcreteSubtype`.
|
| 3581 | static int _debugConcreteSubtype(Element element) {
|
| 3582 | return element is StatefulElement
|
| 3583 | ? 1
|
| 3584 | : element is StatelessElement
|
| 3585 | ? 2
|
| 3586 | : 0;
|
| 3587 | }
|
| 3588 |
|
| 3589 | /// The configuration for this element.
|
| 3590 | ///
|
| 3591 | /// Avoid overriding this field on [Element] subtypes to provide a more
|
| 3592 | /// specific widget type (i.e. [StatelessElement] and [StatelessWidget]).
|
| 3593 | /// Instead, cast at any call sites where the more specific type is required.
|
| 3594 | /// This avoids significant cast overhead on the getter which is accessed
|
| 3595 | /// throughout the framework internals during the build phase - and for which
|
| 3596 | /// the more specific type information is not used.
|
| 3597 | @override
|
| 3598 | Widget get widget => _widget!;
|
| 3599 | Widget? _widget;
|
| 3600 |
|
| 3601 | @override
|
| 3602 | bool get mounted => _widget != null;
|
| 3603 |
|
| 3604 | /// Returns true if the Element is defunct.
|
| 3605 | ///
|
| 3606 | /// This getter always returns false in profile and release builds.
|
| 3607 | /// See the lifecycle documentation for [Element] for additional information.
|
| 3608 | bool get debugIsDefunct {
|
| 3609 | bool isDefunct = false;
|
| 3610 | assert(() {
|
| 3611 | isDefunct = _lifecycleState == _ElementLifecycle.defunct;
|
| 3612 | return true;
|
| 3613 | }());
|
| 3614 | return isDefunct;
|
| 3615 | }
|
| 3616 |
|
| 3617 | /// Returns true if the Element is active.
|
| 3618 | ///
|
| 3619 | /// This getter always returns false in profile and release builds.
|
| 3620 | /// See the lifecycle documentation for [Element] for additional information.
|
| 3621 | bool get debugIsActive {
|
| 3622 | bool isActive = false;
|
| 3623 | assert(() {
|
| 3624 | isActive = _lifecycleState == _ElementLifecycle.active;
|
| 3625 | return true;
|
| 3626 | }());
|
| 3627 | return isActive;
|
| 3628 | }
|
| 3629 |
|
| 3630 | /// The object that manages the lifecycle of this element.
|
| 3631 | @override
|
| 3632 | BuildOwner? get owner => _owner;
|
| 3633 | BuildOwner? _owner;
|
| 3634 |
|
| 3635 | /// A [BuildScope] whose dirty [Element]s can only be rebuilt by
|
| 3636 | /// [BuildOwner.buildScope] calls whose `context` argument is an [Element]
|
| 3637 | /// within this [BuildScope].
|
| 3638 | ///
|
| 3639 | /// The getter typically is only safe to access when this [Element] is [mounted].
|
| 3640 | ///
|
| 3641 | /// The default implementation returns the parent [Element]'s [buildScope],
|
| 3642 | /// as in most cases an [Element] is ready to rebuild as soon as its ancestors
|
| 3643 | /// are no longer dirty. One notable exception is [LayoutBuilder]'s
|
| 3644 | /// descendants, which must not rebuild until the incoming constraints become
|
| 3645 | /// available. [LayoutBuilder]'s [Element] overrides [buildScope] to make none
|
| 3646 | /// of its descendants can rebuild until the incoming constraints are known.
|
| 3647 | ///
|
| 3648 | /// If you choose to override this getter to establish your own [BuildScope],
|
| 3649 | /// to flush the dirty [Element]s in the [BuildScope] you need to manually call
|
| 3650 | /// [BuildOwner.buildScope] with the root [Element] of your [BuildScope] when
|
| 3651 | /// appropriate, as the Flutter framework does not try to register or manage
|
| 3652 | /// custom [BuildScope]s.
|
| 3653 | ///
|
| 3654 | /// Always return the same [BuildScope] instance if you override this getter.
|
| 3655 | /// Changing the value returned by this getter at runtime is not
|
| 3656 | /// supported.
|
| 3657 | ///
|
| 3658 | /// The [updateChild] method ignores [buildScope]: if the parent [Element]
|
| 3659 | /// calls [updateChild] on a child with a different [BuildScope], the child may
|
| 3660 | /// still rebuild.
|
| 3661 | ///
|
| 3662 | /// See also:
|
| 3663 | ///
|
| 3664 | /// * [LayoutBuilder], a widget that establishes a custom [BuildScope].
|
| 3665 | BuildScope get buildScope => _parentBuildScope!;
|
| 3666 | // The cached value of the parent Element's build scope. The cache is updated
|
| 3667 | // when this Element mounts or reparents.
|
| 3668 | BuildScope? _parentBuildScope;
|
| 3669 |
|
| 3670 | /// {@template flutter.widgets.Element.reassemble}
|
| 3671 | /// Called whenever the application is reassembled during debugging, for
|
| 3672 | /// example during hot reload.
|
| 3673 | ///
|
| 3674 | /// This method should rerun any initialization logic that depends on global
|
| 3675 | /// state, for example, image loading from asset bundles (since the asset
|
| 3676 | /// bundle may have changed).
|
| 3677 | ///
|
| 3678 | /// This function will only be called during development. In release builds,
|
| 3679 | /// the `ext.flutter.reassemble` hook is not available, and so this code will
|
| 3680 | /// never execute.
|
| 3681 | ///
|
| 3682 | /// Implementers should not rely on any ordering for hot reload source update,
|
| 3683 | /// reassemble, and build methods after a hot reload has been initiated. It is
|
| 3684 | /// possible that a [Timer] (e.g. an [Animation]) or a debugging session
|
| 3685 | /// attached to the isolate could trigger a build with reloaded code _before_
|
| 3686 | /// reassemble is called. Code that expects preconditions to be set by
|
| 3687 | /// reassemble after a hot reload must be resilient to being called out of
|
| 3688 | /// order, e.g. by fizzling instead of throwing. That said, once reassemble is
|
| 3689 | /// called, build will be called after it at least once.
|
| 3690 | /// {@endtemplate}
|
| 3691 | ///
|
| 3692 | /// See also:
|
| 3693 | ///
|
| 3694 | /// * [State.reassemble]
|
| 3695 | /// * [BindingBase.reassembleApplication]
|
| 3696 | /// * [Image], which uses this to reload images.
|
| 3697 | @mustCallSuper
|
| 3698 | @protected
|
| 3699 | void reassemble() {
|
| 3700 | markNeedsBuild();
|
| 3701 | visitChildren((Element child) {
|
| 3702 | child.reassemble();
|
| 3703 | });
|
| 3704 | }
|
| 3705 |
|
| 3706 | bool _debugIsDescendantOf(Element target) {
|
| 3707 | Element? element = this;
|
| 3708 | while (element != null && element.depth > target.depth) {
|
| 3709 | element = element._parent;
|
| 3710 | }
|
| 3711 | return element == target;
|
| 3712 | }
|
| 3713 |
|
| 3714 | /// The render object at (or below) this location in the tree.
|
| 3715 | ///
|
| 3716 | /// If this object is a [RenderObjectElement], the render object is the one at
|
| 3717 | /// this location in the tree. Otherwise, this getter will walk down the tree
|
| 3718 | /// until it finds a [RenderObjectElement].
|
| 3719 | ///
|
| 3720 | /// Some locations in the tree are not backed by a render object. In those
|
| 3721 | /// cases, this getter returns null. This can happen, if the element is
|
| 3722 | /// located outside of a [View] since only the element subtree rooted in a
|
| 3723 | /// view has a render tree associated with it.
|
| 3724 | RenderObject? get renderObject {
|
| 3725 | Element? current = this;
|
| 3726 | while (current != null) {
|
| 3727 | if (current._lifecycleState == _ElementLifecycle.defunct) {
|
| 3728 | break;
|
| 3729 | } else if (current is RenderObjectElement) {
|
| 3730 | return current.renderObject;
|
| 3731 | } else {
|
| 3732 | current = current.renderObjectAttachingChild;
|
| 3733 | }
|
| 3734 | }
|
| 3735 | return null;
|
| 3736 | }
|
| 3737 |
|
| 3738 | /// Returns the child of this [Element] that will insert a [RenderObject] into
|
| 3739 | /// an ancestor of this Element to construct the render tree.
|
| 3740 | ///
|
| 3741 | /// Returns null if this Element doesn't have any children who need to attach
|
| 3742 | /// a [RenderObject] to an ancestor of this [Element]. A [RenderObjectElement]
|
| 3743 | /// will therefore return null because its children insert their
|
| 3744 | /// [RenderObject]s into the [RenderObjectElement] itself and not into an
|
| 3745 | /// ancestor of the [RenderObjectElement].
|
| 3746 | ///
|
| 3747 | /// Furthermore, this may return null for [Element]s that hoist their own
|
| 3748 | /// independent render tree and do not extend the ancestor render tree.
|
| 3749 | @protected
|
| 3750 | Element? get renderObjectAttachingChild {
|
| 3751 | Element? next;
|
| 3752 | visitChildren((Element child) {
|
| 3753 | assert(next == null); // This verifies that there's only one child.
|
| 3754 | next = child;
|
| 3755 | });
|
| 3756 | return next;
|
| 3757 | }
|
| 3758 |
|
| 3759 | @override
|
| 3760 | List<DiagnosticsNode> describeMissingAncestor({required Type expectedAncestorType}) {
|
| 3761 | final List<DiagnosticsNode> information = <DiagnosticsNode>[];
|
| 3762 | final List<Element> ancestors = <Element>[];
|
| 3763 | visitAncestorElements((Element element) {
|
| 3764 | ancestors.add(element);
|
| 3765 | return true;
|
| 3766 | });
|
| 3767 |
|
| 3768 | information.add(
|
| 3769 | DiagnosticsProperty<Element>(
|
| 3770 | 'The specific widget that could not find a $expectedAncestorType ancestor was' ,
|
| 3771 | this,
|
| 3772 | style: DiagnosticsTreeStyle.errorProperty,
|
| 3773 | ),
|
| 3774 | );
|
| 3775 |
|
| 3776 | if (ancestors.isNotEmpty) {
|
| 3777 | information.add(describeElements('The ancestors of this widget were' , ancestors));
|
| 3778 | } else {
|
| 3779 | information.add(
|
| 3780 | ErrorDescription(
|
| 3781 | 'This widget is the root of the tree, so it has no '
|
| 3782 | 'ancestors, let alone a " $expectedAncestorType" ancestor.' ,
|
| 3783 | ),
|
| 3784 | );
|
| 3785 | }
|
| 3786 | return information;
|
| 3787 | }
|
| 3788 |
|
| 3789 | /// Returns a list of [Element]s from the current build context to the error report.
|
| 3790 | static DiagnosticsNode describeElements(String name, Iterable<Element> elements) {
|
| 3791 | return DiagnosticsBlock(
|
| 3792 | name: name,
|
| 3793 | children: 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 = updateChild(
|
| 4140 | oldChild,
|
| 4141 | newWidget,
|
| 4142 | slotFor(newChildrenTop, previousChild),
|
| 4143 | )!;
|
| 4144 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
| 4145 | newChildren[newChildrenTop] = newChild;
|
| 4146 | previousChild = newChild;
|
| 4147 | newChildrenTop += 1;
|
| 4148 | oldChildrenTop += 1;
|
| 4149 | }
|
| 4150 |
|
| 4151 | // Scan the bottom of the list.
|
| 4152 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
|
| 4153 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
|
| 4154 | final Widget newWidget = newWidgets[newChildrenBottom];
|
| 4155 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
|
| 4156 | if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget)) {
|
| 4157 | break;
|
| 4158 | }
|
| 4159 | oldChildrenBottom -= 1;
|
| 4160 | newChildrenBottom -= 1;
|
| 4161 | }
|
| 4162 |
|
| 4163 | // Scan the old children in the middle of the list.
|
| 4164 | final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
|
| 4165 | Map<Key, Element>? oldKeyedChildren;
|
| 4166 | if (haveOldChildren) {
|
| 4167 | oldKeyedChildren = <Key, Element>{};
|
| 4168 | while (oldChildrenTop <= oldChildrenBottom) {
|
| 4169 | final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
|
| 4170 | assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
|
| 4171 | if (oldChild != null) {
|
| 4172 | if (oldChild.widget.key != null) {
|
| 4173 | oldKeyedChildren[oldChild.widget.key!] = oldChild;
|
| 4174 | } else {
|
| 4175 | deactivateChild(oldChild);
|
| 4176 | }
|
| 4177 | }
|
| 4178 | oldChildrenTop += 1;
|
| 4179 | }
|
| 4180 | }
|
| 4181 |
|
| 4182 | // Update the middle of the list.
|
| 4183 | while (newChildrenTop <= newChildrenBottom) {
|
| 4184 | Element? oldChild;
|
| 4185 | final Widget newWidget = newWidgets[newChildrenTop];
|
| 4186 | if (haveOldChildren) {
|
| 4187 | final Key? key = newWidget.key;
|
| 4188 | if (key != null) {
|
| 4189 | oldChild = oldKeyedChildren![key];
|
| 4190 | if (oldChild != null) {
|
| 4191 | if (Widget.canUpdate(oldChild.widget, newWidget)) {
|
| 4192 | // we found a match!
|
| 4193 | // remove it from oldKeyedChildren so we don't unsync it later
|
| 4194 | oldKeyedChildren.remove(key);
|
| 4195 | } else {
|
| 4196 | // Not a match, let's pretend we didn't see it for now.
|
| 4197 | oldChild = null;
|
| 4198 | }
|
| 4199 | }
|
| 4200 | }
|
| 4201 | }
|
| 4202 | assert(oldChild == null || Widget.canUpdate(oldChild.widget, newWidget));
|
| 4203 | final Element newChild = updateChild(
|
| 4204 | oldChild,
|
| 4205 | newWidget,
|
| 4206 | slotFor(newChildrenTop, previousChild),
|
| 4207 | )!;
|
| 4208 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
| 4209 | assert(
|
| 4210 | oldChild == newChild ||
|
| 4211 | oldChild == null ||
|
| 4212 | oldChild._lifecycleState != _ElementLifecycle.active,
|
| 4213 | );
|
| 4214 | newChildren[newChildrenTop] = newChild;
|
| 4215 | previousChild = newChild;
|
| 4216 | newChildrenTop += 1;
|
| 4217 | }
|
| 4218 |
|
| 4219 | // We've scanned the whole list.
|
| 4220 | assert(oldChildrenTop == oldChildrenBottom + 1);
|
| 4221 | assert(newChildrenTop == newChildrenBottom + 1);
|
| 4222 | assert(newWidgets.length - newChildrenTop == oldChildren.length - oldChildrenTop);
|
| 4223 | newChildrenBottom = newWidgets.length - 1;
|
| 4224 | oldChildrenBottom = oldChildren.length - 1;
|
| 4225 |
|
| 4226 | // Update the bottom of the list.
|
| 4227 | while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
|
| 4228 | final Element oldChild = oldChildren[oldChildrenTop];
|
| 4229 | assert(replaceWithNullIfForgotten(oldChild) != null);
|
| 4230 | assert(oldChild._lifecycleState == _ElementLifecycle.active);
|
| 4231 | final Widget newWidget = newWidgets[newChildrenTop];
|
| 4232 | assert(Widget.canUpdate(oldChild.widget, newWidget));
|
| 4233 | final Element newChild = updateChild(
|
| 4234 | oldChild,
|
| 4235 | newWidget,
|
| 4236 | slotFor(newChildrenTop, previousChild),
|
| 4237 | )!;
|
| 4238 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
| 4239 | assert(oldChild == newChild || oldChild._lifecycleState != _ElementLifecycle.active);
|
| 4240 | newChildren[newChildrenTop] = newChild;
|
| 4241 | previousChild = newChild;
|
| 4242 | newChildrenTop += 1;
|
| 4243 | oldChildrenTop += 1;
|
| 4244 | }
|
| 4245 |
|
| 4246 | // Clean up any of the remaining middle nodes from the old list.
|
| 4247 | if (haveOldChildren && oldKeyedChildren!.isNotEmpty) {
|
| 4248 | for (final Element oldChild in oldKeyedChildren.values) {
|
| 4249 | if (forgottenChildren == null || !forgottenChildren.contains(oldChild)) {
|
| 4250 | deactivateChild(oldChild);
|
| 4251 | }
|
| 4252 | }
|
| 4253 | }
|
| 4254 | assert(newChildren.every((Element element) => element is! _NullElement));
|
| 4255 | return newChildren;
|
| 4256 | }
|
| 4257 |
|
| 4258 | /// Add this element to the tree in the given slot of the given parent.
|
| 4259 | ///
|
| 4260 | /// The framework calls this function when a newly created element is added to
|
| 4261 | /// the tree for the first time. Use this method to initialize state that
|
| 4262 | /// depends on having a parent. State that is independent of the parent can
|
| 4263 | /// more easily be initialized in the constructor.
|
| 4264 | ///
|
| 4265 | /// This method transitions the element from the "initial" lifecycle state to
|
| 4266 | /// the "active" lifecycle state.
|
| 4267 | ///
|
| 4268 | /// Subclasses that override this method are likely to want to also override
|
| 4269 | /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
|
| 4270 | /// [RenderObjectElement.moveRenderObjectChild], and
|
| 4271 | /// [RenderObjectElement.removeRenderObjectChild].
|
| 4272 | ///
|
| 4273 | /// Implementations of this method should start with a call to the inherited
|
| 4274 | /// method, as in `super.mount(parent, newSlot)`.
|
| 4275 | @mustCallSuper
|
| 4276 | void mount(Element? parent, Object? newSlot) {
|
| 4277 | assert(
|
| 4278 | _lifecycleState == _ElementLifecycle.initial,
|
| 4279 | 'This element is no longer in its initial state ( ${_lifecycleState.name})' ,
|
| 4280 | );
|
| 4281 | assert(
|
| 4282 | _parent == null,
|
| 4283 | "This element already has a parent ( $_parent) and it shouldn't have one yet." ,
|
| 4284 | );
|
| 4285 | assert(
|
| 4286 | parent == null || parent._lifecycleState == _ElementLifecycle.active,
|
| 4287 | 'Parent ( $parent) should be null or in the active state ( ${parent._lifecycleState.name})' ,
|
| 4288 | );
|
| 4289 | assert(slot == null, "This element already has a slot ( $slot) and it shouldn't" );
|
| 4290 | _parent = parent;
|
| 4291 | _slot = newSlot;
|
| 4292 | _lifecycleState = _ElementLifecycle.active;
|
| 4293 | _depth = 1 + (_parent?.depth ?? 0);
|
| 4294 | if (parent != null) {
|
| 4295 | // Only assign ownership if the parent is non-null. If parent is null
|
| 4296 | // (the root node), the owner should have already been assigned.
|
| 4297 | // See RootRenderObjectElement.assignOwner().
|
| 4298 | _owner = parent.owner;
|
| 4299 | _parentBuildScope = parent.buildScope;
|
| 4300 | }
|
| 4301 | assert(owner != null);
|
| 4302 | final Key? key = widget.key;
|
| 4303 | if (key is GlobalKey) {
|
| 4304 | owner!._registerGlobalKey(key, this);
|
| 4305 | }
|
| 4306 | _updateInheritance();
|
| 4307 | attachNotificationTree();
|
| 4308 | }
|
| 4309 |
|
| 4310 | void _debugRemoveGlobalKeyReservation(Element child) {
|
| 4311 | assert(owner != null);
|
| 4312 | owner!._debugRemoveGlobalKeyReservationFor(this, child);
|
| 4313 | }
|
| 4314 |
|
| 4315 | /// Change the widget used to configure this element.
|
| 4316 | ///
|
| 4317 | /// The framework calls this function when the parent wishes to use a
|
| 4318 | /// different widget to configure this element. The new widget is guaranteed
|
| 4319 | /// to have the same [runtimeType] as the old widget.
|
| 4320 | ///
|
| 4321 | /// This function is called only during the "active" lifecycle state.
|
| 4322 | @mustCallSuper
|
| 4323 | void update(covariant Widget newWidget) {
|
| 4324 | // This code is hot when hot reloading, so we try to
|
| 4325 | // only call _AssertionError._evaluateAssertion once.
|
| 4326 | assert(
|
| 4327 | _lifecycleState == _ElementLifecycle.active &&
|
| 4328 | newWidget != widget &&
|
| 4329 | Widget.canUpdate(widget, newWidget),
|
| 4330 | );
|
| 4331 | // This Element was told to update and we can now release all the global key
|
| 4332 | // reservations of forgotten children. We cannot do this earlier because the
|
| 4333 | // forgotten children still represent global key duplications if the element
|
| 4334 | // never updates (the forgotten children are not removed from the tree
|
| 4335 | // until the call to update happens)
|
| 4336 | assert(() {
|
| 4337 | _debugForgottenChildrenWithGlobalKey?.forEach(_debugRemoveGlobalKeyReservation);
|
| 4338 | _debugForgottenChildrenWithGlobalKey?.clear();
|
| 4339 | return true;
|
| 4340 | }());
|
| 4341 | _widget = newWidget;
|
| 4342 | }
|
| 4343 |
|
| 4344 | /// Change the slot that the given child occupies in its parent.
|
| 4345 | ///
|
| 4346 | /// Called by [MultiChildRenderObjectElement], and other [RenderObjectElement]
|
| 4347 | /// subclasses that have multiple children, when child moves from one position
|
| 4348 | /// to another in this element's child list.
|
| 4349 | @protected
|
| 4350 | void updateSlotForChild(Element child, Object? newSlot) {
|
| 4351 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 4352 | assert(child._parent == this);
|
| 4353 | void visit(Element element) {
|
| 4354 | element.updateSlot(newSlot);
|
| 4355 | final Element? descendant = element.renderObjectAttachingChild;
|
| 4356 | if (descendant != null) {
|
| 4357 | visit(descendant);
|
| 4358 | }
|
| 4359 | }
|
| 4360 |
|
| 4361 | visit(child);
|
| 4362 | }
|
| 4363 |
|
| 4364 | /// Called by [updateSlotForChild] when the framework needs to change the slot
|
| 4365 | /// that this [Element] occupies in its ancestor.
|
| 4366 | @protected
|
| 4367 | @mustCallSuper
|
| 4368 | void updateSlot(Object? newSlot) {
|
| 4369 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 4370 | assert(_parent != null);
|
| 4371 | assert(_parent!._lifecycleState == _ElementLifecycle.active);
|
| 4372 | _slot = newSlot;
|
| 4373 | }
|
| 4374 |
|
| 4375 | void _updateDepth(int parentDepth) {
|
| 4376 | final int expectedDepth = parentDepth + 1;
|
| 4377 | if (_depth < expectedDepth) {
|
| 4378 | _depth = expectedDepth;
|
| 4379 | visitChildren((Element child) {
|
| 4380 | child._updateDepth(expectedDepth);
|
| 4381 | });
|
| 4382 | }
|
| 4383 | }
|
| 4384 |
|
| 4385 | void _updateBuildScopeRecursively() {
|
| 4386 | if (identical(buildScope, _parent?.buildScope)) {
|
| 4387 | return;
|
| 4388 | }
|
| 4389 | // Unset the _inDirtyList flag so this Element can be added to the dirty list
|
| 4390 | // of the new build scope if it's dirty.
|
| 4391 | _inDirtyList = false;
|
| 4392 | _parentBuildScope = _parent?.buildScope;
|
| 4393 | visitChildren((Element child) {
|
| 4394 | child._updateBuildScopeRecursively();
|
| 4395 | });
|
| 4396 | }
|
| 4397 |
|
| 4398 | /// Remove [renderObject] from the render tree.
|
| 4399 | ///
|
| 4400 | /// The default implementation of this function calls
|
| 4401 | /// [detachRenderObject] recursively on each child. The
|
| 4402 | /// [RenderObjectElement.detachRenderObject] override does the actual work of
|
| 4403 | /// removing [renderObject] from the render tree.
|
| 4404 | ///
|
| 4405 | /// This is called by [deactivateChild].
|
| 4406 | void detachRenderObject() {
|
| 4407 | visitChildren((Element child) {
|
| 4408 | child.detachRenderObject();
|
| 4409 | });
|
| 4410 | _slot = null;
|
| 4411 | }
|
| 4412 |
|
| 4413 | /// Add [renderObject] to the render tree at the location specified by `newSlot`.
|
| 4414 | ///
|
| 4415 | /// The default implementation of this function calls
|
| 4416 | /// [attachRenderObject] recursively on each child. The
|
| 4417 | /// [RenderObjectElement.attachRenderObject] override does the actual work of
|
| 4418 | /// adding [renderObject] to the render tree.
|
| 4419 | ///
|
| 4420 | /// The `newSlot` argument specifies the new value for this element's [slot].
|
| 4421 | void attachRenderObject(Object? newSlot) {
|
| 4422 | assert(slot == null);
|
| 4423 | visitChildren((Element child) {
|
| 4424 | child.attachRenderObject(newSlot);
|
| 4425 | });
|
| 4426 | _slot = newSlot;
|
| 4427 | }
|
| 4428 |
|
| 4429 | Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) {
|
| 4430 | // The "inactivity" of the element being retaken here may be forward-looking: if
|
| 4431 | // we are taking an element with a GlobalKey from an element that currently has
|
| 4432 | // it as a child, then we know that element will soon no longer have that
|
| 4433 | // element as a child. The only way that assumption could be false is if the
|
| 4434 | // global key is being duplicated, and we'll try to track that using the
|
| 4435 | // _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans call below.
|
| 4436 | final Element? element = key._currentElement;
|
| 4437 | if (element == null) {
|
| 4438 | return null;
|
| 4439 | }
|
| 4440 | if (!Widget.canUpdate(element.widget, newWidget)) {
|
| 4441 | return null;
|
| 4442 | }
|
| 4443 | assert(() {
|
| 4444 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
| 4445 | debugPrint(
|
| 4446 | 'Attempting to take $element from ${element._parent ?? "inactive elements list" } to put in $this.' ,
|
| 4447 | );
|
| 4448 | }
|
| 4449 | return true;
|
| 4450 | }());
|
| 4451 | final Element? parent = element._parent;
|
| 4452 | if (parent != null) {
|
| 4453 | assert(() {
|
| 4454 | if (parent == this) {
|
| 4455 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4456 | ErrorSummary("A GlobalKey was used multiple times inside one widget's child list." ),
|
| 4457 | DiagnosticsProperty<GlobalKey>('The offending GlobalKey was' , key),
|
| 4458 | parent.describeElement('The parent of the widgets with that key was' ),
|
| 4459 | element.describeElement('The first child to get instantiated with that key became' ),
|
| 4460 | DiagnosticsProperty<Widget>(
|
| 4461 | 'The second child that was to be instantiated with that key was' ,
|
| 4462 | widget,
|
| 4463 | style: DiagnosticsTreeStyle.errorProperty,
|
| 4464 | ),
|
| 4465 | ErrorDescription(
|
| 4466 | 'A GlobalKey can only be specified on one widget at a time in the widget tree.' ,
|
| 4467 | ),
|
| 4468 | ]);
|
| 4469 | }
|
| 4470 | parent.owner!._debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans(
|
| 4471 | parent,
|
| 4472 | key,
|
| 4473 | );
|
| 4474 | return true;
|
| 4475 | }());
|
| 4476 | parent.forgetChild(element);
|
| 4477 | parent.deactivateChild(element);
|
| 4478 | }
|
| 4479 | assert(element._parent == null);
|
| 4480 | owner!._inactiveElements.remove(element);
|
| 4481 | return element;
|
| 4482 | }
|
| 4483 |
|
| 4484 | /// Create an element for the given widget and add it as a child of this
|
| 4485 | /// element in the given slot.
|
| 4486 | ///
|
| 4487 | /// This method is typically called by [updateChild] but can be called
|
| 4488 | /// directly by subclasses that need finer-grained control over creating
|
| 4489 | /// elements.
|
| 4490 | ///
|
| 4491 | /// If the given widget has a global key and an element already exists that
|
| 4492 | /// has a widget with that global key, this function will reuse that element
|
| 4493 | /// (potentially grafting it from another location in the tree or reactivating
|
| 4494 | /// it from the list of inactive elements) rather than creating a new element.
|
| 4495 | ///
|
| 4496 | /// The `newSlot` argument specifies the new value for this element's [slot].
|
| 4497 | ///
|
| 4498 | /// The element returned by this function will already have been mounted and
|
| 4499 | /// will be in the "active" lifecycle state.
|
| 4500 | @protected
|
| 4501 | @pragma('dart2js:tryInline' )
|
| 4502 | @pragma('vm:prefer-inline' )
|
| 4503 | @pragma('wasm:prefer-inline' )
|
| 4504 | Element inflateWidget(Widget newWidget, Object? newSlot) {
|
| 4505 | final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
|
| 4506 | if (isTimelineTracked) {
|
| 4507 | Map<String, String>? debugTimelineArguments;
|
| 4508 | assert(() {
|
| 4509 | if (kDebugMode && debugEnhanceBuildTimelineArguments) {
|
| 4510 | debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments();
|
| 4511 | }
|
| 4512 | return true;
|
| 4513 | }());
|
| 4514 | FlutterTimeline.startSync(' ${newWidget.runtimeType}' , arguments: debugTimelineArguments);
|
| 4515 | }
|
| 4516 |
|
| 4517 | try {
|
| 4518 | final Key? key = newWidget.key;
|
| 4519 | if (key is GlobalKey) {
|
| 4520 | final Element? newChild = _retakeInactiveElement(key, newWidget);
|
| 4521 | if (newChild != null) {
|
| 4522 | assert(newChild._parent == null);
|
| 4523 | assert(() {
|
| 4524 | _debugCheckForCycles(newChild);
|
| 4525 | return true;
|
| 4526 | }());
|
| 4527 | try {
|
| 4528 | newChild._activateWithParent(this, newSlot);
|
| 4529 | } catch (_) {
|
| 4530 | // Attempt to do some clean-up if activation fails to leave tree in a reasonable state.
|
| 4531 | try {
|
| 4532 | deactivateChild(newChild);
|
| 4533 | } catch (_) {
|
| 4534 | // Clean-up failed. Only surface original exception.
|
| 4535 | }
|
| 4536 | rethrow;
|
| 4537 | }
|
| 4538 | final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
|
| 4539 | assert(newChild == updatedChild);
|
| 4540 | return updatedChild!;
|
| 4541 | }
|
| 4542 | }
|
| 4543 | final Element newChild = newWidget.createElement();
|
| 4544 | assert(() {
|
| 4545 | _debugCheckForCycles(newChild);
|
| 4546 | return true;
|
| 4547 | }());
|
| 4548 | newChild.mount(this, newSlot);
|
| 4549 | assert(newChild._lifecycleState == _ElementLifecycle.active);
|
| 4550 |
|
| 4551 | return newChild;
|
| 4552 | } finally {
|
| 4553 | if (isTimelineTracked) {
|
| 4554 | FlutterTimeline.finishSync();
|
| 4555 | }
|
| 4556 | }
|
| 4557 | }
|
| 4558 |
|
| 4559 | void _debugCheckForCycles(Element newChild) {
|
| 4560 | assert(newChild._parent == null);
|
| 4561 | assert(() {
|
| 4562 | Element node = this;
|
| 4563 | while (node._parent != null) {
|
| 4564 | node = node._parent!;
|
| 4565 | }
|
| 4566 | assert(node != newChild); // indicates we are about to create a cycle
|
| 4567 | return true;
|
| 4568 | }());
|
| 4569 | }
|
| 4570 |
|
| 4571 | /// Move the given element to the list of inactive elements and detach its
|
| 4572 | /// render object from the render tree.
|
| 4573 | ///
|
| 4574 | /// This method stops the given element from being a child of this element by
|
| 4575 | /// detaching its render object from the render tree and moving the element to
|
| 4576 | /// the list of inactive elements.
|
| 4577 | ///
|
| 4578 | /// This method (indirectly) calls [deactivate] on the child.
|
| 4579 | ///
|
| 4580 | /// The caller is responsible for removing the child from its child model.
|
| 4581 | /// Typically [deactivateChild] is called by the element itself while it is
|
| 4582 | /// updating its child model; however, during [GlobalKey] reparenting, the new
|
| 4583 | /// parent proactively calls the old parent's [deactivateChild], first using
|
| 4584 | /// [forgetChild] to cause the old parent to update its child model.
|
| 4585 | @protected
|
| 4586 | void deactivateChild(Element child) {
|
| 4587 | assert(child._parent == this);
|
| 4588 | child._parent = null;
|
| 4589 | child.detachRenderObject();
|
| 4590 | owner!._inactiveElements.add(child); // this eventually calls child.deactivate()
|
| 4591 | assert(() {
|
| 4592 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
| 4593 | if (child.widget.key is GlobalKey) {
|
| 4594 | debugPrint('Deactivated $child (keyed child of $this)' );
|
| 4595 | }
|
| 4596 | }
|
| 4597 | return true;
|
| 4598 | }());
|
| 4599 | }
|
| 4600 |
|
| 4601 | // The children that have been forgotten by forgetChild. This will be used in
|
| 4602 | // [update] to remove the global key reservations of forgotten children.
|
| 4603 | //
|
| 4604 | // In Profile/Release mode this field is initialized to `null`. The Dart compiler can
|
| 4605 | // eliminate unused fields, but not their initializers.
|
| 4606 | @_debugOnly
|
| 4607 | final Set<Element>? _debugForgottenChildrenWithGlobalKey = kDebugMode ? HashSet<Element>() : null;
|
| 4608 |
|
| 4609 | /// Remove the given child from the element's child list, in preparation for
|
| 4610 | /// the child being reused elsewhere in the element tree.
|
| 4611 | ///
|
| 4612 | /// This updates the child model such that, e.g., [visitChildren] does not
|
| 4613 | /// walk that child anymore.
|
| 4614 | ///
|
| 4615 | /// The element will still have a valid parent when this is called, and the
|
| 4616 | /// child's [Element.slot] value will be valid in the context of that parent.
|
| 4617 | /// After this is called, [deactivateChild] is called to sever the link to
|
| 4618 | /// this object.
|
| 4619 | ///
|
| 4620 | /// The [update] is responsible for updating or creating the new child that
|
| 4621 | /// will replace this [child].
|
| 4622 | @protected
|
| 4623 | @mustCallSuper
|
| 4624 | void forgetChild(Element child) {
|
| 4625 | // This method is called on the old parent when the given child (with a
|
| 4626 | // global key) is given a new parent. We cannot remove the global key
|
| 4627 | // reservation directly in this method because the forgotten child is not
|
| 4628 | // removed from the tree until this Element is updated in [update]. If
|
| 4629 | // [update] is never called, the forgotten child still represents a global
|
| 4630 | // key duplication that we need to catch.
|
| 4631 | assert(() {
|
| 4632 | if (child.widget.key is GlobalKey) {
|
| 4633 | _debugForgottenChildrenWithGlobalKey?.add(child);
|
| 4634 | }
|
| 4635 | return true;
|
| 4636 | }());
|
| 4637 | }
|
| 4638 |
|
| 4639 | void _activateWithParent(Element parent, Object? newSlot) {
|
| 4640 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
| 4641 | _parent = parent;
|
| 4642 | _owner = parent.owner;
|
| 4643 | assert(() {
|
| 4644 | if (debugPrintGlobalKeyedWidgetLifecycle) {
|
| 4645 | debugPrint('Reactivating $this (now child of $_parent).' );
|
| 4646 | }
|
| 4647 | return true;
|
| 4648 | }());
|
| 4649 | _updateDepth(_parent!.depth);
|
| 4650 | _updateBuildScopeRecursively();
|
| 4651 | _activateRecursively(this);
|
| 4652 | attachRenderObject(newSlot);
|
| 4653 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 4654 | }
|
| 4655 |
|
| 4656 | static void _activateRecursively(Element element) {
|
| 4657 | assert(element._lifecycleState == _ElementLifecycle.inactive);
|
| 4658 | element.activate();
|
| 4659 | assert(element._lifecycleState == _ElementLifecycle.active);
|
| 4660 | element.visitChildren(_activateRecursively);
|
| 4661 | }
|
| 4662 |
|
| 4663 | /// Transition from the "inactive" to the "active" lifecycle state.
|
| 4664 | ///
|
| 4665 | /// The framework calls this method when a previously deactivated element has
|
| 4666 | /// been reincorporated into the tree. The framework does not call this method
|
| 4667 | /// the first time an element becomes active (i.e., from the "initial"
|
| 4668 | /// lifecycle state). Instead, the framework calls [mount] in that situation.
|
| 4669 | ///
|
| 4670 | /// See the lifecycle documentation for [Element] for additional information.
|
| 4671 | ///
|
| 4672 | /// Implementations of this method should start with a call to the inherited
|
| 4673 | /// method, as in `super.activate()`.
|
| 4674 | @mustCallSuper
|
| 4675 | void activate() {
|
| 4676 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
| 4677 | assert(owner != null);
|
| 4678 | final bool hadDependencies =
|
| 4679 | (_dependencies?.isNotEmpty ?? false) || _hadUnsatisfiedDependencies;
|
| 4680 | _lifecycleState = _ElementLifecycle.active;
|
| 4681 | // We unregistered our dependencies in deactivate, but never cleared the list.
|
| 4682 | // Since we're going to be reused, let's clear our list now.
|
| 4683 | _dependencies?.clear();
|
| 4684 | _hadUnsatisfiedDependencies = false;
|
| 4685 | _updateInheritance();
|
| 4686 | attachNotificationTree();
|
| 4687 | if (_dirty) {
|
| 4688 | owner!.scheduleBuildFor(this);
|
| 4689 | }
|
| 4690 | if (hadDependencies) {
|
| 4691 | didChangeDependencies();
|
| 4692 | }
|
| 4693 | }
|
| 4694 |
|
| 4695 | /// Transition from the "active" to the "inactive" lifecycle state.
|
| 4696 | ///
|
| 4697 | /// The framework calls this method when a previously active element is moved
|
| 4698 | /// to the list of inactive elements. While in the inactive state, the element
|
| 4699 | /// will not appear on screen. The element can remain in the inactive state
|
| 4700 | /// only until the end of the current animation frame. At the end of the
|
| 4701 | /// animation frame, if the element has not be reactivated, the framework will
|
| 4702 | /// unmount the element.
|
| 4703 | ///
|
| 4704 | /// This is (indirectly) called by [deactivateChild].
|
| 4705 | ///
|
| 4706 | /// See the lifecycle documentation for [Element] for additional information.
|
| 4707 | ///
|
| 4708 | /// Implementations of this method should end with a call to the inherited
|
| 4709 | /// method, as in `super.deactivate()`.
|
| 4710 | @mustCallSuper
|
| 4711 | void deactivate() {
|
| 4712 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 4713 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
|
| 4714 | if (_dependencies?.isNotEmpty ?? false) {
|
| 4715 | for (final InheritedElement dependency in _dependencies!) {
|
| 4716 | dependency.removeDependent(this);
|
| 4717 | }
|
| 4718 | // For expediency, we don't actually clear the list here, even though it's
|
| 4719 | // no longer representative of what we are registered with. If we never
|
| 4720 | // get re-used, it doesn't matter. If we do, then we'll clear the list in
|
| 4721 | // activate(). The benefit of this is that it allows Element's activate()
|
| 4722 | // implementation to decide whether to rebuild based on whether we had
|
| 4723 | // dependencies here.
|
| 4724 | }
|
| 4725 | _inheritedElements = null;
|
| 4726 | _lifecycleState = _ElementLifecycle.inactive;
|
| 4727 | }
|
| 4728 |
|
| 4729 | /// Called, in debug mode, after children have been deactivated (see [deactivate]).
|
| 4730 | ///
|
| 4731 | /// This method is not called in release builds.
|
| 4732 | @mustCallSuper
|
| 4733 | void debugDeactivated() {
|
| 4734 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
| 4735 | }
|
| 4736 |
|
| 4737 | /// Transition from the "inactive" to the "defunct" lifecycle state.
|
| 4738 | ///
|
| 4739 | /// Called when the framework determines that an inactive element will never
|
| 4740 | /// be reactivated. At the end of each animation frame, the framework calls
|
| 4741 | /// [unmount] on any remaining inactive elements, preventing inactive elements
|
| 4742 | /// from remaining inactive for longer than a single animation frame.
|
| 4743 | ///
|
| 4744 | /// After this function is called, the element will not be incorporated into
|
| 4745 | /// the tree again.
|
| 4746 | ///
|
| 4747 | /// Any resources this element holds should be released at this point. For
|
| 4748 | /// example, [RenderObjectElement.unmount] calls [RenderObject.dispose] and
|
| 4749 | /// nulls out its reference to the render object.
|
| 4750 | ///
|
| 4751 | /// See the lifecycle documentation for [Element] for additional information.
|
| 4752 | ///
|
| 4753 | /// Implementations of this method should end with a call to the inherited
|
| 4754 | /// method, as in `super.unmount()`.
|
| 4755 | @mustCallSuper
|
| 4756 | void unmount() {
|
| 4757 | assert(_lifecycleState == _ElementLifecycle.inactive);
|
| 4758 | assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
|
| 4759 | assert(owner != null);
|
| 4760 | assert(debugMaybeDispatchDisposed(this));
|
| 4761 | // Use the private property to avoid a CastError during hot reload.
|
| 4762 | final Key? key = _widget?.key;
|
| 4763 | if (key is GlobalKey) {
|
| 4764 | owner!._unregisterGlobalKey(key, this);
|
| 4765 | }
|
| 4766 | // Release resources to reduce the severity of memory leaks caused by
|
| 4767 | // defunct, but accidentally retained Elements.
|
| 4768 | _widget = null;
|
| 4769 | _dependencies = null;
|
| 4770 | _lifecycleState = _ElementLifecycle.defunct;
|
| 4771 | }
|
| 4772 |
|
| 4773 | /// Whether the child in the provided `slot` (or one of its descendants) must
|
| 4774 | /// insert a [RenderObject] into its ancestor [RenderObjectElement] by calling
|
| 4775 | /// [RenderObjectElement.insertRenderObjectChild] on it.
|
| 4776 | ///
|
| 4777 | /// This method is used to define non-rendering zones in the element tree (see
|
| 4778 | /// [WidgetsBinding] for an explanation of rendering and non-rendering zones):
|
| 4779 | ///
|
| 4780 | /// Most branches of the [Element] tree are expected to eventually insert a
|
| 4781 | /// [RenderObject] into their [RenderObjectElement] ancestor to construct the
|
| 4782 | /// render tree. However, there is a notable exception: an [Element] may
|
| 4783 | /// expect that the occupant of a certain child slot creates a new independent
|
| 4784 | /// render tree and therefore is not allowed to insert a render object into
|
| 4785 | /// the existing render tree. Those elements must return false from this
|
| 4786 | /// method for the slot in question to signal to the child in that slot that
|
| 4787 | /// it must not call [RenderObjectElement.insertRenderObjectChild] on its
|
| 4788 | /// ancestor.
|
| 4789 | ///
|
| 4790 | /// As an example, the element backing the [ViewAnchor] returns false from
|
| 4791 | /// this method for the [ViewAnchor.view] slot to enforce that it is occupied
|
| 4792 | /// by e.g. a [View] widget, which will ultimately bootstrap a separate
|
| 4793 | /// render tree for that view. Another example is the [ViewCollection] widget,
|
| 4794 | /// which returns false for all its slots for the same reason.
|
| 4795 | ///
|
| 4796 | /// Overriding this method is not common, as elements behaving in the way
|
| 4797 | /// described above are rare.
|
| 4798 | bool debugExpectsRenderObjectForSlot(Object? slot) => true;
|
| 4799 |
|
| 4800 | @override
|
| 4801 | RenderObject? findRenderObject() {
|
| 4802 | assert(() {
|
| 4803 | if (_lifecycleState != _ElementLifecycle.active) {
|
| 4804 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4805 | ErrorSummary('Cannot get renderObject of inactive element.' ),
|
| 4806 | ErrorDescription(
|
| 4807 | 'In order for an element to have a valid renderObject, it must be '
|
| 4808 | 'active, which means it is part of the tree.\n'
|
| 4809 | 'Instead, this element is in the $_lifecycleState state.\n'
|
| 4810 | 'If you called this method from a State object, consider guarding '
|
| 4811 | 'it with State.mounted.' ,
|
| 4812 | ),
|
| 4813 | describeElement('The findRenderObject() method was called for the following element' ),
|
| 4814 | ]);
|
| 4815 | }
|
| 4816 | return true;
|
| 4817 | }());
|
| 4818 | return renderObject;
|
| 4819 | }
|
| 4820 |
|
| 4821 | @override
|
| 4822 | Size? get size {
|
| 4823 | assert(() {
|
| 4824 | if (_lifecycleState != _ElementLifecycle.active) {
|
| 4825 | // TODO(jacobr): is this a good separation into contract and violation?
|
| 4826 | // I have added a line of white space.
|
| 4827 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4828 | ErrorSummary('Cannot get size of inactive element.' ),
|
| 4829 | ErrorDescription(
|
| 4830 | 'In order for an element to have a valid size, the element must be '
|
| 4831 | 'active, which means it is part of the tree.\n'
|
| 4832 | 'Instead, this element is in the $_lifecycleState state.' ,
|
| 4833 | ),
|
| 4834 | describeElement('The size getter was called for the following element' ),
|
| 4835 | ]);
|
| 4836 | }
|
| 4837 | if (owner!._debugBuilding) {
|
| 4838 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4839 | ErrorSummary('Cannot get size during build.' ),
|
| 4840 | ErrorDescription(
|
| 4841 | 'The size of this render object has not yet been determined because '
|
| 4842 | 'the framework is still in the process of building widgets, which '
|
| 4843 | 'means the render tree for this frame has not yet been determined. '
|
| 4844 | 'The size getter should only be called from paint callbacks or '
|
| 4845 | 'interaction event handlers (e.g. gesture callbacks).' ,
|
| 4846 | ),
|
| 4847 | ErrorSpacer(),
|
| 4848 | ErrorHint(
|
| 4849 | 'If you need some sizing information during build to decide which '
|
| 4850 | 'widgets to build, consider using a LayoutBuilder widget, which can '
|
| 4851 | 'tell you the layout constraints at a given location in the tree. See '
|
| 4852 | '<https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html> '
|
| 4853 | 'for more details.' ,
|
| 4854 | ),
|
| 4855 | ErrorSpacer(),
|
| 4856 | describeElement('The size getter was called for the following element' ),
|
| 4857 | ]);
|
| 4858 | }
|
| 4859 | return true;
|
| 4860 | }());
|
| 4861 | final RenderObject? renderObject = findRenderObject();
|
| 4862 | assert(() {
|
| 4863 | if (renderObject == null) {
|
| 4864 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4865 | ErrorSummary('Cannot get size without a render object.' ),
|
| 4866 | ErrorHint(
|
| 4867 | 'In order for an element to have a valid size, the element must have '
|
| 4868 | 'an associated render object. This element does not have an associated '
|
| 4869 | 'render object, which typically means that the size getter was called '
|
| 4870 | 'too early in the pipeline (e.g., during the build phase) before the '
|
| 4871 | 'framework has created the render tree.' ,
|
| 4872 | ),
|
| 4873 | describeElement('The size getter was called for the following element' ),
|
| 4874 | ]);
|
| 4875 | }
|
| 4876 | if (renderObject is RenderSliver) {
|
| 4877 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4878 | ErrorSummary('Cannot get size from a RenderSliver.' ),
|
| 4879 | ErrorHint(
|
| 4880 | 'The render object associated with this element is a '
|
| 4881 | ' ${renderObject.runtimeType}, which is a subtype of RenderSliver. '
|
| 4882 | 'Slivers do not have a size per se. They have a more elaborate '
|
| 4883 | 'geometry description, which can be accessed by calling '
|
| 4884 | 'findRenderObject and then using the "geometry" getter on the '
|
| 4885 | 'resulting object.' ,
|
| 4886 | ),
|
| 4887 | describeElement('The size getter was called for the following element' ),
|
| 4888 | renderObject.describeForError('The associated render sliver was' ),
|
| 4889 | ]);
|
| 4890 | }
|
| 4891 | if (renderObject is! RenderBox) {
|
| 4892 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4893 | ErrorSummary('Cannot get size from a render object that is not a RenderBox.' ),
|
| 4894 | ErrorHint(
|
| 4895 | 'Instead of being a subtype of RenderBox, the render object associated '
|
| 4896 | 'with this element is a ${renderObject.runtimeType}. If this type of '
|
| 4897 | 'render object does have a size, consider calling findRenderObject '
|
| 4898 | 'and extracting its size manually.' ,
|
| 4899 | ),
|
| 4900 | describeElement('The size getter was called for the following element' ),
|
| 4901 | renderObject.describeForError('The associated render object was' ),
|
| 4902 | ]);
|
| 4903 | }
|
| 4904 | final RenderBox box = renderObject;
|
| 4905 | if (!box.hasSize) {
|
| 4906 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4907 | ErrorSummary('Cannot get size from a render object that has not been through layout.' ),
|
| 4908 | ErrorHint(
|
| 4909 | 'The size of this render object has not yet been determined because '
|
| 4910 | 'this render object has not yet been through layout, which typically '
|
| 4911 | 'means that the size getter was called too early in the pipeline '
|
| 4912 | '(e.g., during the build phase) before the framework has determined '
|
| 4913 | 'the size and position of the render objects during layout.' ,
|
| 4914 | ),
|
| 4915 | describeElement('The size getter was called for the following element' ),
|
| 4916 | box.describeForError('The render object from which the size was to be obtained was' ),
|
| 4917 | ]);
|
| 4918 | }
|
| 4919 | if (box.debugNeedsLayout) {
|
| 4920 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4921 | ErrorSummary(
|
| 4922 | 'Cannot get size from a render object that has been marked dirty for layout.' ,
|
| 4923 | ),
|
| 4924 | ErrorHint(
|
| 4925 | 'The size of this render object is ambiguous because this render object has '
|
| 4926 | 'been modified since it was last laid out, which typically means that the size '
|
| 4927 | 'getter was called too early in the pipeline (e.g., during the build phase) '
|
| 4928 | 'before the framework has determined the size and position of the render '
|
| 4929 | 'objects during layout.' ,
|
| 4930 | ),
|
| 4931 | describeElement('The size getter was called for the following element' ),
|
| 4932 | box.describeForError('The render object from which the size was to be obtained was' ),
|
| 4933 | ErrorHint(
|
| 4934 | 'Consider using debugPrintMarkNeedsLayoutStacks to determine why the render '
|
| 4935 | 'object in question is dirty, if you did not expect this.' ,
|
| 4936 | ),
|
| 4937 | ]);
|
| 4938 | }
|
| 4939 | return true;
|
| 4940 | }());
|
| 4941 | if (renderObject is RenderBox) {
|
| 4942 | return renderObject.size;
|
| 4943 | }
|
| 4944 | return null;
|
| 4945 | }
|
| 4946 |
|
| 4947 | PersistentHashMap<Type, InheritedElement>? _inheritedElements;
|
| 4948 | Set<InheritedElement>? _dependencies;
|
| 4949 | bool _hadUnsatisfiedDependencies = false;
|
| 4950 |
|
| 4951 | bool _debugCheckStateIsActiveForAncestorLookup() {
|
| 4952 | assert(() {
|
| 4953 | if (_lifecycleState != _ElementLifecycle.active) {
|
| 4954 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 4955 | ErrorSummary("Looking up a deactivated widget's ancestor is unsafe." ),
|
| 4956 | ErrorDescription(
|
| 4957 | "At this point the state of the widget's element tree is no longer "
|
| 4958 | 'stable.' ,
|
| 4959 | ),
|
| 4960 | ErrorHint(
|
| 4961 | "To safely refer to a widget's ancestor in its dispose() method, "
|
| 4962 | 'save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() '
|
| 4963 | "in the widget's didChangeDependencies() method." ,
|
| 4964 | ),
|
| 4965 | ]);
|
| 4966 | }
|
| 4967 | return true;
|
| 4968 | }());
|
| 4969 | return true;
|
| 4970 | }
|
| 4971 |
|
| 4972 | /// Returns `true` if [dependOnInheritedElement] was previously called with [ancestor].
|
| 4973 | @protected
|
| 4974 | bool doesDependOnInheritedElement(InheritedElement ancestor) {
|
| 4975 | return _dependencies?.contains(ancestor) ?? false;
|
| 4976 | }
|
| 4977 |
|
| 4978 | @override
|
| 4979 | InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect}) {
|
| 4980 | _dependencies ??= HashSet<InheritedElement>();
|
| 4981 | _dependencies!.add(ancestor);
|
| 4982 | ancestor.updateDependencies(this, aspect);
|
| 4983 | return ancestor.widget as InheritedWidget;
|
| 4984 | }
|
| 4985 |
|
| 4986 | @override
|
| 4987 | T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
|
| 4988 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 4989 | final InheritedElement? ancestor = _inheritedElements?[T];
|
| 4990 | if (ancestor != null) {
|
| 4991 | return dependOnInheritedElement(ancestor, aspect: aspect) as T;
|
| 4992 | }
|
| 4993 | _hadUnsatisfiedDependencies = true;
|
| 4994 | return null;
|
| 4995 | }
|
| 4996 |
|
| 4997 | @override
|
| 4998 | T? getInheritedWidgetOfExactType<T extends InheritedWidget>() {
|
| 4999 | return getElementForInheritedWidgetOfExactType<T>()?.widget as T?;
|
| 5000 | }
|
| 5001 |
|
| 5002 | @override
|
| 5003 | InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
|
| 5004 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5005 | return _inheritedElements?[T];
|
| 5006 | }
|
| 5007 |
|
| 5008 | /// Called in [Element.mount] and [Element.activate] to register this element in
|
| 5009 | /// the notification tree.
|
| 5010 | ///
|
| 5011 | /// This method is only exposed so that [NotifiableElementMixin] can be implemented.
|
| 5012 | /// Subclasses of [Element] that wish to respond to notifications should mix that
|
| 5013 | /// in instead.
|
| 5014 | ///
|
| 5015 | /// See also:
|
| 5016 | /// * [NotificationListener], a widget that allows listening to notifications.
|
| 5017 | @protected
|
| 5018 | void attachNotificationTree() {
|
| 5019 | _notificationTree = _parent?._notificationTree;
|
| 5020 | }
|
| 5021 |
|
| 5022 | void _updateInheritance() {
|
| 5023 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 5024 | _inheritedElements = _parent?._inheritedElements;
|
| 5025 | }
|
| 5026 |
|
| 5027 | @override
|
| 5028 | T? findAncestorWidgetOfExactType<T extends Widget>() {
|
| 5029 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5030 | Element? ancestor = _parent;
|
| 5031 | while (ancestor != null && ancestor.widget.runtimeType != T) {
|
| 5032 | ancestor = ancestor._parent;
|
| 5033 | }
|
| 5034 | return ancestor?.widget as T?;
|
| 5035 | }
|
| 5036 |
|
| 5037 | @override
|
| 5038 | T? findAncestorStateOfType<T extends State<StatefulWidget>>() {
|
| 5039 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5040 | Element? ancestor = _parent;
|
| 5041 | while (ancestor != null) {
|
| 5042 | if (ancestor is StatefulElement && ancestor.state is T) {
|
| 5043 | break;
|
| 5044 | }
|
| 5045 | ancestor = ancestor._parent;
|
| 5046 | }
|
| 5047 | final StatefulElement? statefulAncestor = ancestor as StatefulElement?;
|
| 5048 | return statefulAncestor?.state as T?;
|
| 5049 | }
|
| 5050 |
|
| 5051 | @override
|
| 5052 | T? findRootAncestorStateOfType<T extends State<StatefulWidget>>() {
|
| 5053 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5054 | Element? ancestor = _parent;
|
| 5055 | StatefulElement? statefulAncestor;
|
| 5056 | while (ancestor != null) {
|
| 5057 | if (ancestor is StatefulElement && ancestor.state is T) {
|
| 5058 | statefulAncestor = ancestor;
|
| 5059 | }
|
| 5060 | ancestor = ancestor._parent;
|
| 5061 | }
|
| 5062 | return statefulAncestor?.state as T?;
|
| 5063 | }
|
| 5064 |
|
| 5065 | @override
|
| 5066 | T? findAncestorRenderObjectOfType<T extends RenderObject>() {
|
| 5067 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5068 | Element? ancestor = _parent;
|
| 5069 | while (ancestor != null) {
|
| 5070 | if (ancestor is RenderObjectElement && ancestor.renderObject is T) {
|
| 5071 | return ancestor.renderObject as T;
|
| 5072 | }
|
| 5073 | ancestor = ancestor._parent;
|
| 5074 | }
|
| 5075 | return null;
|
| 5076 | }
|
| 5077 |
|
| 5078 | @override
|
| 5079 | void visitAncestorElements(ConditionalElementVisitor visitor) {
|
| 5080 | assert(_debugCheckStateIsActiveForAncestorLookup());
|
| 5081 | Element? ancestor = _parent;
|
| 5082 | while (ancestor != null && visitor(ancestor)) {
|
| 5083 | ancestor = ancestor._parent;
|
| 5084 | }
|
| 5085 | }
|
| 5086 |
|
| 5087 | /// Called when a dependency of this element changes.
|
| 5088 | ///
|
| 5089 | /// The [dependOnInheritedWidgetOfExactType] registers this element as depending on
|
| 5090 | /// inherited information of the given type. When the information of that type
|
| 5091 | /// changes at this location in the tree (e.g., because the [InheritedElement]
|
| 5092 | /// updated to a new [InheritedWidget] and
|
| 5093 | /// [InheritedWidget.updateShouldNotify] returned true), the framework calls
|
| 5094 | /// this function to notify this element of the change.
|
| 5095 | @mustCallSuper
|
| 5096 | void didChangeDependencies() {
|
| 5097 | assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
|
| 5098 | assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies' ));
|
| 5099 | markNeedsBuild();
|
| 5100 | }
|
| 5101 |
|
| 5102 | bool _debugCheckOwnerBuildTargetExists(String methodName) {
|
| 5103 | assert(() {
|
| 5104 | if (owner!._debugCurrentBuildTarget == null) {
|
| 5105 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5106 | ErrorSummary(
|
| 5107 | ' $methodName for ${widget.runtimeType} was called at an '
|
| 5108 | 'inappropriate time.' ,
|
| 5109 | ),
|
| 5110 | ErrorDescription('It may only be called while the widgets are being built.' ),
|
| 5111 | ErrorHint(
|
| 5112 | 'A possible cause of this error is when $methodName is called during '
|
| 5113 | 'one of:\n'
|
| 5114 | ' * network I/O event\n'
|
| 5115 | ' * file I/O event\n'
|
| 5116 | ' * timer\n'
|
| 5117 | ' * microtask (caused by Future.then, async/await, scheduleMicrotask)' ,
|
| 5118 | ),
|
| 5119 | ]);
|
| 5120 | }
|
| 5121 | return true;
|
| 5122 | }());
|
| 5123 | return true;
|
| 5124 | }
|
| 5125 |
|
| 5126 | /// Returns a description of what caused this element to be created.
|
| 5127 | ///
|
| 5128 | /// Useful for debugging the source of an element.
|
| 5129 | String debugGetCreatorChain(int limit) {
|
| 5130 | final List<String> chain = <String>[];
|
| 5131 | Element? node = this;
|
| 5132 | while (chain.length < limit && node != null) {
|
| 5133 | chain.add(node.toStringShort());
|
| 5134 | node = node._parent;
|
| 5135 | }
|
| 5136 | if (node != null) {
|
| 5137 | chain.add('\u22EF' );
|
| 5138 | }
|
| 5139 | return chain.join(' \u2190 ' );
|
| 5140 | }
|
| 5141 |
|
| 5142 | /// Returns the parent chain from this element back to the root of the tree.
|
| 5143 | ///
|
| 5144 | /// Useful for debug display of a tree of Elements with only nodes in the path
|
| 5145 | /// from the root to this Element expanded.
|
| 5146 | List<Element> debugGetDiagnosticChain() {
|
| 5147 | final List<Element> chain = <Element>[this];
|
| 5148 | Element? node = _parent;
|
| 5149 | while (node != null) {
|
| 5150 | chain.add(node);
|
| 5151 | node = node._parent;
|
| 5152 | }
|
| 5153 | return chain;
|
| 5154 | }
|
| 5155 |
|
| 5156 | @override
|
| 5157 | void dispatchNotification(Notification notification) {
|
| 5158 | _notificationTree?.dispatchNotification(notification);
|
| 5159 | }
|
| 5160 |
|
| 5161 | /// A short, textual description of this element.
|
| 5162 | @override
|
| 5163 | String toStringShort() => _widget?.toStringShort() ?? ' ${describeIdentity(this)}(DEFUNCT)' ;
|
| 5164 |
|
| 5165 | @override
|
| 5166 | DiagnosticsNode toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) {
|
| 5167 | return _ElementDiagnosticableTreeNode(name: name, value: this, style: style);
|
| 5168 | }
|
| 5169 |
|
| 5170 | @override
|
| 5171 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
| 5172 | super.debugFillProperties(properties);
|
| 5173 | properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
|
| 5174 | if (_lifecycleState != _ElementLifecycle.initial) {
|
| 5175 | properties.add(ObjectFlagProperty<int>('depth' , depth, ifNull: 'no depth' ));
|
| 5176 | }
|
| 5177 | properties.add(ObjectFlagProperty<Widget>('widget' , _widget, ifNull: 'no widget' ));
|
| 5178 | properties.add(
|
| 5179 | DiagnosticsProperty<Key>(
|
| 5180 | 'key' ,
|
| 5181 | _widget?.key,
|
| 5182 | showName: false,
|
| 5183 | defaultValue: null,
|
| 5184 | level: DiagnosticLevel.hidden,
|
| 5185 | ),
|
| 5186 | );
|
| 5187 | _widget?.debugFillProperties(properties);
|
| 5188 | properties.add(FlagProperty('dirty' , value: dirty, ifTrue: 'dirty' ));
|
| 5189 | final Set<InheritedElement>? deps = _dependencies;
|
| 5190 | if (deps != null && deps.isNotEmpty) {
|
| 5191 | final List<InheritedElement> sortedDependencies = deps.toList()
|
| 5192 | ..sort(
|
| 5193 | (InheritedElement a, InheritedElement b) =>
|
| 5194 | a.toStringShort().compareTo(b.toStringShort()),
|
| 5195 | );
|
| 5196 | final List<DiagnosticsNode> diagnosticsDependencies = sortedDependencies
|
| 5197 | .map(
|
| 5198 | (InheritedElement element) =>
|
| 5199 | element.widget.toDiagnosticsNode(style: DiagnosticsTreeStyle.sparse),
|
| 5200 | )
|
| 5201 | .toList();
|
| 5202 | properties.add(
|
| 5203 | DiagnosticsProperty<Set<InheritedElement>>(
|
| 5204 | 'dependencies' ,
|
| 5205 | deps,
|
| 5206 | description: diagnosticsDependencies.toString(),
|
| 5207 | ),
|
| 5208 | );
|
| 5209 | }
|
| 5210 | }
|
| 5211 |
|
| 5212 | @override
|
| 5213 | List<DiagnosticsNode> debugDescribeChildren() {
|
| 5214 | final List<DiagnosticsNode> children = <DiagnosticsNode>[];
|
| 5215 | visitChildren((Element child) {
|
| 5216 | children.add(child.toDiagnosticsNode());
|
| 5217 | });
|
| 5218 | return children;
|
| 5219 | }
|
| 5220 |
|
| 5221 | /// Returns true if the element has been marked as needing rebuilding.
|
| 5222 | ///
|
| 5223 | /// The flag is true when the element is first created and after
|
| 5224 | /// [markNeedsBuild] has been called. The flag is typically reset to false in
|
| 5225 | /// the [performRebuild] implementation, but certain elements (that of the
|
| 5226 | /// [LayoutBuilder] widget, for example) may choose to override [markNeedsBuild]
|
| 5227 | /// such that it does not set the [dirty] flag to `true` when called.
|
| 5228 | bool get dirty => _dirty;
|
| 5229 | bool _dirty = true;
|
| 5230 |
|
| 5231 | // Whether this is in _buildScope._dirtyElements. This is used to know whether
|
| 5232 | // we should be adding the element back into the list when it's reactivated.
|
| 5233 | bool _inDirtyList = false;
|
| 5234 |
|
| 5235 | // Whether we've already built or not. Set in [rebuild].
|
| 5236 | bool _debugBuiltOnce = false;
|
| 5237 |
|
| 5238 | /// Marks the element as dirty and adds it to the global list of widgets to
|
| 5239 | /// rebuild in the next frame.
|
| 5240 | ///
|
| 5241 | /// Since it is inefficient to build an element twice in one frame,
|
| 5242 | /// applications and widgets should be structured so as to only mark
|
| 5243 | /// widgets dirty during event handlers before the frame begins, not during
|
| 5244 | /// the build itself.
|
| 5245 | void markNeedsBuild() {
|
| 5246 | assert(_lifecycleState != _ElementLifecycle.defunct);
|
| 5247 | if (_lifecycleState != _ElementLifecycle.active) {
|
| 5248 | return;
|
| 5249 | }
|
| 5250 | assert(owner != null);
|
| 5251 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 5252 | assert(() {
|
| 5253 | if (owner!._debugBuilding) {
|
| 5254 | assert(owner!._debugCurrentBuildTarget != null);
|
| 5255 | assert(owner!._debugStateLocked);
|
| 5256 | if (_debugIsDescendantOf(owner!._debugCurrentBuildTarget!)) {
|
| 5257 | return true;
|
| 5258 | }
|
| 5259 | final List<DiagnosticsNode> information = <DiagnosticsNode>[
|
| 5260 | ErrorSummary('setState() or markNeedsBuild() called during build.' ),
|
| 5261 | ErrorDescription(
|
| 5262 | 'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
|
| 5263 | 'is already in the process of building widgets. A widget can be marked as '
|
| 5264 | 'needing to be built during the build phase only if one of its ancestors '
|
| 5265 | 'is currently building. This exception is allowed because the framework '
|
| 5266 | 'builds parent widgets before children, which means a dirty descendant '
|
| 5267 | 'will always be built. Otherwise, the framework might not visit this '
|
| 5268 | 'widget during this build phase.' ,
|
| 5269 | ),
|
| 5270 | describeElement('The widget on which setState() or markNeedsBuild() was called was' ),
|
| 5271 | ];
|
| 5272 | if (owner!._debugCurrentBuildTarget != null) {
|
| 5273 | information.add(
|
| 5274 | owner!._debugCurrentBuildTarget!.describeWidget(
|
| 5275 | 'The widget which was currently being built when the offending call was made was' ,
|
| 5276 | ),
|
| 5277 | );
|
| 5278 | }
|
| 5279 | throw FlutterError.fromParts(information);
|
| 5280 | } else if (owner!._debugStateLocked) {
|
| 5281 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5282 | ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.' ),
|
| 5283 | ErrorDescription(
|
| 5284 | 'This ${widget.runtimeType} widget cannot be marked as needing to build '
|
| 5285 | 'because the framework is locked.' ,
|
| 5286 | ),
|
| 5287 | describeElement('The widget on which setState() or markNeedsBuild() was called was' ),
|
| 5288 | ]);
|
| 5289 | }
|
| 5290 | return true;
|
| 5291 | }());
|
| 5292 | if (dirty) {
|
| 5293 | return;
|
| 5294 | }
|
| 5295 | _dirty = true;
|
| 5296 | owner!.scheduleBuildFor(this);
|
| 5297 | }
|
| 5298 |
|
| 5299 | /// Cause the widget to update itself. In debug builds, also verify various
|
| 5300 | /// invariants.
|
| 5301 | ///
|
| 5302 | /// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
|
| 5303 | /// called to mark this element dirty, by [mount] when the element is first
|
| 5304 | /// built, and by [update] when the widget has changed.
|
| 5305 | ///
|
| 5306 | /// The method will only rebuild if [dirty] is true. To rebuild regardless
|
| 5307 | /// of the [dirty] flag, set `force` to true. Forcing a rebuild is convenient
|
| 5308 | /// from [update], during which [dirty] is false.
|
| 5309 | ///
|
| 5310 | /// ## When rebuilds happen
|
| 5311 | ///
|
| 5312 | /// ### Terminology
|
| 5313 | ///
|
| 5314 | /// [Widget]s represent the configuration of [Element]s. Each [Element] has a
|
| 5315 | /// widget, specified in [Element.widget]. The term "widget" is often used
|
| 5316 | /// when strictly speaking "element" would be more correct.
|
| 5317 | ///
|
| 5318 | /// While an [Element] has a current [Widget], over time, that widget may be
|
| 5319 | /// replaced by others. For example, the element backing a [ColoredBox] may
|
| 5320 | /// first have as its widget a [ColoredBox] whose [ColoredBox.color] is blue,
|
| 5321 | /// then later be given a new [ColoredBox] whose color is green.
|
| 5322 | ///
|
| 5323 | /// At any particular time, multiple [Element]s in the same tree may have the
|
| 5324 | /// same [Widget]. For example, the same [ColoredBox] with the green color may
|
| 5325 | /// be used in multiple places in the widget tree at the same time, each being
|
| 5326 | /// backed by a different [Element].
|
| 5327 | ///
|
| 5328 | /// ### Marking an element dirty
|
| 5329 | ///
|
| 5330 | /// An [Element] can be marked dirty between frames. This can happen for various
|
| 5331 | /// reasons, including the following:
|
| 5332 | ///
|
| 5333 | /// * The [State] of a [StatefulWidget] can cause its [Element] to be marked
|
| 5334 | /// dirty by calling the [State.setState] method.
|
| 5335 | ///
|
| 5336 | /// * When an [InheritedWidget] changes, descendants that have previously
|
| 5337 | /// subscribed to it will be marked dirty.
|
| 5338 | ///
|
| 5339 | /// * During a hot reload, every element is marked dirty (using [Element.reassemble]).
|
| 5340 | ///
|
| 5341 | /// ### Rebuilding
|
| 5342 | ///
|
| 5343 | /// Dirty elements are rebuilt during the next frame. Precisely how this is
|
| 5344 | /// done depends on the kind of element. A [StatelessElement] rebuilds by
|
| 5345 | /// using its widget's [StatelessWidget.build] method. A [StatefulElement]
|
| 5346 | /// rebuilds by using its widget's state's [State.build] method. A
|
| 5347 | /// [RenderObjectElement] rebuilds by updating its [RenderObject].
|
| 5348 | ///
|
| 5349 | /// In many cases, the end result of rebuilding is a single child widget
|
| 5350 | /// or (for [MultiChildRenderObjectElement]s) a list of children widgets.
|
| 5351 | ///
|
| 5352 | /// These child widgets are used to update the [widget] property of the
|
| 5353 | /// element's child (or children) elements. The new [Widget] is considered to
|
| 5354 | /// correspond to an existing [Element] if it has the same [Type] and [Key].
|
| 5355 | /// (In the case of [MultiChildRenderObjectElement]s, some effort is put into
|
| 5356 | /// tracking widgets even when they change order; see
|
| 5357 | /// [RenderObjectElement.updateChildren].)
|
| 5358 | ///
|
| 5359 | /// If there was no corresponding previous child, this results in a new
|
| 5360 | /// [Element] being created (using [Widget.createElement]); that element is
|
| 5361 | /// then itself built, recursively.
|
| 5362 | ///
|
| 5363 | /// If there was a child previously but the build did not provide a
|
| 5364 | /// corresponding child to update it, then the old child is discarded (or, in
|
| 5365 | /// cases involving [GlobalKey] reparenting, reused elsewhere in the element
|
| 5366 | /// tree).
|
| 5367 | ///
|
| 5368 | /// The most common case, however, is that there was a corresponding previous
|
| 5369 | /// child. This is handled by asking the child [Element] to update itself
|
| 5370 | /// using the new child [Widget]. In the case of [StatefulElement]s, this
|
| 5371 | /// is what triggers [State.didUpdateWidget].
|
| 5372 | ///
|
| 5373 | /// ### Not rebuilding
|
| 5374 | ///
|
| 5375 | /// Before an [Element] is told to update itself with a new [Widget], the old
|
| 5376 | /// and new objects are compared using `operator ==`.
|
| 5377 | ///
|
| 5378 | /// In general, this is equivalent to doing a comparison using [identical] to
|
| 5379 | /// see if the two objects are in fact the exact same instance. If they are,
|
| 5380 | /// and if the element is not already marked dirty for other reasons, then the
|
| 5381 | /// element skips updating itself as it can determine with certainty that
|
| 5382 | /// there would be no value in updating itself or its descendants.
|
| 5383 | ///
|
| 5384 | /// It is strongly advised to avoid overriding `operator ==` on [Widget]
|
| 5385 | /// objects. While doing so seems like it could improve performance, in
|
| 5386 | /// practice, for non-leaf widgets, it results in O(N²) behavior. This is
|
| 5387 | /// because by necessity the comparison would have to include comparing child
|
| 5388 | /// widgets, and if those child widgets also implement `operator ==`, it
|
| 5389 | /// ultimately results in a complete walk of the widget tree... which is then
|
| 5390 | /// repeated at each level of the tree. In practice, just rebuilding is
|
| 5391 | /// cheaper. (Additionally, if _any_ subclass of [Widget] used in an
|
| 5392 | /// application implements `operator ==`, then the compiler cannot inline the
|
| 5393 | /// comparison anywhere, because it has to treat the call as virtual just in
|
| 5394 | /// case the instance happens to be one that has an overridden operator.)
|
| 5395 | ///
|
| 5396 | /// Instead, the best way to avoid unnecessary rebuilds is to cache the
|
| 5397 | /// widgets that are returned from [State.build], so that each frame the same
|
| 5398 | /// widgets are used until such time as they change. Several mechanisms exist
|
| 5399 | /// to encourage this: `const` widgets, for example, are a form of automatic
|
| 5400 | /// caching (if a widget is constructed using the `const` keyword, the same
|
| 5401 | /// instance is returned each time it is constructed with the same arguments).
|
| 5402 | ///
|
| 5403 | /// Another example is the [AnimatedBuilder.child] property, which allows the
|
| 5404 | /// non-animating parts of a subtree to remain static even as the
|
| 5405 | /// [AnimatedBuilder.builder] callback recreates the other components.
|
| 5406 | @pragma('dart2js:tryInline' )
|
| 5407 | @pragma('vm:prefer-inline' )
|
| 5408 | @pragma('wasm:prefer-inline' )
|
| 5409 | void rebuild({bool force = false}) {
|
| 5410 | assert(_lifecycleState != _ElementLifecycle.initial);
|
| 5411 | if (_lifecycleState != _ElementLifecycle.active || (!_dirty && !force)) {
|
| 5412 | return;
|
| 5413 | }
|
| 5414 | assert(() {
|
| 5415 | debugOnRebuildDirtyWidget?.call(this, _debugBuiltOnce);
|
| 5416 | if (debugPrintRebuildDirtyWidgets) {
|
| 5417 | if (!_debugBuiltOnce) {
|
| 5418 | debugPrint('Building $this' );
|
| 5419 | _debugBuiltOnce = true;
|
| 5420 | } else {
|
| 5421 | debugPrint('Rebuilding $this' );
|
| 5422 | }
|
| 5423 | }
|
| 5424 | return true;
|
| 5425 | }());
|
| 5426 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 5427 | assert(owner!._debugStateLocked);
|
| 5428 | Element? debugPreviousBuildTarget;
|
| 5429 | assert(() {
|
| 5430 | debugPreviousBuildTarget = owner!._debugCurrentBuildTarget;
|
| 5431 | owner!._debugCurrentBuildTarget = this;
|
| 5432 | return true;
|
| 5433 | }());
|
| 5434 | try {
|
| 5435 | performRebuild();
|
| 5436 | } finally {
|
| 5437 | assert(() {
|
| 5438 | owner!._debugElementWasRebuilt(this);
|
| 5439 | assert(owner!._debugCurrentBuildTarget == this);
|
| 5440 | owner!._debugCurrentBuildTarget = debugPreviousBuildTarget;
|
| 5441 | return true;
|
| 5442 | }());
|
| 5443 | }
|
| 5444 | assert(!_dirty);
|
| 5445 | }
|
| 5446 |
|
| 5447 | /// Cause the widget to update itself.
|
| 5448 | ///
|
| 5449 | /// Called by [rebuild] after the appropriate checks have been made.
|
| 5450 | ///
|
| 5451 | /// The base implementation only clears the [dirty] flag.
|
| 5452 | @protected
|
| 5453 | @mustCallSuper
|
| 5454 | void performRebuild() {
|
| 5455 | _dirty = false;
|
| 5456 | }
|
| 5457 | }
|
| 5458 |
|
| 5459 | class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode {
|
| 5460 | _ElementDiagnosticableTreeNode({
|
| 5461 | super.name,
|
| 5462 | required Element super.value,
|
| 5463 | required super.style,
|
| 5464 | this.stateful = false,
|
| 5465 | });
|
| 5466 |
|
| 5467 | final bool stateful;
|
| 5468 |
|
| 5469 | @override
|
| 5470 | Map<String, Object?> toJsonMap(DiagnosticsSerializationDelegate delegate) {
|
| 5471 | final Map<String, Object?> json = super.toJsonMap(delegate);
|
| 5472 | final Element element = value as Element;
|
| 5473 | if (!element.debugIsDefunct) {
|
| 5474 | json['widgetRuntimeType' ] = element.widget.runtimeType.toString();
|
| 5475 | }
|
| 5476 | json['stateful' ] = stateful;
|
| 5477 | return json;
|
| 5478 | }
|
| 5479 | }
|
| 5480 |
|
| 5481 | /// Signature for the constructor that is called when an error occurs while
|
| 5482 | /// building a widget.
|
| 5483 | ///
|
| 5484 | /// The argument provides information regarding the cause of the error.
|
| 5485 | ///
|
| 5486 | /// See also:
|
| 5487 | ///
|
| 5488 | /// * [ErrorWidget.builder], which can be set to override the default
|
| 5489 | /// [ErrorWidget] builder.
|
| 5490 | /// * [FlutterError.reportError], which is typically called with the same
|
| 5491 | /// [FlutterErrorDetails] object immediately prior to [ErrorWidget.builder]
|
| 5492 | /// being called.
|
| 5493 | typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details);
|
| 5494 |
|
| 5495 | /// A widget that renders an exception's message.
|
| 5496 | ///
|
| 5497 | /// This widget is used when a build method fails, to help with determining
|
| 5498 | /// where the problem lies. Exceptions are also logged to the console, which you
|
| 5499 | /// can read using `flutter logs`. The console will also include additional
|
| 5500 | /// information such as the stack trace for the exception.
|
| 5501 | ///
|
| 5502 | /// It is possible to override this widget.
|
| 5503 | ///
|
| 5504 | /// {@tool dartpad}
|
| 5505 | /// This example shows how to override the standard error widget builder in release
|
| 5506 | /// mode, but use the standard one in debug mode.
|
| 5507 | ///
|
| 5508 | /// The error occurs when you click the "Error Prone" button.
|
| 5509 | ///
|
| 5510 | /// ** See code in examples/api/lib/widgets/framework/error_widget.0.dart **
|
| 5511 | /// {@end-tool}
|
| 5512 | ///
|
| 5513 | /// See also:
|
| 5514 | ///
|
| 5515 | /// * [FlutterError.onError], which can be set to a method that exits the
|
| 5516 | /// application if that is preferable to showing an error message.
|
| 5517 | /// * <https://docs.flutter.dev/testing/errors>, more information about error
|
| 5518 | /// handling in Flutter.
|
| 5519 | class ErrorWidget extends LeafRenderObjectWidget {
|
| 5520 | /// Creates a widget that displays the given exception.
|
| 5521 | ///
|
| 5522 | /// The message will be the stringification of the given exception, unless
|
| 5523 | /// computing that value itself throws an exception, in which case it will
|
| 5524 | /// be the string "Error".
|
| 5525 | ///
|
| 5526 | /// If this object is inspected from an IDE or the devtools, and the original
|
| 5527 | /// exception is a [FlutterError] object, the original exception itself will
|
| 5528 | /// be shown in the inspection output.
|
| 5529 | ErrorWidget(Object exception)
|
| 5530 | : message = _stringify(exception),
|
| 5531 | _flutterError = exception is FlutterError ? exception : null,
|
| 5532 | super(key: UniqueKey());
|
| 5533 |
|
| 5534 | /// Creates a widget that displays the given error message.
|
| 5535 | ///
|
| 5536 | /// An explicit [FlutterError] can be provided to be reported to inspection
|
| 5537 | /// tools. It need not match the message.
|
| 5538 | ErrorWidget.withDetails({this.message = '' , FlutterError? error})
|
| 5539 | : _flutterError = error,
|
| 5540 | super(key: UniqueKey());
|
| 5541 |
|
| 5542 | /// The configurable factory for [ErrorWidget].
|
| 5543 | ///
|
| 5544 | /// When an error occurs while building a widget, the broken widget is
|
| 5545 | /// replaced by the widget returned by this function. By default, an
|
| 5546 | /// [ErrorWidget] is returned.
|
| 5547 | ///
|
| 5548 | /// The system is typically in an unstable state when this function is called.
|
| 5549 | /// An exception has just been thrown in the middle of build (and possibly
|
| 5550 | /// layout), so surrounding widgets and render objects may be in a rather
|
| 5551 | /// fragile state. The framework itself (especially the [BuildOwner]) may also
|
| 5552 | /// be confused, and additional exceptions are quite likely to be thrown.
|
| 5553 | ///
|
| 5554 | /// Because of this, it is highly recommended that the widget returned from
|
| 5555 | /// this function perform the least amount of work possible. A
|
| 5556 | /// [LeafRenderObjectWidget] is the best choice, especially one that
|
| 5557 | /// corresponds to a [RenderBox] that can handle the most absurd of incoming
|
| 5558 | /// constraints. The default constructor maps to a [RenderErrorBox].
|
| 5559 | ///
|
| 5560 | /// The default behavior is to show the exception's message in debug mode,
|
| 5561 | /// and to show nothing but a gray background in release builds.
|
| 5562 | ///
|
| 5563 | /// See also:
|
| 5564 | ///
|
| 5565 | /// * [FlutterError.onError], which is typically called with the same
|
| 5566 | /// [FlutterErrorDetails] object immediately prior to this callback being
|
| 5567 | /// invoked, and which can also be configured to control how errors are
|
| 5568 | /// reported.
|
| 5569 | /// * <https://docs.flutter.dev/testing/errors>, more information about error
|
| 5570 | /// handling in Flutter.
|
| 5571 | static ErrorWidgetBuilder builder = _defaultErrorWidgetBuilder;
|
| 5572 |
|
| 5573 | static Widget _defaultErrorWidgetBuilder(FlutterErrorDetails details) {
|
| 5574 | String message = '' ;
|
| 5575 | assert(() {
|
| 5576 | message =
|
| 5577 | ' ${_stringify(details.exception)}\nSee also: https://docs.flutter.dev/testing/errors';
|
| 5578 | return true;
|
| 5579 | }());
|
| 5580 | final Object exception = details.exception;
|
| 5581 | return ErrorWidget.withDetails(
|
| 5582 | message: message,
|
| 5583 | error: exception is FlutterError ? exception : null,
|
| 5584 | );
|
| 5585 | }
|
| 5586 |
|
| 5587 | static String _stringify(Object? exception) {
|
| 5588 | try {
|
| 5589 | return exception.toString();
|
| 5590 | } catch (error) {
|
| 5591 | // If we get here, it means things have really gone off the rails, and we're better
|
| 5592 | // off just returning a simple string and letting the developer find out what the
|
| 5593 | // root cause of all their problems are by looking at the console logs.
|
| 5594 | }
|
| 5595 | return 'Error' ;
|
| 5596 | }
|
| 5597 |
|
| 5598 | /// The message to display.
|
| 5599 | final String message;
|
| 5600 | final FlutterError? _flutterError;
|
| 5601 |
|
| 5602 | @override
|
| 5603 | RenderBox createRenderObject(BuildContext context) => RenderErrorBox(message);
|
| 5604 |
|
| 5605 | @override
|
| 5606 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
| 5607 | super.debugFillProperties(properties);
|
| 5608 | if (_flutterError == null) {
|
| 5609 | properties.add(StringProperty('message' , message, quoted: false));
|
| 5610 | } else {
|
| 5611 | properties.add(_flutterError.toDiagnosticsNode(style: DiagnosticsTreeStyle.whitespace));
|
| 5612 | }
|
| 5613 | }
|
| 5614 | }
|
| 5615 |
|
| 5616 | /// Signature for a function that creates a widget, e.g. [StatelessWidget.build]
|
| 5617 | /// or [State.build].
|
| 5618 | ///
|
| 5619 | /// Used by [Builder.builder], [OverlayEntry.builder], etc.
|
| 5620 | ///
|
| 5621 | /// See also:
|
| 5622 | ///
|
| 5623 | /// * [IndexedWidgetBuilder], which is similar but also takes an index.
|
| 5624 | /// * [TransitionBuilder], which is similar but also takes a child.
|
| 5625 | /// * [ValueWidgetBuilder], which is similar but takes a value and a child.
|
| 5626 | typedef WidgetBuilder = Widget Function(BuildContext context);
|
| 5627 |
|
| 5628 | /// Signature for a function that creates a widget for a given index, e.g., in a
|
| 5629 | /// list.
|
| 5630 | ///
|
| 5631 | /// Used by [ListView.builder] and other APIs that use lazily-generated widgets.
|
| 5632 | ///
|
| 5633 | /// See also:
|
| 5634 | ///
|
| 5635 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext].
|
| 5636 | /// * [TransitionBuilder], which is similar but also takes a child.
|
| 5637 | /// * [NullableIndexedWidgetBuilder], which is similar but may return null.
|
| 5638 | typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index);
|
| 5639 |
|
| 5640 | /// Signature for a function that creates a widget for a given index, e.g., in a
|
| 5641 | /// list, but may return null.
|
| 5642 | ///
|
| 5643 | /// Used by [SliverChildBuilderDelegate.builder] and other APIs that
|
| 5644 | /// use lazily-generated widgets where the child count is not known
|
| 5645 | /// ahead of time.
|
| 5646 | ///
|
| 5647 | /// Unlike most builders, this callback can return null, indicating the index
|
| 5648 | /// is out of range. Whether and when this is valid depends on the semantics
|
| 5649 | /// of the builder. For example, [SliverChildBuilderDelegate.builder] returns
|
| 5650 | /// null when the index is out of range, where the range is defined by the
|
| 5651 | /// [SliverChildBuilderDelegate.childCount]; so in that case the `index`
|
| 5652 | /// parameter's value may determine whether returning null is valid or not.
|
| 5653 | ///
|
| 5654 | /// See also:
|
| 5655 | ///
|
| 5656 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext].
|
| 5657 | /// * [TransitionBuilder], which is similar but also takes a child.
|
| 5658 | /// * [IndexedWidgetBuilder], which is similar but not nullable.
|
| 5659 | typedef NullableIndexedWidgetBuilder = Widget? Function(BuildContext context, int index);
|
| 5660 |
|
| 5661 | /// A builder that builds a widget given a child.
|
| 5662 | ///
|
| 5663 | /// The child should typically be part of the returned widget tree.
|
| 5664 | ///
|
| 5665 | /// Used by [AnimatedBuilder.builder], [ListenableBuilder.builder],
|
| 5666 | /// [WidgetsApp.builder], and [MaterialApp.builder].
|
| 5667 | ///
|
| 5668 | /// See also:
|
| 5669 | ///
|
| 5670 | /// * [WidgetBuilder], which is similar but only takes a [BuildContext].
|
| 5671 | /// * [IndexedWidgetBuilder], which is similar but also takes an index.
|
| 5672 | /// * [ValueWidgetBuilder], which is similar but takes a value and a child.
|
| 5673 | typedef TransitionBuilder = Widget Function(BuildContext context, Widget? child);
|
| 5674 |
|
| 5675 | /// An [Element] that composes other [Element]s.
|
| 5676 | ///
|
| 5677 | /// Rather than creating a [RenderObject] directly, a [ComponentElement] creates
|
| 5678 | /// [RenderObject]s indirectly by creating other [Element]s.
|
| 5679 | ///
|
| 5680 | /// Contrast with [RenderObjectElement].
|
| 5681 | abstract class ComponentElement extends Element {
|
| 5682 | /// Creates an element that uses the given widget as its configuration.
|
| 5683 | ComponentElement(super.widget);
|
| 5684 |
|
| 5685 | Element? _child;
|
| 5686 |
|
| 5687 | bool _debugDoingBuild = false;
|
| 5688 | @override
|
| 5689 | bool get debugDoingBuild => _debugDoingBuild;
|
| 5690 |
|
| 5691 | @override
|
| 5692 | Element? get renderObjectAttachingChild => _child;
|
| 5693 |
|
| 5694 | @override
|
| 5695 | void mount(Element? parent, Object? newSlot) {
|
| 5696 | super.mount(parent, newSlot);
|
| 5697 | assert(_child == null);
|
| 5698 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 5699 | _firstBuild();
|
| 5700 | assert(_child != null);
|
| 5701 | }
|
| 5702 |
|
| 5703 | void _firstBuild() {
|
| 5704 | // StatefulElement overrides this to also call state.didChangeDependencies.
|
| 5705 | rebuild(); // This eventually calls performRebuild.
|
| 5706 | }
|
| 5707 |
|
| 5708 | /// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
|
| 5709 | /// (for stateless widgets) or the [State.build] method of the [State] object
|
| 5710 | /// (for stateful widgets) and then updates the widget tree.
|
| 5711 | ///
|
| 5712 | /// Called automatically during [mount] to generate the first build, and by
|
| 5713 | /// [rebuild] when the element needs updating.
|
| 5714 | @override
|
| 5715 | @pragma('vm:notify-debugger-on-exception' )
|
| 5716 | void performRebuild() {
|
| 5717 | Widget? built;
|
| 5718 | try {
|
| 5719 | assert(() {
|
| 5720 | _debugDoingBuild = true;
|
| 5721 | return true;
|
| 5722 | }());
|
| 5723 | built = build();
|
| 5724 | assert(() {
|
| 5725 | _debugDoingBuild = false;
|
| 5726 | return true;
|
| 5727 | }());
|
| 5728 | debugWidgetBuilderValue(widget, built);
|
| 5729 | } catch (e, stack) {
|
| 5730 | _debugDoingBuild = false;
|
| 5731 | built = ErrorWidget.builder(
|
| 5732 | _reportException(
|
| 5733 | ErrorDescription('building $this' ),
|
| 5734 | e,
|
| 5735 | stack,
|
| 5736 | informationCollector: () => <DiagnosticsNode>[
|
| 5737 | if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this)),
|
| 5738 | ],
|
| 5739 | ),
|
| 5740 | );
|
| 5741 | } finally {
|
| 5742 | // We delay marking the element as clean until after calling build() so
|
| 5743 | // that attempts to markNeedsBuild() during build() will be ignored.
|
| 5744 | super.performRebuild(); // clears the "dirty" flag
|
| 5745 | }
|
| 5746 | try {
|
| 5747 | _child = updateChild(_child, built, slot);
|
| 5748 | assert(_child != null);
|
| 5749 | } catch (e, stack) {
|
| 5750 | built = ErrorWidget.builder(
|
| 5751 | _reportException(
|
| 5752 | ErrorDescription('building $this' ),
|
| 5753 | e,
|
| 5754 | stack,
|
| 5755 | informationCollector: () => <DiagnosticsNode>[
|
| 5756 | if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this)),
|
| 5757 | ],
|
| 5758 | ),
|
| 5759 | );
|
| 5760 | _child = updateChild(null, built, slot);
|
| 5761 | }
|
| 5762 | }
|
| 5763 |
|
| 5764 | /// Subclasses should override this function to actually call the appropriate
|
| 5765 | /// `build` function (e.g., [StatelessWidget.build] or [State.build]) for
|
| 5766 | /// their widget.
|
| 5767 | @protected
|
| 5768 | Widget build();
|
| 5769 |
|
| 5770 | @override
|
| 5771 | void visitChildren(ElementVisitor visitor) {
|
| 5772 | if (_child != null) {
|
| 5773 | visitor(_child!);
|
| 5774 | }
|
| 5775 | }
|
| 5776 |
|
| 5777 | @override
|
| 5778 | void forgetChild(Element child) {
|
| 5779 | assert(child == _child);
|
| 5780 | _child = null;
|
| 5781 | super.forgetChild(child);
|
| 5782 | }
|
| 5783 | }
|
| 5784 |
|
| 5785 | /// An [Element] that uses a [StatelessWidget] as its configuration.
|
| 5786 | class StatelessElement extends ComponentElement {
|
| 5787 | /// Creates an element that uses the given widget as its configuration.
|
| 5788 | StatelessElement(StatelessWidget super.widget);
|
| 5789 |
|
| 5790 | @override
|
| 5791 | Widget build() => (widget as StatelessWidget).build(this);
|
| 5792 |
|
| 5793 | @override
|
| 5794 | void update(StatelessWidget newWidget) {
|
| 5795 | super.update(newWidget);
|
| 5796 | assert(widget == newWidget);
|
| 5797 | rebuild(force: true);
|
| 5798 | }
|
| 5799 | }
|
| 5800 |
|
| 5801 | /// An [Element] that uses a [StatefulWidget] as its configuration.
|
| 5802 | class StatefulElement extends ComponentElement {
|
| 5803 | /// Creates an element that uses the given widget as its configuration.
|
| 5804 | StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) {
|
| 5805 | assert(() {
|
| 5806 | if (!state._debugTypesAreRight(widget)) {
|
| 5807 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5808 | ErrorSummary(
|
| 5809 | 'StatefulWidget.createState must return a subtype of State< ${widget.runtimeType}>' ,
|
| 5810 | ),
|
| 5811 | ErrorDescription(
|
| 5812 | 'The createState function for ${widget.runtimeType} returned a state '
|
| 5813 | 'of type ${state.runtimeType}, which is not a subtype of '
|
| 5814 | 'State< ${widget.runtimeType}>, violating the contract for createState.' ,
|
| 5815 | ),
|
| 5816 | ]);
|
| 5817 | }
|
| 5818 | return true;
|
| 5819 | }());
|
| 5820 | assert(state._element == null);
|
| 5821 | state._element = this;
|
| 5822 | assert(
|
| 5823 | state._widget == null,
|
| 5824 | 'The createState function for $widget returned an old or invalid state '
|
| 5825 | 'instance: ${state._widget}, which is not null, violating the contract '
|
| 5826 | 'for createState.' ,
|
| 5827 | );
|
| 5828 | state._widget = widget;
|
| 5829 | assert(state._debugLifecycleState == _StateLifecycle.created);
|
| 5830 | }
|
| 5831 |
|
| 5832 | @override
|
| 5833 | Widget build() => state.build(this);
|
| 5834 |
|
| 5835 | /// The [State] instance associated with this location in the tree.
|
| 5836 | ///
|
| 5837 | /// There is a one-to-one relationship between [State] objects and the
|
| 5838 | /// [StatefulElement] objects that hold them. The [State] objects are created
|
| 5839 | /// by [StatefulElement] in [mount].
|
| 5840 | State<StatefulWidget> get state => _state!;
|
| 5841 | State<StatefulWidget>? _state;
|
| 5842 |
|
| 5843 | @override
|
| 5844 | void reassemble() {
|
| 5845 | state.reassemble();
|
| 5846 | super.reassemble();
|
| 5847 | }
|
| 5848 |
|
| 5849 | @override
|
| 5850 | void _firstBuild() {
|
| 5851 | assert(state._debugLifecycleState == _StateLifecycle.created);
|
| 5852 | final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
|
| 5853 | assert(() {
|
| 5854 | if (debugCheckForReturnedFuture is Future) {
|
| 5855 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5856 | ErrorSummary(' ${state.runtimeType}.initState() returned a Future.' ),
|
| 5857 | ErrorDescription('State.initState() must be a void method without an `async` keyword.' ),
|
| 5858 | ErrorHint(
|
| 5859 | 'Rather than awaiting on asynchronous work directly inside of initState, '
|
| 5860 | 'call a separate method to do this work without awaiting it.' ,
|
| 5861 | ),
|
| 5862 | ]);
|
| 5863 | }
|
| 5864 | return true;
|
| 5865 | }());
|
| 5866 | assert(() {
|
| 5867 | state._debugLifecycleState = _StateLifecycle.initialized;
|
| 5868 | return true;
|
| 5869 | }());
|
| 5870 | state.didChangeDependencies();
|
| 5871 | assert(() {
|
| 5872 | state._debugLifecycleState = _StateLifecycle.ready;
|
| 5873 | return true;
|
| 5874 | }());
|
| 5875 | super._firstBuild();
|
| 5876 | }
|
| 5877 |
|
| 5878 | @override
|
| 5879 | void performRebuild() {
|
| 5880 | if (_didChangeDependencies) {
|
| 5881 | state.didChangeDependencies();
|
| 5882 | _didChangeDependencies = false;
|
| 5883 | }
|
| 5884 | super.performRebuild();
|
| 5885 | }
|
| 5886 |
|
| 5887 | @override
|
| 5888 | void update(StatefulWidget newWidget) {
|
| 5889 | super.update(newWidget);
|
| 5890 | assert(widget == newWidget);
|
| 5891 | final StatefulWidget oldWidget = state._widget!;
|
| 5892 | state._widget = widget as StatefulWidget;
|
| 5893 | final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
|
| 5894 | assert(() {
|
| 5895 | if (debugCheckForReturnedFuture is Future) {
|
| 5896 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5897 | ErrorSummary(' ${state.runtimeType}.didUpdateWidget() returned a Future.' ),
|
| 5898 | ErrorDescription(
|
| 5899 | 'State.didUpdateWidget() must be a void method without an `async` keyword.' ,
|
| 5900 | ),
|
| 5901 | ErrorHint(
|
| 5902 | 'Rather than awaiting on asynchronous work directly inside of didUpdateWidget, '
|
| 5903 | 'call a separate method to do this work without awaiting it.' ,
|
| 5904 | ),
|
| 5905 | ]);
|
| 5906 | }
|
| 5907 | return true;
|
| 5908 | }());
|
| 5909 | rebuild(force: true);
|
| 5910 | }
|
| 5911 |
|
| 5912 | @override
|
| 5913 | void activate() {
|
| 5914 | super.activate();
|
| 5915 | state.activate();
|
| 5916 | // Since the State could have observed the deactivate() and thus disposed of
|
| 5917 | // resources allocated in the build method, we have to rebuild the widget
|
| 5918 | // so that its State can reallocate its resources.
|
| 5919 | assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
|
| 5920 | markNeedsBuild();
|
| 5921 | }
|
| 5922 |
|
| 5923 | @override
|
| 5924 | void deactivate() {
|
| 5925 | state.deactivate();
|
| 5926 | super.deactivate();
|
| 5927 | }
|
| 5928 |
|
| 5929 | @override
|
| 5930 | void unmount() {
|
| 5931 | super.unmount();
|
| 5932 | state.dispose();
|
| 5933 | assert(() {
|
| 5934 | if (state._debugLifecycleState == _StateLifecycle.defunct) {
|
| 5935 | return true;
|
| 5936 | }
|
| 5937 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5938 | ErrorSummary(' ${state.runtimeType}.dispose failed to call super.dispose.' ),
|
| 5939 | ErrorDescription(
|
| 5940 | 'dispose() implementations must always call their superclass dispose() method, to ensure '
|
| 5941 | 'that all the resources used by the widget are fully released.' ,
|
| 5942 | ),
|
| 5943 | ]);
|
| 5944 | }());
|
| 5945 | state._element = null;
|
| 5946 | // Release resources to reduce the severity of memory leaks caused by
|
| 5947 | // defunct, but accidentally retained Elements.
|
| 5948 | _state = null;
|
| 5949 | }
|
| 5950 |
|
| 5951 | @override
|
| 5952 | InheritedWidget dependOnInheritedElement(Element ancestor, {Object? aspect}) {
|
| 5953 | assert(() {
|
| 5954 | final Type targetType = ancestor.widget.runtimeType;
|
| 5955 | if (state._debugLifecycleState == _StateLifecycle.created) {
|
| 5956 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5957 | ErrorSummary(
|
| 5958 | 'dependOnInheritedWidgetOfExactType< $targetType>() or dependOnInheritedElement() was called before ${state.runtimeType}.initState() completed.' ,
|
| 5959 | ),
|
| 5960 | ErrorDescription(
|
| 5961 | 'When an inherited widget changes, for example if the value of Theme.of() changes, '
|
| 5962 | "its dependent widgets are rebuilt. If the dependent widget's reference to "
|
| 5963 | 'the inherited widget is in a constructor or an initState() method, '
|
| 5964 | 'then the rebuilt dependent widget will not reflect the changes in the '
|
| 5965 | 'inherited widget.' ,
|
| 5966 | ),
|
| 5967 | ErrorHint(
|
| 5968 | 'Typically references to inherited widgets should occur in widget build() methods. Alternatively, '
|
| 5969 | 'initialization based on inherited widgets can be placed in the didChangeDependencies method, which '
|
| 5970 | 'is called after initState and whenever the dependencies change thereafter.' ,
|
| 5971 | ),
|
| 5972 | ]);
|
| 5973 | }
|
| 5974 | if (state._debugLifecycleState == _StateLifecycle.defunct) {
|
| 5975 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 5976 | ErrorSummary(
|
| 5977 | 'dependOnInheritedWidgetOfExactType< $targetType>() or dependOnInheritedElement() was called after dispose(): $this' ,
|
| 5978 | ),
|
| 5979 | ErrorDescription(
|
| 5980 | 'This error happens if you call dependOnInheritedWidgetOfExactType() on the '
|
| 5981 | 'BuildContext for a widget that no longer appears in the widget tree '
|
| 5982 | '(e.g., whose parent widget no longer includes the widget in its '
|
| 5983 | 'build). This error can occur when code calls '
|
| 5984 | 'dependOnInheritedWidgetOfExactType() from a timer or an animation callback.' ,
|
| 5985 | ),
|
| 5986 | ErrorHint(
|
| 5987 | 'The preferred solution is to cancel the timer or stop listening to the '
|
| 5988 | 'animation in the dispose() callback. Another solution is to check the '
|
| 5989 | '"mounted" property of this object before calling '
|
| 5990 | 'dependOnInheritedWidgetOfExactType() to ensure the object is still in the '
|
| 5991 | 'tree.' ,
|
| 5992 | ),
|
| 5993 | ErrorHint(
|
| 5994 | 'This error might indicate a memory leak if '
|
| 5995 | 'dependOnInheritedWidgetOfExactType() is being called because another object '
|
| 5996 | 'is retaining a reference to this State object after it has been '
|
| 5997 | 'removed from the tree. To avoid memory leaks, consider breaking the '
|
| 5998 | 'reference to this object during dispose().' ,
|
| 5999 | ),
|
| 6000 | ]);
|
| 6001 | }
|
| 6002 | return true;
|
| 6003 | }());
|
| 6004 | return super.dependOnInheritedElement(ancestor as InheritedElement, aspect: aspect);
|
| 6005 | }
|
| 6006 |
|
| 6007 | /// This controls whether we should call [State.didChangeDependencies] from
|
| 6008 | /// the start of [build], to avoid calls when the [State] will not get built.
|
| 6009 | /// This can happen when the widget has dropped out of the tree, but depends
|
| 6010 | /// on an [InheritedWidget] that is still in the tree.
|
| 6011 | ///
|
| 6012 | /// It is set initially to false, since [_firstBuild] makes the initial call
|
| 6013 | /// on the [state]. When it is true, [build] will call
|
| 6014 | /// `state.didChangeDependencies` and then sets it to false. Subsequent calls
|
| 6015 | /// to [didChangeDependencies] set it to true.
|
| 6016 | bool _didChangeDependencies = false;
|
| 6017 |
|
| 6018 | @override
|
| 6019 | void didChangeDependencies() {
|
| 6020 | super.didChangeDependencies();
|
| 6021 | _didChangeDependencies = true;
|
| 6022 | }
|
| 6023 |
|
| 6024 | @override
|
| 6025 | DiagnosticsNode toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) {
|
| 6026 | return _ElementDiagnosticableTreeNode(name: name, value: this, style: style, stateful: true);
|
| 6027 | }
|
| 6028 |
|
| 6029 | @override
|
| 6030 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
| 6031 | super.debugFillProperties(properties);
|
| 6032 | properties.add(DiagnosticsProperty<State<StatefulWidget>>('state' , _state, defaultValue: null));
|
| 6033 | }
|
| 6034 | }
|
| 6035 |
|
| 6036 | /// An [Element] that uses a [ProxyWidget] as its configuration.
|
| 6037 | abstract class ProxyElement extends ComponentElement {
|
| 6038 | /// Initializes fields for subclasses.
|
| 6039 | ProxyElement(ProxyWidget super.widget);
|
| 6040 |
|
| 6041 | @override
|
| 6042 | Widget build() => (widget as ProxyWidget).child;
|
| 6043 |
|
| 6044 | @override
|
| 6045 | void update(ProxyWidget newWidget) {
|
| 6046 | final ProxyWidget oldWidget = widget as ProxyWidget;
|
| 6047 | assert(widget != newWidget);
|
| 6048 | super.update(newWidget);
|
| 6049 | assert(widget == newWidget);
|
| 6050 | updated(oldWidget);
|
| 6051 | rebuild(force: true);
|
| 6052 | }
|
| 6053 |
|
| 6054 | /// Called during build when the [widget] has changed.
|
| 6055 | ///
|
| 6056 | /// By default, calls [notifyClients]. Subclasses may override this method to
|
| 6057 | /// avoid calling [notifyClients] unnecessarily (e.g. if the old and new
|
| 6058 | /// widgets are equivalent).
|
| 6059 | @protected
|
| 6060 | void updated(covariant ProxyWidget oldWidget) {
|
| 6061 | notifyClients(oldWidget);
|
| 6062 | }
|
| 6063 |
|
| 6064 | /// Notify other objects that the widget associated with this element has
|
| 6065 | /// changed.
|
| 6066 | ///
|
| 6067 | /// Called during [update] (via [updated]) after changing the widget
|
| 6068 | /// associated with this element but before rebuilding this element.
|
| 6069 | @protected
|
| 6070 | void notifyClients(covariant ProxyWidget oldWidget);
|
| 6071 | }
|
| 6072 |
|
| 6073 | /// An [Element] that uses a [ParentDataWidget] as its configuration.
|
| 6074 | class ParentDataElement<T extends ParentData> extends ProxyElement {
|
| 6075 | /// Creates an element that uses the given widget as its configuration.
|
| 6076 | ParentDataElement(ParentDataWidget<T> super.widget);
|
| 6077 |
|
| 6078 | /// Returns the [Type] of [ParentData] that this element has been configured
|
| 6079 | /// for.
|
| 6080 | ///
|
| 6081 | /// This is only available in debug mode. It will throw in profile and
|
| 6082 | /// release modes.
|
| 6083 | Type get debugParentDataType {
|
| 6084 | Type? type;
|
| 6085 | assert(() {
|
| 6086 | type = T;
|
| 6087 | return true;
|
| 6088 | }());
|
| 6089 | if (type != null) {
|
| 6090 | return type!;
|
| 6091 | }
|
| 6092 | throw UnsupportedError('debugParentDataType is only supported in debug builds' );
|
| 6093 | }
|
| 6094 |
|
| 6095 | void _applyParentData(ParentDataWidget<T> widget) {
|
| 6096 | void applyParentDataToChild(Element child) {
|
| 6097 | if (child is RenderObjectElement) {
|
| 6098 | child._updateParentData(widget);
|
| 6099 | } else if (child.renderObjectAttachingChild != null) {
|
| 6100 | applyParentDataToChild(child.renderObjectAttachingChild!);
|
| 6101 | }
|
| 6102 | }
|
| 6103 |
|
| 6104 | if (renderObjectAttachingChild != null) {
|
| 6105 | applyParentDataToChild(renderObjectAttachingChild!);
|
| 6106 | }
|
| 6107 | }
|
| 6108 |
|
| 6109 | /// Calls [ParentDataWidget.applyParentData] on the given widget, passing it
|
| 6110 | /// the [RenderObject] whose parent data this element is ultimately
|
| 6111 | /// responsible for.
|
| 6112 | ///
|
| 6113 | /// This allows a render object's [RenderObject.parentData] to be modified
|
| 6114 | /// without triggering a build. This is generally ill-advised, but makes sense
|
| 6115 | /// in situations such as the following:
|
| 6116 | ///
|
| 6117 | /// * Build and layout are currently under way, but the [ParentData] in question
|
| 6118 | /// does not affect layout, and the value to be applied could not be
|
| 6119 | /// determined before build and layout (e.g. it depends on the layout of a
|
| 6120 | /// descendant).
|
| 6121 | ///
|
| 6122 | /// * Paint is currently under way, but the [ParentData] in question does not
|
| 6123 | /// affect layout or paint, and the value to be applied could not be
|
| 6124 | /// determined before paint (e.g. it depends on the compositing phase).
|
| 6125 | ///
|
| 6126 | /// In either case, the next build is expected to cause this element to be
|
| 6127 | /// configured with the given new widget (or a widget with equivalent data).
|
| 6128 | ///
|
| 6129 | /// Only [ParentDataWidget]s that return true for
|
| 6130 | /// [ParentDataWidget.debugCanApplyOutOfTurn] can be applied this way.
|
| 6131 | ///
|
| 6132 | /// The new widget must have the same child as the current widget.
|
| 6133 | ///
|
| 6134 | /// An example of when this is used is the [AutomaticKeepAlive] widget. If it
|
| 6135 | /// receives a notification during the build of one of its descendants saying
|
| 6136 | /// that its child must be kept alive, it will apply a [KeepAlive] widget out
|
| 6137 | /// of turn. This is safe, because by definition the child is already alive,
|
| 6138 | /// and therefore this will not change the behavior of the parent this frame.
|
| 6139 | /// It is more efficient than requesting an additional frame just for the
|
| 6140 | /// purpose of updating the [KeepAlive] widget.
|
| 6141 | void applyWidgetOutOfTurn(ParentDataWidget<T> newWidget) {
|
| 6142 | assert(newWidget.debugCanApplyOutOfTurn());
|
| 6143 | assert(newWidget.child == (widget as ParentDataWidget<T>).child);
|
| 6144 | _applyParentData(newWidget);
|
| 6145 | }
|
| 6146 |
|
| 6147 | @override
|
| 6148 | void notifyClients(ParentDataWidget<T> oldWidget) {
|
| 6149 | _applyParentData(widget as ParentDataWidget<T>);
|
| 6150 | }
|
| 6151 | }
|
| 6152 |
|
| 6153 | /// An [Element] that uses an [InheritedWidget] as its configuration.
|
| 6154 | class InheritedElement extends ProxyElement {
|
| 6155 | /// Creates an element that uses the given widget as its configuration.
|
| 6156 | InheritedElement(InheritedWidget super.widget);
|
| 6157 |
|
| 6158 | final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
|
| 6159 |
|
| 6160 | @override
|
| 6161 | void _updateInheritance() {
|
| 6162 | assert(_lifecycleState == _ElementLifecycle.active);
|
| 6163 | final PersistentHashMap<Type, InheritedElement> incomingWidgets =
|
| 6164 | _parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();
|
| 6165 | _inheritedElements = incomingWidgets.put(widget.runtimeType, this);
|
| 6166 | }
|
| 6167 |
|
| 6168 | @override
|
| 6169 | void debugDeactivated() {
|
| 6170 | assert(() {
|
| 6171 | assert(_dependents.isEmpty);
|
| 6172 | return true;
|
| 6173 | }());
|
| 6174 | super.debugDeactivated();
|
| 6175 | }
|
| 6176 |
|
| 6177 | /// Returns the dependencies value recorded for [dependent]
|
| 6178 | /// with [setDependencies].
|
| 6179 | ///
|
| 6180 | /// Each dependent element is mapped to a single object value
|
| 6181 | /// which represents how the element depends on this
|
| 6182 | /// [InheritedElement]. This value is null by default and by default
|
| 6183 | /// dependent elements are rebuilt unconditionally.
|
| 6184 | ///
|
| 6185 | /// Subclasses can manage these values with [updateDependencies]
|
| 6186 | /// so that they can selectively rebuild dependents in
|
| 6187 | /// [notifyDependent].
|
| 6188 | ///
|
| 6189 | /// This method is typically only called in overrides of [updateDependencies].
|
| 6190 | ///
|
| 6191 | /// See also:
|
| 6192 | ///
|
| 6193 | /// * [updateDependencies], which is called each time a dependency is
|
| 6194 | /// created with [dependOnInheritedWidgetOfExactType].
|
| 6195 | /// * [setDependencies], which sets dependencies value for a dependent
|
| 6196 | /// element.
|
| 6197 | /// * [notifyDependent], which can be overridden to use a dependent's
|
| 6198 | /// dependencies value to decide if the dependent needs to be rebuilt.
|
| 6199 | /// * [InheritedModel], which is an example of a class that uses this method
|
| 6200 | /// to manage dependency values.
|
| 6201 | @protected
|
| 6202 | Object? getDependencies(Element dependent) {
|
| 6203 | return _dependents[dependent];
|
| 6204 | }
|
| 6205 |
|
| 6206 | /// Sets the value returned by [getDependencies] value for [dependent].
|
| 6207 | ///
|
| 6208 | /// Each dependent element is mapped to a single object value
|
| 6209 | /// which represents how the element depends on this
|
| 6210 | /// [InheritedElement]. The [updateDependencies] method sets this value to
|
| 6211 | /// null by default so that dependent elements are rebuilt unconditionally.
|
| 6212 | ///
|
| 6213 | /// Subclasses can manage these values with [updateDependencies]
|
| 6214 | /// so that they can selectively rebuild dependents in [notifyDependent].
|
| 6215 | ///
|
| 6216 | /// This method is typically only called in overrides of [updateDependencies].
|
| 6217 | ///
|
| 6218 | /// See also:
|
| 6219 | ///
|
| 6220 | /// * [updateDependencies], which is called each time a dependency is
|
| 6221 | /// created with [dependOnInheritedWidgetOfExactType].
|
| 6222 | /// * [getDependencies], which returns the current value for a dependent
|
| 6223 | /// element.
|
| 6224 | /// * [notifyDependent], which can be overridden to use a dependent's
|
| 6225 | /// [getDependencies] value to decide if the dependent needs to be rebuilt.
|
| 6226 | /// * [InheritedModel], which is an example of a class that uses this method
|
| 6227 | /// to manage dependency values.
|
| 6228 | @protected
|
| 6229 | void setDependencies(Element dependent, Object? value) {
|
| 6230 | _dependents[dependent] = value;
|
| 6231 | }
|
| 6232 |
|
| 6233 | /// Called by [dependOnInheritedWidgetOfExactType] when a new [dependent] is added.
|
| 6234 | ///
|
| 6235 | /// Each dependent element can be mapped to a single object value with
|
| 6236 | /// [setDependencies]. This method can lookup the existing dependencies with
|
| 6237 | /// [getDependencies].
|
| 6238 | ///
|
| 6239 | /// By default this method sets the inherited dependencies for [dependent]
|
| 6240 | /// to null. This only serves to record an unconditional dependency on
|
| 6241 | /// [dependent].
|
| 6242 | ///
|
| 6243 | /// Subclasses can manage their own dependencies values so that they
|
| 6244 | /// can selectively rebuild dependents in [notifyDependent].
|
| 6245 | ///
|
| 6246 | /// See also:
|
| 6247 | ///
|
| 6248 | /// * [getDependencies], which returns the current value for a dependent
|
| 6249 | /// element.
|
| 6250 | /// * [setDependencies], which sets the value for a dependent element.
|
| 6251 | /// * [notifyDependent], which can be overridden to use a dependent's
|
| 6252 | /// dependencies value to decide if the dependent needs to be rebuilt.
|
| 6253 | /// * [InheritedModel], which is an example of a class that uses this method
|
| 6254 | /// to manage dependency values.
|
| 6255 | @protected
|
| 6256 | void updateDependencies(Element dependent, Object? aspect) {
|
| 6257 | setDependencies(dependent, null);
|
| 6258 | }
|
| 6259 |
|
| 6260 | /// Called by [notifyClients] for each dependent.
|
| 6261 | ///
|
| 6262 | /// Calls `dependent.didChangeDependencies()` by default.
|
| 6263 | ///
|
| 6264 | /// Subclasses can override this method to selectively call
|
| 6265 | /// [didChangeDependencies] based on the value of [getDependencies].
|
| 6266 | ///
|
| 6267 | /// See also:
|
| 6268 | ///
|
| 6269 | /// * [updateDependencies], which is called each time a dependency is
|
| 6270 | /// created with [dependOnInheritedWidgetOfExactType].
|
| 6271 | /// * [getDependencies], which returns the current value for a dependent
|
| 6272 | /// element.
|
| 6273 | /// * [setDependencies], which sets the value for a dependent element.
|
| 6274 | /// * [InheritedModel], which is an example of a class that uses this method
|
| 6275 | /// to manage dependency values.
|
| 6276 | @protected
|
| 6277 | void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
|
| 6278 | dependent.didChangeDependencies();
|
| 6279 | }
|
| 6280 |
|
| 6281 | /// Called by [Element.deactivate] to remove the provided `dependent` [Element] from this [InheritedElement].
|
| 6282 | ///
|
| 6283 | /// After the dependent is removed, [Element.didChangeDependencies] will no
|
| 6284 | /// longer be called on it when this [InheritedElement] notifies its dependents.
|
| 6285 | ///
|
| 6286 | /// Subclasses can override this method to release any resources retained for
|
| 6287 | /// a given [dependent].
|
| 6288 | @protected
|
| 6289 | @mustCallSuper
|
| 6290 | void removeDependent(Element dependent) {
|
| 6291 | _dependents.remove(dependent);
|
| 6292 | }
|
| 6293 |
|
| 6294 | /// Calls [Element.didChangeDependencies] of all dependent elements, if
|
| 6295 | /// [InheritedWidget.updateShouldNotify] returns true.
|
| 6296 | ///
|
| 6297 | /// Called by [update], immediately prior to [build].
|
| 6298 | ///
|
| 6299 | /// Calls [notifyClients] to actually trigger the notifications.
|
| 6300 | @override
|
| 6301 | void updated(InheritedWidget oldWidget) {
|
| 6302 | if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
|
| 6303 | super.updated(oldWidget);
|
| 6304 | }
|
| 6305 | }
|
| 6306 |
|
| 6307 | /// Notifies all dependent elements that this inherited widget has changed, by
|
| 6308 | /// calling [Element.didChangeDependencies].
|
| 6309 | ///
|
| 6310 | /// This method must only be called during the build phase. Usually this
|
| 6311 | /// method is called automatically when an inherited widget is rebuilt, e.g.
|
| 6312 | /// as a result of calling [State.setState] above the inherited widget.
|
| 6313 | ///
|
| 6314 | /// See also:
|
| 6315 | ///
|
| 6316 | /// * [InheritedNotifier], a subclass of [InheritedWidget] that also calls
|
| 6317 | /// this method when its [Listenable] sends a notification.
|
| 6318 | @override
|
| 6319 | void notifyClients(InheritedWidget oldWidget) {
|
| 6320 | assert(_debugCheckOwnerBuildTargetExists('notifyClients' ));
|
| 6321 | for (final Element dependent in _dependents.keys) {
|
| 6322 | assert(() {
|
| 6323 | // check that it really is our descendant
|
| 6324 | Element? ancestor = dependent._parent;
|
| 6325 | while (ancestor != this && ancestor != null) {
|
| 6326 | ancestor = ancestor._parent;
|
| 6327 | }
|
| 6328 | return ancestor == this;
|
| 6329 | }());
|
| 6330 | // check that it really depends on us
|
| 6331 | assert(dependent._dependencies!.contains(this));
|
| 6332 | notifyDependent(oldWidget, dependent);
|
| 6333 | }
|
| 6334 | }
|
| 6335 | }
|
| 6336 |
|
| 6337 | /// An [Element] that uses a [RenderObjectWidget] as its configuration.
|
| 6338 | ///
|
| 6339 | /// [RenderObjectElement] objects have an associated [RenderObject] widget in
|
| 6340 | /// the render tree, which handles concrete operations like laying out,
|
| 6341 | /// painting, and hit testing.
|
| 6342 | ///
|
| 6343 | /// Contrast with [ComponentElement].
|
| 6344 | ///
|
| 6345 | /// For details on the lifecycle of an element, see the discussion at [Element].
|
| 6346 | ///
|
| 6347 | /// ## Writing a RenderObjectElement subclass
|
| 6348 | ///
|
| 6349 | /// There are three common child models used by most [RenderObject]s:
|
| 6350 | ///
|
| 6351 | /// * Leaf render objects, with no children: The [LeafRenderObjectElement] class
|
| 6352 | /// handles this case.
|
| 6353 | ///
|
| 6354 | /// * A single child: The [SingleChildRenderObjectElement] class handles this
|
| 6355 | /// case.
|
| 6356 | ///
|
| 6357 | /// * A linked list of children: The [MultiChildRenderObjectElement] class
|
| 6358 | /// handles this case.
|
| 6359 | ///
|
| 6360 | /// Sometimes, however, a render object's child model is more complicated. Maybe
|
| 6361 | /// it has a two-dimensional array of children. Maybe it constructs children on
|
| 6362 | /// demand. Maybe it features multiple lists. In such situations, the
|
| 6363 | /// corresponding [Element] for the [Widget] that configures that [RenderObject]
|
| 6364 | /// will be a new subclass of [RenderObjectElement].
|
| 6365 | ///
|
| 6366 | /// Such a subclass is responsible for managing children, specifically the
|
| 6367 | /// [Element] children of this object, and the [RenderObject] children of its
|
| 6368 | /// corresponding [RenderObject].
|
| 6369 | ///
|
| 6370 | /// ### Specializing the getters
|
| 6371 | ///
|
| 6372 | /// [RenderObjectElement] objects spend much of their time acting as
|
| 6373 | /// intermediaries between their [widget] and their [renderObject]. It is
|
| 6374 | /// generally recommended against specializing the [widget] getter and
|
| 6375 | /// instead casting at the various call sites to avoid adding overhead
|
| 6376 | /// outside of this particular implementation.
|
| 6377 | ///
|
| 6378 | /// ```dart
|
| 6379 | /// class FooElement extends RenderObjectElement {
|
| 6380 | /// FooElement(super.widget);
|
| 6381 | ///
|
| 6382 | /// // Specializing the renderObject getter is fine because
|
| 6383 | /// // it is not performance sensitive.
|
| 6384 | /// @override
|
| 6385 | /// RenderFoo get renderObject => super.renderObject as RenderFoo;
|
| 6386 | ///
|
| 6387 | /// void _foo() {
|
| 6388 | /// // For the widget getter, though, we prefer to cast locally
|
| 6389 | /// // since that results in better overall performance where the
|
| 6390 | /// // casting isn't needed:
|
| 6391 | /// final Foo foo = widget as Foo;
|
| 6392 | /// // ...
|
| 6393 | /// }
|
| 6394 | ///
|
| 6395 | /// // ...
|
| 6396 | /// }
|
| 6397 | /// ```
|
| 6398 | ///
|
| 6399 | /// ### Slots
|
| 6400 | ///
|
| 6401 | /// Each child [Element] corresponds to a [RenderObject] which should be
|
| 6402 | /// attached to this element's render object as a child.
|
| 6403 | ///
|
| 6404 | /// However, the immediate children of the element may not be the ones that
|
| 6405 | /// eventually produce the actual [RenderObject] that they correspond to. For
|
| 6406 | /// example, a [StatelessElement] (the element of a [StatelessWidget])
|
| 6407 | /// corresponds to whatever [RenderObject] its child (the element returned by
|
| 6408 | /// its [StatelessWidget.build] method) corresponds to.
|
| 6409 | ///
|
| 6410 | /// Each child is therefore assigned a _[slot]_ token. This is an identifier whose
|
| 6411 | /// meaning is private to this [RenderObjectElement] node. When the descendant
|
| 6412 | /// that finally produces the [RenderObject] is ready to attach it to this
|
| 6413 | /// node's render object, it passes that slot token back to this node, and that
|
| 6414 | /// allows this node to cheaply identify where to put the child render object
|
| 6415 | /// relative to the others in the parent render object.
|
| 6416 | ///
|
| 6417 | /// A child's [slot] is determined when the parent calls [updateChild] to
|
| 6418 | /// inflate the child (see the next section). It can be updated by calling
|
| 6419 | /// [updateSlotForChild].
|
| 6420 | ///
|
| 6421 | /// ### Updating children
|
| 6422 | ///
|
| 6423 | /// Early in the lifecycle of an element, the framework calls the [mount]
|
| 6424 | /// method. This method should call [updateChild] for each child, passing in
|
| 6425 | /// the widget for that child, and the slot for that child, thus obtaining a
|
| 6426 | /// list of child [Element]s.
|
| 6427 | ///
|
| 6428 | /// Subsequently, the framework will call the [update] method. In this method,
|
| 6429 | /// the [RenderObjectElement] should call [updateChild] for each child, passing
|
| 6430 | /// in the [Element] that was obtained during [mount] or the last time [update]
|
| 6431 | /// was run (whichever happened most recently), the new [Widget], and the slot.
|
| 6432 | /// This provides the object with a new list of [Element] objects.
|
| 6433 | ///
|
| 6434 | /// Where possible, the [update] method should attempt to map the elements from
|
| 6435 | /// the last pass to the widgets in the new pass. For example, if one of the
|
| 6436 | /// elements from the last pass was configured with a particular [Key], and one
|
| 6437 | /// of the widgets in this new pass has that same key, they should be paired up,
|
| 6438 | /// and the old element should be updated with the widget (and the slot
|
| 6439 | /// corresponding to the new widget's new position, also). The [updateChildren]
|
| 6440 | /// method may be useful in this regard.
|
| 6441 | ///
|
| 6442 | /// [updateChild] should be called for children in their logical order. The
|
| 6443 | /// order can matter; for example, if two of the children use [PageStorage]'s
|
| 6444 | /// `writeState` feature in their build method (and neither has a [Widget.key]),
|
| 6445 | /// then the state written by the first will be overwritten by the second.
|
| 6446 | ///
|
| 6447 | /// #### Dynamically determining the children during the build phase
|
| 6448 | ///
|
| 6449 | /// The child widgets need not necessarily come from this element's widget
|
| 6450 | /// verbatim. They could be generated dynamically from a callback, or generated
|
| 6451 | /// in other more creative ways.
|
| 6452 | ///
|
| 6453 | /// #### Dynamically determining the children during layout
|
| 6454 | ///
|
| 6455 | /// If the widgets are to be generated at layout time, then generating them in
|
| 6456 | /// the [mount] and [update] methods won't work: layout of this element's render
|
| 6457 | /// object hasn't started yet at that point. Instead, the [update] method can
|
| 6458 | /// mark the render object as needing layout (see
|
| 6459 | /// [RenderObject.markNeedsLayout]), and then the render object's
|
| 6460 | /// [RenderObject.performLayout] method can call back to the element to have it
|
| 6461 | /// generate the widgets and call [updateChild] accordingly.
|
| 6462 | ///
|
| 6463 | /// For a render object to call an element during layout, it must use
|
| 6464 | /// [RenderObject.invokeLayoutCallback]. For an element to call [updateChild]
|
| 6465 | /// outside of its [update] method, it must use [BuildOwner.buildScope].
|
| 6466 | ///
|
| 6467 | /// The framework provides many more checks in normal operation than it does
|
| 6468 | /// when doing a build during layout. For this reason, creating widgets with
|
| 6469 | /// layout-time build semantics should be done with great care.
|
| 6470 | ///
|
| 6471 | /// #### Handling errors when building
|
| 6472 | ///
|
| 6473 | /// If an element calls a builder function to obtain widgets for its children,
|
| 6474 | /// it may find that the build throws an exception. Such exceptions should be
|
| 6475 | /// caught and reported using [FlutterError.reportError]. If a child is needed
|
| 6476 | /// but a builder has failed in this way, an instance of [ErrorWidget] can be
|
| 6477 | /// used instead.
|
| 6478 | ///
|
| 6479 | /// ### Detaching children
|
| 6480 | ///
|
| 6481 | /// It is possible, when using [GlobalKey]s, for a child to be proactively
|
| 6482 | /// removed by another element before this element has been updated.
|
| 6483 | /// (Specifically, this happens when the subtree rooted at a widget with a
|
| 6484 | /// particular [GlobalKey] is being moved from this element to an element
|
| 6485 | /// processed earlier in the build phase.) When this happens, this element's
|
| 6486 | /// [forgetChild] method will be called with a reference to the affected child
|
| 6487 | /// element.
|
| 6488 | ///
|
| 6489 | /// The [forgetChild] method of a [RenderObjectElement] subclass must remove the
|
| 6490 | /// child element from its child list, so that when it next [update]s its
|
| 6491 | /// children, the removed child is not considered.
|
| 6492 | ///
|
| 6493 | /// For performance reasons, if there are many elements, it may be quicker to
|
| 6494 | /// track which elements were forgotten by storing them in a [Set], rather than
|
| 6495 | /// proactively mutating the local record of the child list and the identities
|
| 6496 | /// of all the slots. For example, see the implementation of
|
| 6497 | /// [MultiChildRenderObjectElement].
|
| 6498 | ///
|
| 6499 | /// ### Maintaining the render object tree
|
| 6500 | ///
|
| 6501 | /// Once a descendant produces a render object, it will call
|
| 6502 | /// [insertRenderObjectChild]. If the descendant's slot changes identity, it
|
| 6503 | /// will call [moveRenderObjectChild]. If a descendant goes away, it will call
|
| 6504 | /// [removeRenderObjectChild].
|
| 6505 | ///
|
| 6506 | /// These three methods should update the render tree accordingly, attaching,
|
| 6507 | /// moving, and detaching the given child render object from this element's own
|
| 6508 | /// render object respectively.
|
| 6509 | ///
|
| 6510 | /// ### Walking the children
|
| 6511 | ///
|
| 6512 | /// If a [RenderObjectElement] object has any children [Element]s, it must
|
| 6513 | /// expose them in its implementation of the [visitChildren] method. This method
|
| 6514 | /// is used by many of the framework's internal mechanisms, and so should be
|
| 6515 | /// fast. It is also used by the test framework and [debugDumpApp].
|
| 6516 | abstract class RenderObjectElement extends Element {
|
| 6517 | /// Creates an element that uses the given widget as its configuration.
|
| 6518 | RenderObjectElement(RenderObjectWidget super.widget);
|
| 6519 |
|
| 6520 | /// The underlying [RenderObject] for this element.
|
| 6521 | ///
|
| 6522 | /// If this element has been [unmount]ed, this getter will throw.
|
| 6523 | @override
|
| 6524 | RenderObject get renderObject {
|
| 6525 | assert(_renderObject != null, ' $runtimeType unmounted' );
|
| 6526 | return _renderObject!;
|
| 6527 | }
|
| 6528 |
|
| 6529 | RenderObject? _renderObject;
|
| 6530 |
|
| 6531 | @override
|
| 6532 | Element? get renderObjectAttachingChild => null;
|
| 6533 |
|
| 6534 | bool _debugDoingBuild = false;
|
| 6535 | @override
|
| 6536 | bool get debugDoingBuild => _debugDoingBuild;
|
| 6537 |
|
| 6538 | RenderObjectElement? _ancestorRenderObjectElement;
|
| 6539 |
|
| 6540 | RenderObjectElement? _findAncestorRenderObjectElement() {
|
| 6541 | Element? ancestor = _parent;
|
| 6542 | while (ancestor != null && ancestor is! RenderObjectElement) {
|
| 6543 | // In debug mode we check whether the ancestor accepts RenderObjects to
|
| 6544 | // produce a better error message in attachRenderObject. In release mode,
|
| 6545 | // we assume only correct trees are built (i.e.
|
| 6546 | // debugExpectsRenderObjectForSlot always returns true) and don't check
|
| 6547 | // explicitly.
|
| 6548 | assert(() {
|
| 6549 | if (!ancestor!.debugExpectsRenderObjectForSlot(slot)) {
|
| 6550 | ancestor = null;
|
| 6551 | }
|
| 6552 | return true;
|
| 6553 | }());
|
| 6554 | ancestor = ancestor?._parent;
|
| 6555 | }
|
| 6556 | assert(() {
|
| 6557 | if (ancestor?.debugExpectsRenderObjectForSlot(slot) == false) {
|
| 6558 | ancestor = null;
|
| 6559 | }
|
| 6560 | return true;
|
| 6561 | }());
|
| 6562 | return ancestor as RenderObjectElement?;
|
| 6563 | }
|
| 6564 |
|
| 6565 | void _debugCheckCompetingAncestors(
|
| 6566 | List<ParentDataElement<ParentData>> result,
|
| 6567 | Set<Type> debugAncestorTypes,
|
| 6568 | Set<Type> debugParentDataTypes,
|
| 6569 | List<Type> debugAncestorCulprits,
|
| 6570 | ) {
|
| 6571 | assert(() {
|
| 6572 | // Check that no other ParentDataWidgets of the same
|
| 6573 | // type want to provide parent data.
|
| 6574 | if (debugAncestorTypes.length != result.length ||
|
| 6575 | debugParentDataTypes.length != result.length) {
|
| 6576 | // This can only occur if the Sets of ancestors and parent data types was
|
| 6577 | // provided a dupe and did not add it.
|
| 6578 | assert(
|
| 6579 | debugAncestorTypes.length < result.length || debugParentDataTypes.length < result.length,
|
| 6580 | );
|
| 6581 | try {
|
| 6582 | // We explicitly throw here (even though we immediately redirect the
|
| 6583 | // exception elsewhere) so that debuggers will notice it when they
|
| 6584 | // have "break on exception" enabled.
|
| 6585 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 6586 | ErrorSummary('Incorrect use of ParentDataWidget.' ),
|
| 6587 | ErrorDescription(
|
| 6588 | 'Competing ParentDataWidgets are providing parent data to the '
|
| 6589 | 'same RenderObject:' ,
|
| 6590 | ),
|
| 6591 | for (final ParentDataElement<ParentData> ancestor in result.where((
|
| 6592 | ParentDataElement<ParentData> ancestor,
|
| 6593 | ) {
|
| 6594 | return debugAncestorCulprits.contains(ancestor.runtimeType);
|
| 6595 | }))
|
| 6596 | ErrorDescription(
|
| 6597 | '- ${ancestor.widget}, which writes ParentData of type '
|
| 6598 | ' ${ancestor.debugParentDataType}, (typically placed directly '
|
| 6599 | 'inside a '
|
| 6600 | ' ${(ancestor.widget as ParentDataWidget<ParentData>).debugTypicalAncestorWidgetClass} '
|
| 6601 | 'widget)' ,
|
| 6602 | ),
|
| 6603 | ErrorDescription(
|
| 6604 | 'A RenderObject can receive parent data from multiple '
|
| 6605 | 'ParentDataWidgets, but the Type of ParentData must be unique to '
|
| 6606 | 'prevent one overwriting another.' ,
|
| 6607 | ),
|
| 6608 | ErrorHint(
|
| 6609 | 'Usually, this indicates that one or more of the offending '
|
| 6610 | "ParentDataWidgets listed above isn't placed inside a dedicated "
|
| 6611 | "compatible ancestor widget that it isn't sharing with another "
|
| 6612 | 'ParentDataWidget of the same type.' ,
|
| 6613 | ),
|
| 6614 | ErrorHint(
|
| 6615 | 'Otherwise, separating aspects of ParentData to prevent '
|
| 6616 | 'conflicts can be done using mixins, mixing them all in on the '
|
| 6617 | 'full ParentData Object, such as KeepAlive does with '
|
| 6618 | 'KeepAliveParentDataMixin.' ,
|
| 6619 | ),
|
| 6620 | ErrorDescription(
|
| 6621 | 'The ownership chain for the RenderObject that received the '
|
| 6622 | 'parent data was:\n ${debugGetCreatorChain(10)}' ,
|
| 6623 | ),
|
| 6624 | ]);
|
| 6625 | } on FlutterError catch (error) {
|
| 6626 | _reportException(ErrorSummary('while looking for parent data.' ), error, error.stackTrace);
|
| 6627 | }
|
| 6628 | }
|
| 6629 | return true;
|
| 6630 | }());
|
| 6631 | }
|
| 6632 |
|
| 6633 | List<ParentDataElement<ParentData>> _findAncestorParentDataElements() {
|
| 6634 | Element? ancestor = _parent;
|
| 6635 | final List<ParentDataElement<ParentData>> result = <ParentDataElement<ParentData>>[];
|
| 6636 | final Set<Type> debugAncestorTypes = <Type>{};
|
| 6637 | final Set<Type> debugParentDataTypes = <Type>{};
|
| 6638 | final List<Type> debugAncestorCulprits = <Type>[];
|
| 6639 |
|
| 6640 | // More than one ParentDataWidget can contribute ParentData, but there are
|
| 6641 | // some constraints.
|
| 6642 | // 1. ParentData can only be written by unique ParentDataWidget types.
|
| 6643 | // For example, two KeepAlive ParentDataWidgets trying to write to the
|
| 6644 | // same child is not allowed.
|
| 6645 | // 2. Each contributing ParentDataWidget must contribute to a unique
|
| 6646 | // ParentData type, less ParentData be overwritten.
|
| 6647 | // For example, there cannot be two ParentDataWidgets that both write
|
| 6648 | // ParentData of type KeepAliveParentDataMixin, if the first check was
|
| 6649 | // subverted by a subclassing of the KeepAlive ParentDataWidget.
|
| 6650 | // 3. The ParentData itself must be compatible with all ParentDataWidgets
|
| 6651 | // writing to it.
|
| 6652 | // For example, TwoDimensionalViewportParentData uses the
|
| 6653 | // KeepAliveParentDataMixin, so it could be compatible with both
|
| 6654 | // KeepAlive, and another ParentDataWidget with ParentData type
|
| 6655 | // TwoDimensionalViewportParentData or a subclass thereof.
|
| 6656 | // The first and second cases are verified here. The third is verified in
|
| 6657 | // debugIsValidRenderObject.
|
| 6658 |
|
| 6659 | while (ancestor != null && ancestor is! RenderObjectElement) {
|
| 6660 | if (ancestor is ParentDataElement<ParentData>) {
|
| 6661 | assert((ParentDataElement<ParentData> ancestor) {
|
| 6662 | if (!debugAncestorTypes.add(ancestor.runtimeType) ||
|
| 6663 | !debugParentDataTypes.add(ancestor.debugParentDataType)) {
|
| 6664 | debugAncestorCulprits.add(ancestor.runtimeType);
|
| 6665 | }
|
| 6666 | return true;
|
| 6667 | }(ancestor));
|
| 6668 | result.add(ancestor);
|
| 6669 | }
|
| 6670 | ancestor = ancestor._parent;
|
| 6671 | }
|
| 6672 | assert(() {
|
| 6673 | if (result.isEmpty || ancestor == null) {
|
| 6674 | return true;
|
| 6675 | }
|
| 6676 | // Validate points 1 and 2 from above.
|
| 6677 | _debugCheckCompetingAncestors(
|
| 6678 | result,
|
| 6679 | debugAncestorTypes,
|
| 6680 | debugParentDataTypes,
|
| 6681 | debugAncestorCulprits,
|
| 6682 | );
|
| 6683 | return true;
|
| 6684 | }());
|
| 6685 | return result;
|
| 6686 | }
|
| 6687 |
|
| 6688 | @override
|
| 6689 | void mount(Element? parent, Object? newSlot) {
|
| 6690 | super.mount(parent, newSlot);
|
| 6691 | assert(() {
|
| 6692 | _debugDoingBuild = true;
|
| 6693 | return true;
|
| 6694 | }());
|
| 6695 | _renderObject = (widget as RenderObjectWidget).createRenderObject(this);
|
| 6696 | assert(!_renderObject!.debugDisposed!);
|
| 6697 | assert(() {
|
| 6698 | _debugDoingBuild = false;
|
| 6699 | return true;
|
| 6700 | }());
|
| 6701 | assert(() {
|
| 6702 | _debugUpdateRenderObjectOwner();
|
| 6703 | return true;
|
| 6704 | }());
|
| 6705 | assert(slot == newSlot);
|
| 6706 | attachRenderObject(newSlot);
|
| 6707 | super.performRebuild(); // clears the "dirty" flag
|
| 6708 | }
|
| 6709 |
|
| 6710 | @override
|
| 6711 | void update(covariant RenderObjectWidget newWidget) {
|
| 6712 | super.update(newWidget);
|
| 6713 | assert(widget == newWidget);
|
| 6714 | assert(() {
|
| 6715 | _debugUpdateRenderObjectOwner();
|
| 6716 | return true;
|
| 6717 | }());
|
| 6718 | _performRebuild(); // calls widget.updateRenderObject()
|
| 6719 | }
|
| 6720 |
|
| 6721 | void _debugUpdateRenderObjectOwner() {
|
| 6722 | assert(() {
|
| 6723 | renderObject.debugCreator = DebugCreator(this);
|
| 6724 | return true;
|
| 6725 | }());
|
| 6726 | }
|
| 6727 |
|
| 6728 | @override
|
| 6729 | // ignore: must_call_super, _performRebuild calls super.
|
| 6730 | void performRebuild() {
|
| 6731 | _performRebuild(); // calls widget.updateRenderObject()
|
| 6732 | }
|
| 6733 |
|
| 6734 | @pragma('dart2js:tryInline' )
|
| 6735 | @pragma('vm:prefer-inline' )
|
| 6736 | @pragma('wasm:prefer-inline' )
|
| 6737 | void _performRebuild() {
|
| 6738 | assert(() {
|
| 6739 | _debugDoingBuild = true;
|
| 6740 | return true;
|
| 6741 | }());
|
| 6742 | (widget as RenderObjectWidget).updateRenderObject(this, renderObject);
|
| 6743 | assert(() {
|
| 6744 | _debugDoingBuild = false;
|
| 6745 | return true;
|
| 6746 | }());
|
| 6747 | super.performRebuild(); // clears the "dirty" flag
|
| 6748 | }
|
| 6749 |
|
| 6750 | @override
|
| 6751 | void deactivate() {
|
| 6752 | super.deactivate();
|
| 6753 | assert(
|
| 6754 | !renderObject.attached,
|
| 6755 | 'A RenderObject was still attached when attempting to deactivate its '
|
| 6756 | 'RenderObjectElement: $renderObject' ,
|
| 6757 | );
|
| 6758 | }
|
| 6759 |
|
| 6760 | @override
|
| 6761 | void unmount() {
|
| 6762 | assert(
|
| 6763 | !renderObject.debugDisposed!,
|
| 6764 | 'A RenderObject was disposed prior to its owning element being unmounted: '
|
| 6765 | ' $renderObject' ,
|
| 6766 | );
|
| 6767 | final RenderObjectWidget oldWidget = widget as RenderObjectWidget;
|
| 6768 | super.unmount();
|
| 6769 | assert(
|
| 6770 | !renderObject.attached,
|
| 6771 | 'A RenderObject was still attached when attempting to unmount its '
|
| 6772 | 'RenderObjectElement: $renderObject' ,
|
| 6773 | );
|
| 6774 | oldWidget.didUnmountRenderObject(renderObject);
|
| 6775 | _renderObject!.dispose();
|
| 6776 | _renderObject = null;
|
| 6777 | }
|
| 6778 |
|
| 6779 | void _updateParentData(ParentDataWidget<ParentData> parentDataWidget) {
|
| 6780 | bool applyParentData = true;
|
| 6781 | assert(() {
|
| 6782 | try {
|
| 6783 | if (!parentDataWidget.debugIsValidRenderObject(renderObject)) {
|
| 6784 | applyParentData = false;
|
| 6785 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 6786 | ErrorSummary('Incorrect use of ParentDataWidget.' ),
|
| 6787 | ...parentDataWidget._debugDescribeIncorrectParentDataType(
|
| 6788 | parentData: renderObject.parentData,
|
| 6789 | parentDataCreator: _ancestorRenderObjectElement?.widget as RenderObjectWidget?,
|
| 6790 | ownershipChain: ErrorDescription(debugGetCreatorChain(10)),
|
| 6791 | ),
|
| 6792 | ]);
|
| 6793 | }
|
| 6794 | } on FlutterError catch (e) {
|
| 6795 | // We catch the exception directly to avoid activating the ErrorWidget,
|
| 6796 | // while still allowing debuggers to break on exception. Since the tree
|
| 6797 | // is in a broken state, adding the ErrorWidget would likely cause more
|
| 6798 | // exceptions, which is not good for the debugging experience.
|
| 6799 | _reportException(ErrorSummary('while applying parent data.' ), e, e.stackTrace);
|
| 6800 | }
|
| 6801 | return true;
|
| 6802 | }());
|
| 6803 | if (applyParentData) {
|
| 6804 | parentDataWidget.applyParentData(renderObject);
|
| 6805 | }
|
| 6806 | }
|
| 6807 |
|
| 6808 | @override
|
| 6809 | void updateSlot(Object? newSlot) {
|
| 6810 | final Object? oldSlot = slot;
|
| 6811 | assert(oldSlot != newSlot);
|
| 6812 | super.updateSlot(newSlot);
|
| 6813 | assert(slot == newSlot);
|
| 6814 | assert(_ancestorRenderObjectElement == _findAncestorRenderObjectElement());
|
| 6815 | _ancestorRenderObjectElement?.moveRenderObjectChild(renderObject, oldSlot, slot);
|
| 6816 | }
|
| 6817 |
|
| 6818 | @override
|
| 6819 | void attachRenderObject(Object? newSlot) {
|
| 6820 | assert(_ancestorRenderObjectElement == null);
|
| 6821 | _slot = newSlot;
|
| 6822 | _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
|
| 6823 | assert(() {
|
| 6824 | if (_ancestorRenderObjectElement == null) {
|
| 6825 | FlutterError.reportError(
|
| 6826 | FlutterErrorDetails(
|
| 6827 | exception: FlutterError.fromParts(<DiagnosticsNode>[
|
| 6828 | ErrorSummary(
|
| 6829 | 'The render object for ${toStringShort()} cannot find ancestor render object to attach to.' ,
|
| 6830 | ),
|
| 6831 | ErrorDescription(
|
| 6832 | 'The ownership chain for the RenderObject in question was:\n ${debugGetCreatorChain(10)}' ,
|
| 6833 | ),
|
| 6834 | ErrorHint(
|
| 6835 | 'Try wrapping your widget in a View widget or any other widget that is backed by '
|
| 6836 | 'a $RenderTreeRootElement to serve as the root of the render tree.' ,
|
| 6837 | ),
|
| 6838 | ]),
|
| 6839 | ),
|
| 6840 | );
|
| 6841 | }
|
| 6842 | return true;
|
| 6843 | }());
|
| 6844 | _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
|
| 6845 | final List<ParentDataElement<ParentData>> parentDataElements =
|
| 6846 | _findAncestorParentDataElements();
|
| 6847 | for (final ParentDataElement<ParentData> parentDataElement in parentDataElements) {
|
| 6848 | _updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
|
| 6849 | }
|
| 6850 | }
|
| 6851 |
|
| 6852 | @override
|
| 6853 | void detachRenderObject() {
|
| 6854 | if (_ancestorRenderObjectElement != null) {
|
| 6855 | _ancestorRenderObjectElement!.removeRenderObjectChild(renderObject, slot);
|
| 6856 | _ancestorRenderObjectElement = null;
|
| 6857 | }
|
| 6858 | _slot = null;
|
| 6859 | }
|
| 6860 |
|
| 6861 | /// Insert the given child into [renderObject] at the given slot.
|
| 6862 | ///
|
| 6863 | /// {@template flutter.widgets.RenderObjectElement.insertRenderObjectChild}
|
| 6864 | /// The semantics of `slot` are determined by this element. For example, if
|
| 6865 | /// this element has a single child, the slot should always be null. If this
|
| 6866 | /// element has a list of children, the previous sibling element wrapped in an
|
| 6867 | /// [IndexedSlot] is a convenient value for the slot.
|
| 6868 | /// {@endtemplate}
|
| 6869 | @protected
|
| 6870 | void insertRenderObjectChild(covariant RenderObject child, covariant Object? slot);
|
| 6871 |
|
| 6872 | /// Move the given child from the given old slot to the given new slot.
|
| 6873 | ///
|
| 6874 | /// The given child is guaranteed to have [renderObject] as its parent.
|
| 6875 | ///
|
| 6876 | /// {@macro flutter.widgets.RenderObjectElement.insertRenderObjectChild}
|
| 6877 | ///
|
| 6878 | /// This method is only ever called if [updateChild] can end up being called
|
| 6879 | /// with an existing [Element] child and a `slot` that differs from the slot
|
| 6880 | /// that element was previously given. [MultiChildRenderObjectElement] does this,
|
| 6881 | /// for example. [SingleChildRenderObjectElement] does not (since the `slot` is
|
| 6882 | /// always null). An [Element] that has a specific set of slots with each child
|
| 6883 | /// always having the same slot (and where children in different slots are never
|
| 6884 | /// compared against each other for the purposes of updating one slot with the
|
| 6885 | /// element from another slot) would never call this.
|
| 6886 | @protected
|
| 6887 | void moveRenderObjectChild(
|
| 6888 | covariant RenderObject child,
|
| 6889 | covariant Object? oldSlot,
|
| 6890 | covariant Object? newSlot,
|
| 6891 | );
|
| 6892 |
|
| 6893 | /// Remove the given child from [renderObject].
|
| 6894 | ///
|
| 6895 | /// The given child is guaranteed to have been inserted at the given `slot`
|
| 6896 | /// and have [renderObject] as its parent.
|
| 6897 | @protected
|
| 6898 | void removeRenderObjectChild(covariant RenderObject child, covariant Object? slot);
|
| 6899 |
|
| 6900 | @override
|
| 6901 | void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
| 6902 | super.debugFillProperties(properties);
|
| 6903 | properties.add(
|
| 6904 | DiagnosticsProperty<RenderObject>('renderObject' , _renderObject, defaultValue: null),
|
| 6905 | );
|
| 6906 | }
|
| 6907 | }
|
| 6908 |
|
| 6909 | /// Deprecated. Unused in the framework and will be removed in a future version
|
| 6910 | /// of Flutter.
|
| 6911 | ///
|
| 6912 | /// Classes that extend this class can extend [RenderObjectElement] and mixin
|
| 6913 | /// [RootElementMixin] instead.
|
| 6914 | @Deprecated(
|
| 6915 | 'Use RootElementMixin instead. '
|
| 6916 | 'This feature was deprecated after v3.9.0-16.0.pre.' ,
|
| 6917 | )
|
| 6918 | abstract class RootRenderObjectElement extends RenderObjectElement with RootElementMixin {
|
| 6919 | /// Initializes fields for subclasses.
|
| 6920 | @Deprecated(
|
| 6921 | 'Use RootElementMixin instead. '
|
| 6922 | 'This feature was deprecated after v3.9.0-16.0.pre.' ,
|
| 6923 | )
|
| 6924 | RootRenderObjectElement(super.widget);
|
| 6925 | }
|
| 6926 |
|
| 6927 | /// Mixin for the element at the root of the tree.
|
| 6928 | ///
|
| 6929 | /// Only root elements may have their owner set explicitly. All other
|
| 6930 | /// elements inherit their owner from their parent.
|
| 6931 | mixin RootElementMixin on Element {
|
| 6932 | /// Set the owner of the element. The owner will be propagated to all the
|
| 6933 | /// descendants of this element.
|
| 6934 | ///
|
| 6935 | /// The owner manages the dirty elements list.
|
| 6936 | ///
|
| 6937 | /// The [WidgetsBinding] introduces the primary owner,
|
| 6938 | /// [WidgetsBinding.buildOwner], and assigns it to the widget tree in the call
|
| 6939 | /// to [runApp]. The binding is responsible for driving the build pipeline by
|
| 6940 | /// calling the build owner's [BuildOwner.buildScope] method. See
|
| 6941 | /// [WidgetsBinding.drawFrame].
|
| 6942 | void assignOwner(BuildOwner owner) {
|
| 6943 | _owner = owner;
|
| 6944 | _parentBuildScope = BuildScope();
|
| 6945 | }
|
| 6946 |
|
| 6947 | @override
|
| 6948 | void mount(Element? parent, Object? newSlot) {
|
| 6949 | // Root elements should never have parents.
|
| 6950 | assert(parent == null);
|
| 6951 | assert(newSlot == null);
|
| 6952 | super.mount(parent, newSlot);
|
| 6953 | }
|
| 6954 | }
|
| 6955 |
|
| 6956 | /// An [Element] that uses a [LeafRenderObjectWidget] as its configuration.
|
| 6957 | class LeafRenderObjectElement extends RenderObjectElement {
|
| 6958 | /// Creates an element that uses the given widget as its configuration.
|
| 6959 | LeafRenderObjectElement(LeafRenderObjectWidget super.widget);
|
| 6960 |
|
| 6961 | @override
|
| 6962 | void forgetChild(Element child) {
|
| 6963 | assert(false);
|
| 6964 | super.forgetChild(child);
|
| 6965 | }
|
| 6966 |
|
| 6967 | @override
|
| 6968 | void insertRenderObjectChild(RenderObject child, Object? slot) {
|
| 6969 | assert(false);
|
| 6970 | }
|
| 6971 |
|
| 6972 | @override
|
| 6973 | void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
|
| 6974 | assert(false);
|
| 6975 | }
|
| 6976 |
|
| 6977 | @override
|
| 6978 | void removeRenderObjectChild(RenderObject child, Object? slot) {
|
| 6979 | assert(false);
|
| 6980 | }
|
| 6981 |
|
| 6982 | @override
|
| 6983 | List<DiagnosticsNode> debugDescribeChildren() {
|
| 6984 | return widget.debugDescribeChildren();
|
| 6985 | }
|
| 6986 | }
|
| 6987 |
|
| 6988 | /// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration.
|
| 6989 | ///
|
| 6990 | /// The child is optional.
|
| 6991 | ///
|
| 6992 | /// This element subclass can be used for [RenderObjectWidget]s whose
|
| 6993 | /// [RenderObject]s use the [RenderObjectWithChildMixin] mixin. Such widgets are
|
| 6994 | /// expected to inherit from [SingleChildRenderObjectWidget].
|
| 6995 | class SingleChildRenderObjectElement extends RenderObjectElement {
|
| 6996 | /// Creates an element that uses the given widget as its configuration.
|
| 6997 | SingleChildRenderObjectElement(SingleChildRenderObjectWidget super.widget);
|
| 6998 |
|
| 6999 | Element? _child;
|
| 7000 |
|
| 7001 | @override
|
| 7002 | void visitChildren(ElementVisitor visitor) {
|
| 7003 | if (_child != null) {
|
| 7004 | visitor(_child!);
|
| 7005 | }
|
| 7006 | }
|
| 7007 |
|
| 7008 | @override
|
| 7009 | void forgetChild(Element child) {
|
| 7010 | assert(child == _child);
|
| 7011 | _child = null;
|
| 7012 | super.forgetChild(child);
|
| 7013 | }
|
| 7014 |
|
| 7015 | @override
|
| 7016 | void mount(Element? parent, Object? newSlot) {
|
| 7017 | super.mount(parent, newSlot);
|
| 7018 | _child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
|
| 7019 | }
|
| 7020 |
|
| 7021 | @override
|
| 7022 | void update(SingleChildRenderObjectWidget newWidget) {
|
| 7023 | super.update(newWidget);
|
| 7024 | assert(widget == newWidget);
|
| 7025 | _child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
|
| 7026 | }
|
| 7027 |
|
| 7028 | @override
|
| 7029 | void insertRenderObjectChild(RenderObject child, Object? slot) {
|
| 7030 | final RenderObjectWithChildMixin<RenderObject> renderObject =
|
| 7031 | this.renderObject as RenderObjectWithChildMixin<RenderObject>;
|
| 7032 | assert(slot == null);
|
| 7033 | assert(renderObject.debugValidateChild(child));
|
| 7034 | renderObject.child = child;
|
| 7035 | assert(renderObject == this.renderObject);
|
| 7036 | }
|
| 7037 |
|
| 7038 | @override
|
| 7039 | void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
|
| 7040 | assert(false);
|
| 7041 | }
|
| 7042 |
|
| 7043 | @override
|
| 7044 | void removeRenderObjectChild(RenderObject child, Object? slot) {
|
| 7045 | final RenderObjectWithChildMixin<RenderObject> renderObject =
|
| 7046 | this.renderObject as RenderObjectWithChildMixin<RenderObject>;
|
| 7047 | assert(slot == null);
|
| 7048 | assert(renderObject.child == child);
|
| 7049 | renderObject.child = null;
|
| 7050 | assert(renderObject == this.renderObject);
|
| 7051 | }
|
| 7052 | }
|
| 7053 |
|
| 7054 | /// An [Element] that uses a [MultiChildRenderObjectWidget] as its configuration.
|
| 7055 | ///
|
| 7056 | /// This element subclass can be used for [RenderObjectWidget]s whose
|
| 7057 | /// [RenderObject]s use the [ContainerRenderObjectMixin] mixin with a parent data
|
| 7058 | /// type that implements [ContainerParentDataMixin<RenderObject>]. Such widgets
|
| 7059 | /// are expected to inherit from [MultiChildRenderObjectWidget].
|
| 7060 | ///
|
| 7061 | /// See also:
|
| 7062 | ///
|
| 7063 | /// * [IndexedSlot], which is used as [Element.slot]s for the children of a
|
| 7064 | /// [MultiChildRenderObjectElement].
|
| 7065 | /// * [RenderObjectElement.updateChildren], which discusses why [IndexedSlot]
|
| 7066 | /// is used for the slots of the children.
|
| 7067 | class MultiChildRenderObjectElement extends RenderObjectElement {
|
| 7068 | /// Creates an element that uses the given widget as its configuration.
|
| 7069 | MultiChildRenderObjectElement(MultiChildRenderObjectWidget super.widget)
|
| 7070 | : assert(!debugChildrenHaveDuplicateKeys(widget, widget.children));
|
| 7071 |
|
| 7072 | @override
|
| 7073 | ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>
|
| 7074 | get renderObject {
|
| 7075 | return super.renderObject
|
| 7076 | as ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>;
|
| 7077 | }
|
| 7078 |
|
| 7079 | /// The current list of children of this element.
|
| 7080 | ///
|
| 7081 | /// This list is filtered to hide elements that have been forgotten (using
|
| 7082 | /// [forgetChild]).
|
| 7083 | @protected
|
| 7084 | @visibleForTesting
|
| 7085 | Iterable<Element> get children =>
|
| 7086 | _children.where((Element child) => !_forgottenChildren.contains(child));
|
| 7087 |
|
| 7088 | late List<Element> _children;
|
| 7089 | // We keep a set of forgotten children to avoid O(n^2) work walking _children
|
| 7090 | // repeatedly to remove children.
|
| 7091 | final Set<Element> _forgottenChildren = HashSet<Element>();
|
| 7092 |
|
| 7093 | @override
|
| 7094 | void insertRenderObjectChild(RenderObject child, IndexedSlot<Element?> slot) {
|
| 7095 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>
|
| 7096 | renderObject = this.renderObject;
|
| 7097 | assert(renderObject.debugValidateChild(child));
|
| 7098 | renderObject.insert(child, after: slot.value?.renderObject);
|
| 7099 | assert(renderObject == this.renderObject);
|
| 7100 | }
|
| 7101 |
|
| 7102 | @override
|
| 7103 | void moveRenderObjectChild(
|
| 7104 | RenderObject child,
|
| 7105 | IndexedSlot<Element?> oldSlot,
|
| 7106 | IndexedSlot<Element?> newSlot,
|
| 7107 | ) {
|
| 7108 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>
|
| 7109 | renderObject = this.renderObject;
|
| 7110 | assert(child.parent == renderObject);
|
| 7111 | renderObject.move(child, after: newSlot.value?.renderObject);
|
| 7112 | assert(renderObject == this.renderObject);
|
| 7113 | }
|
| 7114 |
|
| 7115 | @override
|
| 7116 | void removeRenderObjectChild(RenderObject child, Object? slot) {
|
| 7117 | final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>>
|
| 7118 | renderObject = this.renderObject;
|
| 7119 | assert(child.parent == renderObject);
|
| 7120 | renderObject.remove(child);
|
| 7121 | assert(renderObject == this.renderObject);
|
| 7122 | }
|
| 7123 |
|
| 7124 | @override
|
| 7125 | void visitChildren(ElementVisitor visitor) {
|
| 7126 | for (final Element child in _children) {
|
| 7127 | if (!_forgottenChildren.contains(child)) {
|
| 7128 | visitor(child);
|
| 7129 | }
|
| 7130 | }
|
| 7131 | }
|
| 7132 |
|
| 7133 | @override
|
| 7134 | void forgetChild(Element child) {
|
| 7135 | assert(_children.contains(child));
|
| 7136 | assert(!_forgottenChildren.contains(child));
|
| 7137 | _forgottenChildren.add(child);
|
| 7138 | super.forgetChild(child);
|
| 7139 | }
|
| 7140 |
|
| 7141 | bool _debugCheckHasAssociatedRenderObject(Element newChild) {
|
| 7142 | assert(() {
|
| 7143 | if (newChild.renderObject == null) {
|
| 7144 | FlutterError.reportError(
|
| 7145 | FlutterErrorDetails(
|
| 7146 | exception: FlutterError.fromParts(<DiagnosticsNode>[
|
| 7147 | ErrorSummary(
|
| 7148 | 'The children of `MultiChildRenderObjectElement` must each has an associated render object.' ,
|
| 7149 | ),
|
| 7150 | ErrorHint(
|
| 7151 | 'This typically means that the ` ${newChild.widget}` or its children\n'
|
| 7152 | 'are not a subtype of `RenderObjectWidget`.' ,
|
| 7153 | ),
|
| 7154 | newChild.describeElement(
|
| 7155 | 'The following element does not have an associated render object' ,
|
| 7156 | ),
|
| 7157 | DiagnosticsDebugCreator(DebugCreator(newChild)),
|
| 7158 | ]),
|
| 7159 | ),
|
| 7160 | );
|
| 7161 | }
|
| 7162 | return true;
|
| 7163 | }());
|
| 7164 | return true;
|
| 7165 | }
|
| 7166 |
|
| 7167 | @override
|
| 7168 | Element inflateWidget(Widget newWidget, Object? newSlot) {
|
| 7169 | final Element newChild = super.inflateWidget(newWidget, newSlot);
|
| 7170 | assert(_debugCheckHasAssociatedRenderObject(newChild));
|
| 7171 | return newChild;
|
| 7172 | }
|
| 7173 |
|
| 7174 | @override
|
| 7175 | void mount(Element? parent, Object? newSlot) {
|
| 7176 | super.mount(parent, newSlot);
|
| 7177 | final MultiChildRenderObjectWidget multiChildRenderObjectWidget =
|
| 7178 | widget as MultiChildRenderObjectWidget;
|
| 7179 | final List<Element> children = List<Element>.filled(
|
| 7180 | multiChildRenderObjectWidget.children.length,
|
| 7181 | _NullElement.instance,
|
| 7182 | );
|
| 7183 | Element? previousChild;
|
| 7184 | for (int i = 0; i < children.length; i += 1) {
|
| 7185 | final Element newChild = inflateWidget(
|
| 7186 | multiChildRenderObjectWidget.children[i],
|
| 7187 | IndexedSlot<Element?>(i, previousChild),
|
| 7188 | );
|
| 7189 | children[i] = newChild;
|
| 7190 | previousChild = newChild;
|
| 7191 | }
|
| 7192 | _children = children;
|
| 7193 | }
|
| 7194 |
|
| 7195 | @override
|
| 7196 | void update(MultiChildRenderObjectWidget newWidget) {
|
| 7197 | super.update(newWidget);
|
| 7198 | final MultiChildRenderObjectWidget multiChildRenderObjectWidget =
|
| 7199 | widget as MultiChildRenderObjectWidget;
|
| 7200 | assert(widget == newWidget);
|
| 7201 | assert(!debugChildrenHaveDuplicateKeys(widget, multiChildRenderObjectWidget.children));
|
| 7202 | _children = updateChildren(
|
| 7203 | _children,
|
| 7204 | multiChildRenderObjectWidget.children,
|
| 7205 | forgottenChildren: _forgottenChildren,
|
| 7206 | );
|
| 7207 | _forgottenChildren.clear();
|
| 7208 | }
|
| 7209 | }
|
| 7210 |
|
| 7211 | /// A [RenderObjectElement] used to manage the root of a render tree.
|
| 7212 | ///
|
| 7213 | /// Unlike any other render object element this element does not attempt to
|
| 7214 | /// attach its [renderObject] to the closest ancestor [RenderObjectElement].
|
| 7215 | /// Instead, subclasses must override [attachRenderObject] and
|
| 7216 | /// [detachRenderObject] to attach/detach the [renderObject] to whatever
|
| 7217 | /// instance manages the render tree (e.g. by assigning it to
|
| 7218 | /// [PipelineOwner.rootNode]).
|
| 7219 | abstract class RenderTreeRootElement extends RenderObjectElement {
|
| 7220 | /// Creates an element that uses the given widget as its configuration.
|
| 7221 | RenderTreeRootElement(super.widget);
|
| 7222 |
|
| 7223 | @override
|
| 7224 | @mustCallSuper
|
| 7225 | void attachRenderObject(Object? newSlot) {
|
| 7226 | _slot = newSlot;
|
| 7227 | assert(_debugCheckMustNotAttachRenderObjectToAncestor());
|
| 7228 | }
|
| 7229 |
|
| 7230 | @override
|
| 7231 | @mustCallSuper
|
| 7232 | void detachRenderObject() {
|
| 7233 | _slot = null;
|
| 7234 | }
|
| 7235 |
|
| 7236 | @override
|
| 7237 | void updateSlot(Object? newSlot) {
|
| 7238 | super.updateSlot(newSlot);
|
| 7239 | assert(_debugCheckMustNotAttachRenderObjectToAncestor());
|
| 7240 | }
|
| 7241 |
|
| 7242 | bool _debugCheckMustNotAttachRenderObjectToAncestor() {
|
| 7243 | if (!kDebugMode) {
|
| 7244 | return true;
|
| 7245 | }
|
| 7246 | if (_findAncestorRenderObjectElement() != null) {
|
| 7247 | throw FlutterError.fromParts(<DiagnosticsNode>[
|
| 7248 | ErrorSummary(
|
| 7249 | 'The RenderObject for ${toStringShort()} cannot maintain an independent render tree at its current location.' ,
|
| 7250 | ),
|
| 7251 | ErrorDescription(
|
| 7252 | 'The ownership chain for the RenderObject in question was:\n ${debugGetCreatorChain(10)}' ,
|
| 7253 | ),
|
| 7254 | ErrorDescription(
|
| 7255 | 'This RenderObject is the root of an independent render tree and it cannot '
|
| 7256 | 'attach itself to an ancestor in an existing tree. The ancestor RenderObject, '
|
| 7257 | 'however, expects that a child will be attached.' ,
|
| 7258 | ),
|
| 7259 | ErrorHint(
|
| 7260 | 'Try moving the subtree that contains the ${toStringShort()} widget '
|
| 7261 | 'to a location where it is not expected to attach its RenderObject '
|
| 7262 | 'to a parent. This could mean moving the subtree into the view '
|
| 7263 | 'property of a "ViewAnchor" widget or - if the subtree is the root of '
|
| 7264 | 'your widget tree - passing it to "runWidget" instead of "runApp".' ,
|
| 7265 | ),
|
| 7266 | ErrorHint(
|
| 7267 | 'If you are seeing this error in a test and the subtree containing '
|
| 7268 | 'the ${toStringShort()} widget is passed to "WidgetTester.pumpWidget", '
|
| 7269 | 'consider setting the "wrapWithView" parameter of that method to false.' ,
|
| 7270 | ),
|
| 7271 | ]);
|
| 7272 | }
|
| 7273 | return true;
|
| 7274 | }
|
| 7275 | }
|
| 7276 |
|
| 7277 | /// A wrapper class for the [Element] that is the creator of a [RenderObject].
|
| 7278 | ///
|
| 7279 | /// Setting a [DebugCreator] as [RenderObject.debugCreator] will lead to better
|
| 7280 | /// error messages.
|
| 7281 | class DebugCreator {
|
| 7282 | /// Create a [DebugCreator] instance with input [Element].
|
| 7283 | DebugCreator(this.element);
|
| 7284 |
|
| 7285 | /// The creator of the [RenderObject].
|
| 7286 | final Element element;
|
| 7287 |
|
| 7288 | @override
|
| 7289 | String toString() => element.debugGetCreatorChain(12);
|
| 7290 | }
|
| 7291 |
|
| 7292 | FlutterErrorDetails _reportException(
|
| 7293 | DiagnosticsNode context,
|
| 7294 | Object exception,
|
| 7295 | StackTrace? stack, {
|
| 7296 | InformationCollector? informationCollector,
|
| 7297 | }) {
|
| 7298 | final FlutterErrorDetails details = FlutterErrorDetails(
|
| 7299 | exception: exception,
|
| 7300 | stack: stack,
|
| 7301 | library: 'widgets library' ,
|
| 7302 | context: context,
|
| 7303 | informationCollector: informationCollector,
|
| 7304 | );
|
| 7305 | FlutterError.reportError(details);
|
| 7306 | return details;
|
| 7307 | }
|
| 7308 |
|
| 7309 | /// A value for [Element.slot] used for children of
|
| 7310 | /// [MultiChildRenderObjectElement]s.
|
| 7311 | ///
|
| 7312 | /// A slot for a [MultiChildRenderObjectElement] consists of an [index]
|
| 7313 | /// identifying where the child occupying this slot is located in the
|
| 7314 | /// [MultiChildRenderObjectElement]'s child list and an arbitrary [value] that
|
| 7315 | /// can further define where the child occupying this slot fits in its
|
| 7316 | /// parent's child list.
|
| 7317 | ///
|
| 7318 | /// See also:
|
| 7319 | ///
|
| 7320 | /// * [RenderObjectElement.updateChildren], which discusses why this class is
|
| 7321 | /// used as slot values for the children of a [MultiChildRenderObjectElement].
|
| 7322 | @immutable
|
| 7323 | class IndexedSlot<T extends Element?> {
|
| 7324 | /// Creates an [IndexedSlot] with the provided [index] and slot [value].
|
| 7325 | const IndexedSlot(this.index, this.value);
|
| 7326 |
|
| 7327 | /// Information to define where the child occupying this slot fits in its
|
| 7328 | /// parent's child list.
|
| 7329 | final T value;
|
| 7330 |
|
| 7331 | /// The index of this slot in the parent's child list.
|
| 7332 | final int index;
|
| 7333 |
|
| 7334 | @override
|
| 7335 | bool operator ==(Object other) {
|
| 7336 | if (other.runtimeType != runtimeType) {
|
| 7337 | return false;
|
| 7338 | }
|
| 7339 | return other is IndexedSlot && index == other.index && value == other.value;
|
| 7340 | }
|
| 7341 |
|
| 7342 | @override
|
| 7343 | int get hashCode => Object.hash(index, value);
|
| 7344 | }
|
| 7345 |
|
| 7346 | /// Used as a placeholder in [List<Element>] objects when the actual
|
| 7347 | /// elements are not yet determined.
|
| 7348 | class _NullElement extends Element {
|
| 7349 | _NullElement() : super(const _NullWidget());
|
| 7350 |
|
| 7351 | static _NullElement instance = _NullElement();
|
| 7352 |
|
| 7353 | @override
|
| 7354 | bool get debugDoingBuild => throw UnimplementedError();
|
| 7355 | }
|
| 7356 |
|
| 7357 | class _NullWidget extends Widget {
|
| 7358 | const _NullWidget();
|
| 7359 |
|
| 7360 | @override
|
| 7361 | Element createElement() => throw UnimplementedError();
|
| 7362 | }
|
| 7363 |
|