| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:collection'; |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| import 'binding.dart'; |
| import 'debug.dart'; |
| import 'focus_manager.dart'; |
| import 'inherited_model.dart'; |
| import 'notification_listener.dart'; |
| import 'widget_inspector.dart'; |
| |
| export 'package:flutter/foundation.dart' show |
| factory, |
| immutable, |
| mustCallSuper, |
| optionalTypeArgs, |
| protected, |
| required, |
| visibleForTesting; |
| export 'package:flutter/foundation.dart' show ErrorDescription, ErrorHint, ErrorSummary, FlutterError, debugPrint, debugPrintStack; |
| export 'package:flutter/foundation.dart' show ValueChanged, ValueGetter, ValueSetter, VoidCallback; |
| export 'package:flutter/foundation.dart' show DiagnosticLevel, DiagnosticsNode; |
| export 'package:flutter/foundation.dart' show Key, LocalKey, ValueKey; |
| export 'package:flutter/rendering.dart' show RenderBox, RenderObject, debugDumpLayerTree, debugDumpRenderTree; |
| |
| // Examples can assume: |
| // late BuildContext context; |
| // void setState(VoidCallback fn) { } |
| // abstract class RenderFrogJar extends RenderObject { } |
| // abstract class FrogJar extends RenderObjectWidget { const FrogJar({super.key}); } |
| // abstract class FrogJarParentData extends ParentData { late Size size; } |
| // abstract class SomeWidget extends StatefulWidget { const SomeWidget({super.key}); } |
| // typedef ChildWidget = Placeholder; |
| // class _SomeWidgetState extends State<SomeWidget> { @override Widget build(BuildContext context) => widget; } |
| // abstract class RenderFoo extends RenderObject { } |
| // abstract class Foo extends RenderObjectWidget { const Foo({super.key}); } |
| // abstract class StatefulWidgetX { const StatefulWidgetX({this.key}); final Key? key; Widget build(BuildContext context, State state); } |
| // class SpecialWidget extends StatelessWidget { const SpecialWidget({ super.key, this.handler }); final VoidCallback? handler; @override Widget build(BuildContext context) => this; } |
| // late Object? _myState, newValue; |
| // int _counter = 0; |
| // Future<Directory> getApplicationDocumentsDirectory() async => Directory(''); |
| |
| // An annotation used by test_analysis package to verify patterns are followed |
| // that allow for tree-shaking of both fields and their initializers. This |
| // annotation has no impact on code by itself, but indicates the following pattern |
| // should be followed for a given field: |
| // |
| // ```dart |
| // class Foo { |
| // final bar = kDebugMode ? Object() : null; |
| // } |
| // ``` |
| class _DebugOnly { |
| const _DebugOnly(); |
| } |
| |
| const _DebugOnly _debugOnly = _DebugOnly(); |
| |
| // KEYS |
| |
| /// A key that takes its identity from the object used as its value. |
| /// |
| /// Used to tie the identity of a widget to the identity of an object used to |
| /// generate that widget. |
| /// |
| /// See also: |
| /// |
| /// * [Key], the base class for all keys. |
| /// * The discussion at [Widget.key] for more information about how widgets use |
| /// keys. |
| class ObjectKey extends LocalKey { |
| /// Creates a key that uses [identical] on [value] for its [operator==]. |
| const ObjectKey(this.value); |
| |
| /// The object whose identity is used by this key's [operator==]. |
| final Object? value; |
| |
| @override |
| bool operator ==(Object other) { |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| return other is ObjectKey |
| && identical(other.value, value); |
| } |
| |
| @override |
| int get hashCode => Object.hash(runtimeType, identityHashCode(value)); |
| |
| @override |
| String toString() { |
| if (runtimeType == ObjectKey) { |
| return '[${describeIdentity(value)}]'; |
| } |
| return '[${objectRuntimeType(this, 'ObjectKey')} ${describeIdentity(value)}]'; |
| } |
| } |
| |
| /// A key that is unique across the entire app. |
| /// |
| /// Global keys uniquely identify elements. Global keys provide access to other |
| /// objects that are associated with those elements, such as [BuildContext]. |
| /// For [StatefulWidget]s, global keys also provide access to [State]. |
| /// |
| /// Widgets that have global keys reparent their subtrees when they are moved |
| /// from one location in the tree to another location in the tree. In order to |
| /// reparent its subtree, a widget must arrive at its new location in the tree |
| /// in the same animation frame in which it was removed from its old location in |
| /// the tree. |
| /// |
| /// Reparenting an [Element] using a global key is relatively expensive, as |
| /// this operation will trigger a call to [State.deactivate] on the associated |
| /// [State] and all of its descendants; then force all widgets that depends |
| /// on an [InheritedWidget] to rebuild. |
| /// |
| /// If you don't need any of the features listed above, consider using a [Key], |
| /// [ValueKey], [ObjectKey], or [UniqueKey] instead. |
| /// |
| /// You cannot simultaneously include two widgets in the tree with the same |
| /// global key. Attempting to do so will assert at runtime. |
| /// |
| /// ## Pitfalls |
| /// |
| /// GlobalKeys should not be re-created on every build. They should usually be |
| /// long-lived objects owned by a [State] object, for example. |
| /// |
| /// Creating a new GlobalKey on every build will throw away the state of the |
| /// subtree associated with the old key and create a new fresh subtree for the |
| /// new key. Besides harming performance, this can also cause unexpected |
| /// behavior in widgets in the subtree. For example, a [GestureDetector] in the |
| /// subtree will be unable to track ongoing gestures since it will be recreated |
| /// on each build. |
| /// |
| /// Instead, a good practice is to let a State object own the GlobalKey, and |
| /// instantiate it outside the build method, such as in [State.initState]. |
| /// |
| /// See also: |
| /// |
| /// * The discussion at [Widget.key] for more information about how widgets use |
| /// keys. |
| @optionalTypeArgs |
| abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { |
| /// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a label used for |
| /// debugging. |
| /// |
| /// The label is purely for debugging and not used for comparing the identity |
| /// of the key. |
| factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel); |
| |
| /// Creates a global key without a label. |
| /// |
| /// Used by subclasses because the factory constructor shadows the implicit |
| /// constructor. |
| const GlobalKey.constructor() : super.empty(); |
| |
| Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this]; |
| |
| /// The build context in which the widget with this key builds. |
| /// |
| /// The current context is null if there is no widget in the tree that matches |
| /// this global key. |
| BuildContext? get currentContext => _currentElement; |
| |
| /// The widget in the tree that currently has this global key. |
| /// |
| /// The current widget is null if there is no widget in the tree that matches |
| /// this global key. |
| Widget? get currentWidget => _currentElement?.widget; |
| |
| /// The [State] for the widget in the tree that currently has this global key. |
| /// |
| /// The current state is null if (1) there is no widget in the tree that |
| /// matches this global key, (2) that widget is not a [StatefulWidget], or the |
| /// associated [State] object is not a subtype of `T`. |
| T? get currentState { |
| final Element? element = _currentElement; |
| if (element is StatefulElement) { |
| final StatefulElement statefulElement = element; |
| final State state = statefulElement.state; |
| if (state is T) { |
| return state; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /// A global key with a debugging label. |
| /// |
| /// The debug label is useful for documentation and for debugging. The label |
| /// does not affect the key's identity. |
| @optionalTypeArgs |
| class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
| /// Creates a global key with a debugging label. |
| /// |
| /// The label does not affect the key's identity. |
| // ignore: prefer_const_constructors_in_immutables , never use const for this class |
| LabeledGlobalKey(this._debugLabel) : super.constructor(); |
| |
| final String? _debugLabel; |
| |
| @override |
| String toString() { |
| final String label = _debugLabel != null ? ' $_debugLabel' : ''; |
| if (runtimeType == LabeledGlobalKey) { |
| return '[GlobalKey#${shortHash(this)}$label]'; |
| } |
| return '[${describeIdentity(this)}$label]'; |
| } |
| } |
| |
| /// A global key that takes its identity from the object used as its value. |
| /// |
| /// Used to tie the identity of a widget to the identity of an object used to |
| /// generate that widget. |
| /// |
| /// Any [GlobalObjectKey] created for the same object will match. |
| /// |
| /// If the object is not private, then it is possible that collisions will occur |
| /// where independent widgets will reuse the same object as their |
| /// [GlobalObjectKey] value in a different part of the tree, leading to a global |
| /// key conflict. To avoid this problem, create a private [GlobalObjectKey] |
| /// subclass, as in: |
| /// |
| /// ```dart |
| /// class _MyKey extends GlobalObjectKey { |
| /// const _MyKey(super.value); |
| /// } |
| /// ``` |
| /// |
| /// Since the [runtimeType] of the key is part of its identity, this will |
| /// prevent clashes with other [GlobalObjectKey]s even if they have the same |
| /// value. |
| @optionalTypeArgs |
| class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
| /// Creates a global key that uses [identical] on [value] for its [operator==]. |
| const GlobalObjectKey(this.value) : super.constructor(); |
| |
| /// The object whose identity is used by this key's [operator==]. |
| final Object value; |
| |
| @override |
| bool operator ==(Object other) { |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| return other is GlobalObjectKey<T> |
| && identical(other.value, value); |
| } |
| |
| @override |
| int get hashCode => identityHashCode(value); |
| |
| @override |
| String toString() { |
| String selfType = objectRuntimeType(this, 'GlobalObjectKey'); |
| // The runtimeType string of a GlobalObjectKey() returns 'GlobalObjectKey<State<StatefulWidget>>' |
| // because GlobalObjectKey is instantiated to its bounds. To avoid cluttering the output |
| // we remove the suffix. |
| const String suffix = '<State<StatefulWidget>>'; |
| if (selfType.endsWith(suffix)) { |
| selfType = selfType.substring(0, selfType.length - suffix.length); |
| } |
| return '[$selfType ${describeIdentity(value)}]'; |
| } |
| } |
| |
| /// Describes the configuration for an [Element]. |
| /// |
| /// Widgets are the central class hierarchy in the Flutter framework. A widget |
| /// is an immutable description of part of a user interface. Widgets can be |
| /// inflated into elements, which manage the underlying render tree. |
| /// |
| /// Widgets themselves have no mutable state (all their fields must be final). |
| /// If you wish to associate mutable state with a widget, consider using a |
| /// [StatefulWidget], which creates a [State] object (via |
| /// [StatefulWidget.createState]) whenever it is inflated into an element and |
| /// incorporated into the tree. |
| /// |
| /// A given widget can be included in the tree zero or more times. In particular |
| /// a given widget can be placed in the tree multiple times. Each time a widget |
| /// is placed in the tree, it is inflated into an [Element], which means a |
| /// widget that is incorporated into the tree multiple times will be inflated |
| /// multiple times. |
| /// |
| /// The [key] property controls how one widget replaces another widget in the |
| /// tree. If the [runtimeType] and [key] properties of the two widgets are |
| /// [operator==], respectively, then the new widget replaces the old widget by |
| /// updating the underlying element (i.e., by calling [Element.update] with the |
| /// new widget). Otherwise, the old element is removed from the tree, the new |
| /// widget is inflated into an element, and the new element is inserted into the |
| /// tree. |
| /// |
| /// See also: |
| /// |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| /// * [InheritedWidget], for widgets that introduce ambient state that can |
| /// be read by descendant widgets. |
| /// * [StatelessWidget], for widgets that always build the same way given a |
| /// particular configuration and ambient state. |
| @immutable |
| abstract class Widget extends DiagnosticableTree { |
| /// Initializes [key] for subclasses. |
| const Widget({ this.key }); |
| |
| /// Controls how one widget replaces another widget in the tree. |
| /// |
| /// If the [runtimeType] and [key] properties of the two widgets are |
| /// [operator==], respectively, then the new widget replaces the old widget by |
| /// updating the underlying element (i.e., by calling [Element.update] with the |
| /// new widget). Otherwise, the old element is removed from the tree, the new |
| /// widget is inflated into an element, and the new element is inserted into the |
| /// tree. |
| /// |
| /// In addition, using a [GlobalKey] as the widget's [key] allows the element |
| /// to be moved around the tree (changing parent) without losing state. When a |
| /// new widget is found (its key and type do not match a previous widget in |
| /// the same location), but there was a widget with that same global key |
| /// elsewhere in the tree in the previous frame, then that widget's element is |
| /// moved to the new location. |
| /// |
| /// Generally, a widget that is the only child of another widget does not need |
| /// an explicit key. |
| /// |
| /// See also: |
| /// |
| /// * The discussions at [Key] and [GlobalKey]. |
| final Key? key; |
| |
| /// Inflates this configuration to a concrete instance. |
| /// |
| /// A given widget can be included in the tree zero or more times. In particular |
| /// a given widget can be placed in the tree multiple times. Each time a widget |
| /// is placed in the tree, it is inflated into an [Element], which means a |
| /// widget that is incorporated into the tree multiple times will be inflated |
| /// multiple times. |
| @protected |
| @factory |
| Element createElement(); |
| |
| /// A short, textual description of this widget. |
| @override |
| String toStringShort() { |
| final String type = objectRuntimeType(this, 'Widget'); |
| return key == null ? type : '$type-$key'; |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; |
| } |
| |
| @override |
| @nonVirtual |
| bool operator ==(Object other) => super == other; |
| |
| @override |
| @nonVirtual |
| int get hashCode => super.hashCode; |
| |
| /// Whether the `newWidget` can be used to update an [Element] that currently |
| /// has the `oldWidget` as its configuration. |
| /// |
| /// An element that uses a given widget as its configuration can be updated to |
| /// use another widget as its configuration if, and only if, the two widgets |
| /// have [runtimeType] and [key] properties that are [operator==]. |
| /// |
| /// If the widgets have no key (their key is null), then they are considered a |
| /// match if they have the same type, even if their children are completely |
| /// different. |
| static bool canUpdate(Widget oldWidget, Widget newWidget) { |
| return oldWidget.runtimeType == newWidget.runtimeType |
| && oldWidget.key == newWidget.key; |
| } |
| |
| // Return a numeric encoding of the specific `Widget` concrete subtype. |
| // This is used in `Element.updateChild` to determine if a hot reload modified the |
| // superclass of a mounted element's configuration. The encoding of each `Widget` |
| // must match the corresponding `Element` encoding in `Element._debugConcreteSubtype`. |
| static int _debugConcreteSubtype(Widget widget) { |
| return widget is StatefulWidget ? 1 : |
| widget is StatelessWidget ? 2 : |
| 0; |
| } |
| } |
| |
| /// A widget that does not require mutable state. |
| /// |
| /// A stateless widget is a widget that describes part of the user interface by |
| /// building a constellation of other widgets that describe the user interface |
| /// more concretely. The building process continues recursively until the |
| /// description of the user interface is fully concrete (e.g., consists |
| /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s). |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=wE7khGHVkYY} |
| /// |
| /// Stateless widget are useful when the part of the user interface you are |
| /// describing does not depend on anything other than the configuration |
| /// information in the object itself and the [BuildContext] in which the widget |
| /// is inflated. For compositions that can change dynamically, e.g. due to |
| /// having an internal clock-driven state, or depending on some system state, |
| /// consider using [StatefulWidget]. |
| /// |
| /// ## Performance considerations |
| /// |
| /// The [build] method of a stateless widget is typically only called in three |
| /// situations: the first time the widget is inserted in the tree, when the |
| /// widget's parent changes its configuration (see [Element.rebuild]), and when |
| /// an [InheritedWidget] it depends on changes. |
| /// |
| /// If a widget's parent will regularly change the widget's configuration, or if |
| /// it depends on inherited widgets that frequently change, then it is important |
| /// to optimize the performance of the [build] method to maintain a fluid |
| /// rendering performance. |
| /// |
| /// There are several techniques one can use to minimize the impact of |
| /// rebuilding a stateless widget: |
| /// |
| /// * Minimize the number of nodes transitively created by the build method and |
| /// any widgets it creates. For example, instead of an elaborate arrangement |
| /// of [Row]s, [Column]s, [Padding]s, and [SizedBox]es to position a single |
| /// child in a particularly fancy manner, consider using just an [Align] or a |
| /// [CustomSingleChildLayout]. Instead of an intricate layering of multiple |
| /// [Container]s and with [Decoration]s to draw just the right graphical |
| /// effect, consider a single [CustomPaint] widget. |
| /// |
| /// * Use `const` widgets where possible, and provide a `const` constructor for |
| /// the widget so that users of the widget can also do so. |
| /// |
| /// * Consider refactoring the stateless widget into a stateful widget so that |
| /// it can use some of the techniques described at [StatefulWidget], such as |
| /// caching common parts of subtrees and using [GlobalKey]s when changing the |
| /// tree structure. |
| /// |
| /// * If the widget is likely to get rebuilt frequently due to the use of |
| /// [InheritedWidget]s, consider refactoring the stateless widget into |
| /// multiple widgets, with the parts of the tree that change being pushed to |
| /// the leaves. For example instead of building a tree with four widgets, the |
| /// inner-most widget depending on the [Theme], consider factoring out the |
| /// part of the build function that builds the inner-most widget into its own |
| /// widget, so that only the inner-most widget needs to be rebuilt when the |
| /// theme changes. |
| /// {@template flutter.flutter.widgets.framework.prefer_const_over_helper} |
| /// * When trying to create a reusable piece of UI, prefer using a widget |
| /// rather than a helper method. For example, if there was a function used to |
| /// build a widget, a [State.setState] call would require Flutter to entirely |
| /// rebuild the returned wrapping widget. If a [Widget] was used instead, |
| /// Flutter would be able to efficiently re-render only those parts that |
| /// really need to be updated. Even better, if the created widget is `const`, |
| /// Flutter would short-circuit most of the rebuild work. |
| /// {@endtemplate} |
| /// |
| /// This video gives more explanations on why `const` constructors are important |
| /// and why a [Widget] is better than a helper method. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo} |
| /// |
| /// {@tool snippet} |
| /// |
| /// The following is a skeleton of a stateless widget subclass called `GreenFrog`. |
| /// |
| /// Normally, widgets have more constructor arguments, each of which corresponds |
| /// to a `final` property. |
| /// |
| /// ```dart |
| /// class GreenFrog extends StatelessWidget { |
| /// const GreenFrog({ super.key }); |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return Container(color: const Color(0xFF2DBD3A)); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// {@tool snippet} |
| /// |
| /// This next example shows the more generic widget `Frog` which can be given |
| /// a color and a child: |
| /// |
| /// ```dart |
| /// class Frog extends StatelessWidget { |
| /// const Frog({ |
| /// super.key, |
| /// this.color = const Color(0xFF2DBD3A), |
| /// this.child, |
| /// }); |
| /// |
| /// final Color color; |
| /// final Widget? child; |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return ColoredBox(color: color, child: child); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// By convention, widget constructors only use named arguments. Also by |
| /// convention, the first argument is [key], and the last argument is `child`, |
| /// `children`, or the equivalent. |
| /// |
| /// See also: |
| /// |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| /// * [InheritedWidget], for widgets that introduce ambient state that can |
| /// be read by descendant widgets. |
| abstract class StatelessWidget extends Widget { |
| /// Initializes [key] for subclasses. |
| const StatelessWidget({ super.key }); |
| |
| /// Creates a [StatelessElement] to manage this widget's location in the tree. |
| /// |
| /// It is uncommon for subclasses to override this method. |
| @override |
| StatelessElement createElement() => StatelessElement(this); |
| |
| /// Describes the part of the user interface represented by this widget. |
| /// |
| /// The framework calls this method when this widget is inserted into the tree |
| /// in a given [BuildContext] and when the dependencies of this widget change |
| /// (e.g., an [InheritedWidget] referenced by this widget changes). This |
| /// method can potentially be called in every frame and should not have any side |
| /// effects beyond building a widget. |
| /// |
| /// The framework replaces the subtree below this widget with the widget |
| /// returned by this method, either by updating the existing subtree or by |
| /// removing the subtree and inflating a new subtree, depending on whether the |
| /// widget returned by this method can update the root of the existing |
| /// subtree, as determined by calling [Widget.canUpdate]. |
| /// |
| /// Typically implementations return a newly created constellation of widgets |
| /// that are configured with information from this widget's constructor and |
| /// from the given [BuildContext]. |
| /// |
| /// The given [BuildContext] contains information about the location in the |
| /// tree at which this widget is being built. For example, the context |
| /// provides the set of inherited widgets for this location in the tree. A |
| /// given widget might be built with multiple different [BuildContext] |
| /// arguments over time if the widget is moved around the tree or if the |
| /// widget is inserted into the tree in multiple places at once. |
| /// |
| /// The implementation of this method must only depend on: |
| /// |
| /// * the fields of the widget, which themselves must not change over time, |
| /// and |
| /// * any ambient state obtained from the `context` using |
| /// [BuildContext.dependOnInheritedWidgetOfExactType]. |
| /// |
| /// If a widget's [build] method is to depend on anything else, use a |
| /// [StatefulWidget] instead. |
| /// |
| /// See also: |
| /// |
| /// * [StatelessWidget], which contains the discussion on performance considerations. |
| @protected |
| Widget build(BuildContext context); |
| } |
| |
| /// A widget that has mutable state. |
| /// |
| /// State is information that (1) can be read synchronously when the widget is |
| /// built and (2) might change during the lifetime of the widget. It is the |
| /// responsibility of the widget implementer to ensure that the [State] is |
| /// promptly notified when such state changes, using [State.setState]. |
| /// |
| /// A stateful widget is a widget that describes part of the user interface by |
| /// building a constellation of other widgets that describe the user interface |
| /// more concretely. The building process continues recursively until the |
| /// description of the user interface is fully concrete (e.g., consists |
| /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s). |
| /// |
| /// Stateful widgets are useful when the part of the user interface you are |
| /// describing can change dynamically, e.g. due to having an internal |
| /// clock-driven state, or depending on some system state. For compositions that |
| /// depend only on the configuration information in the object itself and the |
| /// [BuildContext] in which the widget is inflated, consider using |
| /// [StatelessWidget]. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=AqCMFXEmf3w} |
| /// |
| /// [StatefulWidget] instances themselves are immutable and store their mutable |
| /// state either in separate [State] objects that are created by the |
| /// [createState] method, or in objects to which that [State] subscribes, for |
| /// example [Stream] or [ChangeNotifier] objects, to which references are stored |
| /// in final fields on the [StatefulWidget] itself. |
| /// |
| /// The framework calls [createState] whenever it inflates a |
| /// [StatefulWidget], which means that multiple [State] objects might be |
| /// associated with the same [StatefulWidget] if that widget has been inserted |
| /// into the tree in multiple places. Similarly, if a [StatefulWidget] is |
| /// removed from the tree and later inserted in to the tree again, the framework |
| /// will call [createState] again to create a fresh [State] object, simplifying |
| /// the lifecycle of [State] objects. |
| /// |
| /// A [StatefulWidget] keeps the same [State] object when moving from one |
| /// location in the tree to another if its creator used a [GlobalKey] for its |
| /// [key]. Because a widget with a [GlobalKey] can be used in at most one |
| /// location in the tree, a widget that uses a [GlobalKey] has at most one |
| /// associated element. The framework takes advantage of this property when |
| /// moving a widget with a global key from one location in the tree to another |
| /// by grafting the (unique) subtree associated with that widget from the old |
| /// location to the new location (instead of recreating the subtree at the new |
| /// location). The [State] objects associated with [StatefulWidget] are grafted |
| /// along with the rest of the subtree, which means the [State] object is reused |
| /// (instead of being recreated) in the new location. However, in order to be |
| /// eligible for grafting, the widget must be inserted into the new location in |
| /// the same animation frame in which it was removed from the old location. |
| /// |
| /// ## Performance considerations |
| /// |
| /// There are two primary categories of [StatefulWidget]s. |
| /// |
| /// The first is one which allocates resources in [State.initState] and disposes |
| /// of them in [State.dispose], but which does not depend on [InheritedWidget]s |
| /// or call [State.setState]. Such widgets are commonly used at the root of an |
| /// application or page, and communicate with subwidgets via [ChangeNotifier]s, |
| /// [Stream]s, or other such objects. Stateful widgets following such a pattern |
| /// are relatively cheap (in terms of CPU and GPU cycles), because they are |
| /// built once then never update. They can, therefore, have somewhat complicated |
| /// and deep build methods. |
| /// |
| /// The second category is widgets that use [State.setState] or depend on |
| /// [InheritedWidget]s. These will typically rebuild many times during the |
| /// application's lifetime, and it is therefore important to minimize the impact |
| /// of rebuilding such a widget. (They may also use [State.initState] or |
| /// [State.didChangeDependencies] and allocate resources, but the important part |
| /// is that they rebuild.) |
| /// |
| /// There are several techniques one can use to minimize the impact of |
| /// rebuilding a stateful widget: |
| /// |
| /// * Push the state to the leaves. For example, if your page has a ticking |
| /// clock, rather than putting the state at the top of the page and |
| /// rebuilding the entire page each time the clock ticks, create a dedicated |
| /// clock widget that only updates itself. |
| /// |
| /// * Minimize the number of nodes transitively created by the build method and |
| /// any widgets it creates. Ideally, a stateful widget would only create a |
| /// single widget, and that widget would be a [RenderObjectWidget]. |
| /// (Obviously this isn't always practical, but the closer a widget gets to |
| /// this ideal, the more efficient it will be.) |
| /// |
| /// * If a subtree does not change, cache the widget that represents that |
| /// subtree and re-use it each time it can be used. To do this, assign |
| /// a widget to a `final` state variable and re-use it in the build method. It |
| /// is massively more efficient for a widget to be re-used than for a new (but |
| /// identically-configured) widget to be created. Another caching strategy |
| /// consists in extracting the mutable part of the widget into a [StatefulWidget] |
| /// which accepts a child parameter. |
| /// |
| /// * Use `const` widgets where possible. (This is equivalent to caching a |
| /// widget and re-using it.) |
| /// |
| /// * Avoid changing the depth of any created subtrees or changing the type of |
| /// any widgets in the subtree. For example, rather than returning either the |
| /// child or the child wrapped in an [IgnorePointer], always wrap the child |
| /// widget in an [IgnorePointer] and control the [IgnorePointer.ignoring] |
| /// property. This is because changing the depth of the subtree requires |
| /// rebuilding, laying out, and painting the entire subtree, whereas just |
| /// changing the property will require the least possible change to the |
| /// render tree (in the case of [IgnorePointer], for example, no layout or |
| /// repaint is necessary at all). |
| /// |
| /// * If the depth must be changed for some reason, consider wrapping the |
| /// common parts of the subtrees in widgets that have a [GlobalKey] that |
| /// remains consistent for the life of the stateful widget. (The |
| /// [KeyedSubtree] widget may be useful for this purpose if no other widget |
| /// can conveniently be assigned the key.) |
| /// |
| /// {@macro flutter.flutter.widgets.framework.prefer_const_over_helper} |
| /// |
| /// This video gives more explanations on why `const` constructors are important |
| /// and why a [Widget] is better than a helper method. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo} |
| /// |
| /// For more details on the mechanics of rebuilding a widget, see |
| /// the discussion at [Element.rebuild]. |
| /// |
| /// {@tool snippet} |
| /// |
| /// This is a skeleton of a stateful widget subclass called `YellowBird`. |
| /// |
| /// In this example, the [State] has no actual state. State is normally |
| /// represented as private member fields. Also, normally widgets have more |
| /// constructor arguments, each of which corresponds to a `final` property. |
| /// |
| /// ```dart |
| /// class YellowBird extends StatefulWidget { |
| /// const YellowBird({ super.key }); |
| /// |
| /// @override |
| /// State<YellowBird> createState() => _YellowBirdState(); |
| /// } |
| /// |
| /// class _YellowBirdState extends State<YellowBird> { |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return Container(color: const Color(0xFFFFE306)); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// {@tool snippet} |
| /// |
| /// This example shows the more generic widget `Bird` which can be given a |
| /// color and a child, and which has some internal state with a method that |
| /// can be called to mutate it: |
| /// |
| /// ```dart |
| /// class Bird extends StatefulWidget { |
| /// const Bird({ |
| /// super.key, |
| /// this.color = const Color(0xFFFFE306), |
| /// this.child, |
| /// }); |
| /// |
| /// final Color color; |
| /// final Widget? child; |
| /// |
| /// @override |
| /// State<Bird> createState() => _BirdState(); |
| /// } |
| /// |
| /// class _BirdState extends State<Bird> { |
| /// double _size = 1.0; |
| /// |
| /// void grow() { |
| /// setState(() { _size += 0.1; }); |
| /// } |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return Container( |
| /// color: widget.color, |
| /// transform: Matrix4.diagonal3Values(_size, _size, 1.0), |
| /// child: widget.child, |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// By convention, widget constructors only use named arguments. Also by |
| /// convention, the first argument is [key], and the last argument is `child`, |
| /// `children`, or the equivalent. |
| /// |
| /// See also: |
| /// |
| /// * [State], where the logic behind a [StatefulWidget] is hosted. |
| /// * [StatelessWidget], for widgets that always build the same way given a |
| /// particular configuration and ambient state. |
| /// * [InheritedWidget], for widgets that introduce ambient state that can |
| /// be read by descendant widgets. |
| abstract class StatefulWidget extends Widget { |
| /// Initializes [key] for subclasses. |
| const StatefulWidget({ super.key }); |
| |
| /// Creates a [StatefulElement] to manage this widget's location in the tree. |
| /// |
| /// It is uncommon for subclasses to override this method. |
| @override |
| StatefulElement createElement() => StatefulElement(this); |
| |
| /// Creates the mutable state for this widget at a given location in the tree. |
| /// |
| /// Subclasses should override this method to return a newly created |
| /// instance of their associated [State] subclass: |
| /// |
| /// ```dart |
| /// @override |
| /// State<SomeWidget> createState() => _SomeWidgetState(); |
| /// ``` |
| /// |
| /// The framework can call this method multiple times over the lifetime of |
| /// a [StatefulWidget]. For example, if the widget is inserted into the tree |
| /// in multiple locations, the framework will create a separate [State] object |
| /// for each location. Similarly, if the widget is removed from the tree and |
| /// later inserted into the tree again, the framework will call [createState] |
| /// again to create a fresh [State] object, simplifying the lifecycle of |
| /// [State] objects. |
| @protected |
| @factory |
| State createState(); |
| } |
| |
| /// Tracks the lifecycle of [State] objects when asserts are enabled. |
| enum _StateLifecycle { |
| /// The [State] object has been created. [State.initState] is called at this |
| /// time. |
| created, |
| |
| /// The [State.initState] method has been called but the [State] object is |
| /// not yet ready to build. [State.didChangeDependencies] is called at this time. |
| initialized, |
| |
| /// The [State] object is ready to build and [State.dispose] has not yet been |
| /// called. |
| ready, |
| |
| /// The [State.dispose] method has been called and the [State] object is |
| /// no longer able to build. |
| defunct, |
| } |
| |
| /// The signature of [State.setState] functions. |
| typedef StateSetter = void Function(VoidCallback fn); |
| |
| const String _flutterWidgetsLibrary = 'package:flutter/widgets.dart'; |
| |
| /// The logic and internal state for a [StatefulWidget]. |
| /// |
| /// State is information that (1) can be read synchronously when the widget is |
| /// built and (2) might change during the lifetime of the widget. It is the |
| /// responsibility of the widget implementer to ensure that the [State] is |
| /// promptly notified when such state changes, using [State.setState]. |
| /// |
| /// [State] objects are created by the framework by calling the |
| /// [StatefulWidget.createState] method when inflating a [StatefulWidget] to |
| /// insert it into the tree. Because a given [StatefulWidget] instance can be |
| /// inflated multiple times (e.g., the widget is incorporated into the tree in |
| /// multiple places at once), there might be more than one [State] object |
| /// associated with a given [StatefulWidget] instance. Similarly, if a |
| /// [StatefulWidget] is removed from the tree and later inserted in to the tree |
| /// again, the framework will call [StatefulWidget.createState] again to create |
| /// a fresh [State] object, simplifying the lifecycle of [State] objects. |
| /// |
| /// [State] objects have the following lifecycle: |
| /// |
| /// * The framework creates a [State] object by calling |
| /// [StatefulWidget.createState]. |
| /// * The newly created [State] object is associated with a [BuildContext]. |
| /// This association is permanent: the [State] object will never change its |
| /// [BuildContext]. However, the [BuildContext] itself can be moved around |
| /// the tree along with its subtree. At this point, the [State] object is |
| /// considered [mounted]. |
| /// * The framework calls [initState]. Subclasses of [State] should override |
| /// [initState] to perform one-time initialization that depends on the |
| /// [BuildContext] or the widget, which are available as the [context] and |
| /// [widget] properties, respectively, when the [initState] method is |
| /// called. |
| /// * The framework calls [didChangeDependencies]. Subclasses of [State] should |
| /// override [didChangeDependencies] to perform initialization involving |
| /// [InheritedWidget]s. If [BuildContext.dependOnInheritedWidgetOfExactType] is |
| /// called, the [didChangeDependencies] method will be called again if the |
| /// inherited widgets subsequently change or if the widget moves in the tree. |
| /// * At this point, the [State] object is fully initialized and the framework |
| /// might call its [build] method any number of times to obtain a |
| /// description of the user interface for this subtree. [State] objects can |
| /// spontaneously request to rebuild their subtree by calling their |
| /// [setState] method, which indicates that some of their internal state |
| /// has changed in a way that might impact the user interface in this |
| /// subtree. |
| /// * During this time, a parent widget might rebuild and request that this |
| /// location in the tree update to display a new widget with the same |
| /// [runtimeType] and [Widget.key]. When this happens, the framework will |
| /// update the [widget] property to refer to the new widget and then call the |
| /// [didUpdateWidget] method with the previous widget as an argument. [State] |
| /// objects should override [didUpdateWidget] to respond to changes in their |
| /// associated widget (e.g., to start implicit animations). The framework |
| /// always calls [build] after calling [didUpdateWidget], which means any |
| /// calls to [setState] in [didUpdateWidget] are redundant. (See alse the |
| /// discussion at [Element.rebuild].) |
| /// * During development, if a hot reload occurs (whether initiated from the |
| /// command line `flutter` tool by pressing `r`, or from an IDE), the |
| /// [reassemble] method is called. This provides an opportunity to |
| /// reinitialize any data that was prepared in the [initState] method. |
| /// * If the subtree containing the [State] object is removed from the tree |
| /// (e.g., because the parent built a widget with a different [runtimeType] |
| /// or [Widget.key]), the framework calls the [deactivate] method. Subclasses |
| /// should override this method to clean up any links between this object |
| /// and other elements in the tree (e.g. if you have provided an ancestor |
| /// with a pointer to a descendant's [RenderObject]). |
| /// * At this point, the framework might reinsert this subtree into another |
| /// part of the tree. If that happens, the framework will ensure that it |
| /// calls [build] to give the [State] object a chance to adapt to its new |
| /// location in the tree. If the framework does reinsert this subtree, it |
| /// will do so before the end of the animation frame in which the subtree was |
| /// removed from the tree. For this reason, [State] objects can defer |
| /// releasing most resources until the framework calls their [dispose] |
| /// method. |
| /// * If the framework does not reinsert this subtree by the end of the current |
| /// animation frame, the framework will call [dispose], which indicates that |
| /// this [State] object will never build again. Subclasses should override |
| /// this method to release any resources retained by this object (e.g., |
| /// stop any active animations). |
| /// * After the framework calls [dispose], the [State] object is considered |
| /// unmounted and the [mounted] property is false. It is an error to call |
| /// [setState] at this point. This stage of the lifecycle is terminal: there |
| /// is no way to remount a [State] object that has been disposed. |
| /// |
| /// See also: |
| /// |
| /// * [StatefulWidget], where the current configuration of a [State] is hosted, |
| /// and whose documentation has sample code for [State]. |
| /// * [StatelessWidget], for widgets that always build the same way given a |
| /// particular configuration and ambient state. |
| /// * [InheritedWidget], for widgets that introduce ambient state that can |
| /// be read by descendant widgets. |
| /// * [Widget], for an overview of widgets in general. |
| @optionalTypeArgs |
| abstract class State<T extends StatefulWidget> with Diagnosticable { |
| /// The current configuration. |
| /// |
| /// A [State] object's configuration is the corresponding [StatefulWidget] |
| /// instance. This property is initialized by the framework before calling |
| /// [initState]. If the parent updates this location in the tree to a new |
| /// widget with the same [runtimeType] and [Widget.key] as the current |
| /// configuration, the framework will update this property to refer to the new |
| /// widget and then call [didUpdateWidget], passing the old configuration as |
| /// an argument. |
| T get widget => _widget!; |
| T? _widget; |
| |
| /// The current stage in the lifecycle for this state object. |
| /// |
| /// This field is used by the framework when asserts are enabled to verify |
| /// that [State] objects move through their lifecycle in an orderly fashion. |
| _StateLifecycle _debugLifecycleState = _StateLifecycle.created; |
| |
| /// Verifies that the [State] that was created is one that expects to be |
| /// created for that particular [Widget]. |
| bool _debugTypesAreRight(Widget widget) => widget is T; |
| |
| /// The location in the tree where this widget builds. |
| /// |
| /// The framework associates [State] objects with a [BuildContext] after |
| /// creating them with [StatefulWidget.createState] and before calling |
| /// [initState]. The association is permanent: the [State] object will never |
| /// change its [BuildContext]. However, the [BuildContext] itself can be moved |
| /// around the tree. |
| /// |
| /// After calling [dispose], the framework severs the [State] object's |
| /// connection with the [BuildContext]. |
| BuildContext get context { |
| assert(() { |
| if (_element == null) { |
| throw FlutterError( |
| 'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n' |
| 'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.', |
| ); |
| } |
| return true; |
| }()); |
| return _element!; |
| } |
| StatefulElement? _element; |
| |
| /// Whether this [State] object is currently in a tree. |
| /// |
| /// After creating a [State] object and before calling [initState], the |
| /// framework "mounts" the [State] object by associating it with a |
| /// [BuildContext]. The [State] object remains mounted until the framework |
| /// calls [dispose], after which time the framework will never ask the [State] |
| /// object to [build] again. |
| /// |
| /// It is an error to call [setState] unless [mounted] is true. |
| bool get mounted => _element != null; |
| |
| /// Called when this object is inserted into the tree. |
| /// |
| /// The framework will call this method exactly once for each [State] object |
| /// it creates. |
| /// |
| /// Override this method to perform initialization that depends on the |
| /// location at which this object was inserted into the tree (i.e., [context]) |
| /// or on the widget used to configure this object (i.e., [widget]). |
| /// |
| /// {@template flutter.widgets.State.initState} |
| /// If a [State]'s [build] method depends on an object that can itself |
| /// change state, for example a [ChangeNotifier] or [Stream], or some |
| /// other object to which one can subscribe to receive notifications, then |
| /// be sure to subscribe and unsubscribe properly in [initState], |
| /// [didUpdateWidget], and [dispose]: |
| /// |
| /// * In [initState], subscribe to the object. |
| /// * In [didUpdateWidget] unsubscribe from the old object and subscribe |
| /// to the new one if the updated widget configuration requires |
| /// replacing the object. |
| /// * In [dispose], unsubscribe from the object. |
| /// |
| /// {@endtemplate} |
| /// |
| /// You cannot use [BuildContext.dependOnInheritedWidgetOfExactType] from this |
| /// method. However, [didChangeDependencies] will be called immediately |
| /// following this method, and [BuildContext.dependOnInheritedWidgetOfExactType] can |
| /// be used there. |
| /// |
| /// Implementations of this method should start with a call to the inherited |
| /// method, as in `super.initState()`. |
| @protected |
| @mustCallSuper |
| void initState() { |
| assert(_debugLifecycleState == _StateLifecycle.created); |
| if (kFlutterMemoryAllocationsEnabled) { |
| MemoryAllocations.instance.dispatchObjectCreated( |
| library: _flutterWidgetsLibrary, |
| className: '$State', |
| object: this, |
| ); |
| } |
| } |
| |
| /// Called whenever the widget configuration changes. |
| /// |
| /// If the parent widget rebuilds and requests that this location in the tree |
| /// update to display a new widget with the same [runtimeType] and |
| /// [Widget.key], the framework will update the [widget] property of this |
| /// [State] object to refer to the new widget and then call this method |
| /// with the previous widget as an argument. |
| /// |
| /// Override this method to respond when the [widget] changes (e.g., to start |
| /// implicit animations). |
| /// |
| /// The framework always calls [build] after calling [didUpdateWidget], which |
| /// means any calls to [setState] in [didUpdateWidget] are redundant. |
| /// |
| /// {@macro flutter.widgets.State.initState} |
| /// |
| /// Implementations of this method should start with a call to the inherited |
| /// method, as in `super.didUpdateWidget(oldWidget)`. |
| /// |
| /// _See the discussion at [Element.rebuild] for more information on when this |
| /// method is called._ |
| @mustCallSuper |
| @protected |
| void didUpdateWidget(covariant T oldWidget) { } |
| |
| /// {@macro flutter.widgets.Element.reassemble} |
| /// |
| /// In addition to this method being invoked, it is guaranteed that the |
| /// [build] method will be invoked when a reassemble is signaled. Most |
| /// widgets therefore do not need to do anything in the [reassemble] method. |
| /// |
| /// See also: |
| /// |
| /// * [Element.reassemble] |
| /// * [BindingBase.reassembleApplication] |
| /// * [Image], which uses this to reload images. |
| @protected |
| @mustCallSuper |
| void reassemble() { } |
| |
| /// Notify the framework that the internal state of this object has changed. |
| /// |
| /// Whenever you change the internal state of a [State] object, make the |
| /// change in a function that you pass to [setState]: |
| /// |
| /// ```dart |
| /// setState(() { _myState = newValue; }); |
| /// ``` |
| /// |
| /// The provided callback is immediately called synchronously. It must not |
| /// return a future (the callback cannot be `async`), since then it would be |
| /// unclear when the state was actually being set. |
| /// |
| /// Calling [setState] notifies the framework that the internal state of this |
| /// object has changed in a way that might impact the user interface in this |
| /// subtree, which causes the framework to schedule a [build] for this [State] |
| /// object. |
| /// |
| /// If you just change the state directly without calling [setState], the |
| /// framework might not schedule a [build] and the user interface for this |
| /// subtree might not be updated to reflect the new state. |
| /// |
| /// Generally it is recommended that the [setState] method only be used to |
| /// wrap the actual changes to the state, not any computation that might be |
| /// associated with the change. For example, here a value used by the [build] |
| /// function is incremented, and then the change is written to disk, but only |
| /// the increment is wrapped in the [setState]: |
| /// |
| /// ```dart |
| /// Future<void> _incrementCounter() async { |
| /// setState(() { |
| /// _counter++; |
| /// }); |
| /// Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package |
| /// final String dirName = directory.path; |
| /// await File('$dirName/counter.txt').writeAsString('$_counter'); |
| /// } |
| /// ``` |
| /// |
| /// It is an error to call this method after the framework calls [dispose]. |
| /// You can determine whether it is legal to call this method by checking |
| /// whether the [mounted] property is true. |
| @protected |
| void setState(VoidCallback fn) { |
| assert(() { |
| if (_debugLifecycleState == _StateLifecycle.defunct) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('setState() called after dispose(): $this'), |
| ErrorDescription( |
| 'This error happens if you call setState() on a State object for a widget that ' |
| 'no longer appears in the widget tree (e.g., whose parent widget no longer ' |
| 'includes the widget in its build). This error can occur when code calls ' |
| 'setState() from a timer or an animation callback.', |
| ), |
| ErrorHint( |
| 'The preferred solution is ' |
| 'to cancel the timer or stop listening to the animation in the dispose() ' |
| 'callback. Another solution is to check the "mounted" property of this ' |
| 'object before calling setState() to ensure the object is still in the ' |
| 'tree.', |
| ), |
| ErrorHint( |
| 'This error might indicate a memory leak if setState() is being called ' |
| 'because another object is retaining a reference to this State object ' |
| 'after it has been removed from the tree. To avoid memory leaks, ' |
| 'consider breaking the reference to this object during dispose().', |
| ), |
| ]); |
| } |
| if (_debugLifecycleState == _StateLifecycle.created && !mounted) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('setState() called in constructor: $this'), |
| ErrorHint( |
| 'This happens when you call setState() on a State object for a widget that ' |
| "hasn't been inserted into the widget tree yet. It is not necessary to call " |
| 'setState() in the constructor, since the state is already assumed to be dirty ' |
| 'when it is initially created.', |
| ), |
| ]); |
| } |
| return true; |
| }()); |
| final Object? result = fn() as dynamic; |
| assert(() { |
| if (result is Future) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('setState() callback argument returned a Future.'), |
| ErrorDescription( |
| 'The setState() method on $this was called with a closure or method that ' |
| 'returned a Future. Maybe it is marked as "async".', |
| ), |
| ErrorHint( |
| 'Instead of performing asynchronous work inside a call to setState(), first ' |
| 'execute the work (without updating the widget state), and then synchronously ' |
| 'update the state inside a call to setState().', |
| ), |
| ]); |
| } |
| // We ignore other types of return values so that you can do things like: |
| // setState(() => x = 3); |
| return true; |
| }()); |
| _element!.markNeedsBuild(); |
| } |
| |
| /// Called when this object is removed from the tree. |
| /// |
| /// The framework calls this method whenever it removes this [State] object |
| /// from the tree. In some cases, the framework will reinsert the [State] |
| /// object into another part of the tree (e.g., if the subtree containing this |
| /// [State] object is grafted from one location in the tree to another due to |
| /// the use of a [GlobalKey]). If that happens, the framework will call |
| /// [activate] to give the [State] object a chance to reacquire any resources |
| /// that it released in [deactivate]. It will then also call [build] to give |
| /// the [State] object a chance to adapt to its new location in the tree. If |
| /// the framework does reinsert this subtree, it will do so before the end of |
| /// the animation frame in which the subtree was removed from the tree. For |
| /// this reason, [State] objects can defer releasing most resources until the |
| /// framework calls their [dispose] method. |
| /// |
| /// Subclasses should override this method to clean up any links between |
| /// this object and other elements in the tree (e.g. if you have provided an |
| /// ancestor with a pointer to a descendant's [RenderObject]). |
| /// |
| /// Implementations of this method should end with a call to the inherited |
| /// method, as in `super.deactivate()`. |
| /// |
| /// See also: |
| /// |
| /// * [dispose], which is called after [deactivate] if the widget is removed |
| /// from the tree permanently. |
| @protected |
| @mustCallSuper |
| void deactivate() { } |
| |
| /// Called when this object is reinserted into the tree after having been |
| /// removed via [deactivate]. |
| /// |
| /// In most cases, after a [State] object has been deactivated, it is _not_ |
| /// reinserted into the tree, and its [dispose] method will be called to |
| /// signal that it is ready to be garbage collected. |
| /// |
| /// In some cases, however, after a [State] object has been deactivated, the |
| /// framework will reinsert it into another part of the tree (e.g., if the |
| /// subtree containing this [State] object is grafted from one location in |
| /// the tree to another due to the use of a [GlobalKey]). If that happens, |
| /// the framework will call [activate] to give the [State] object a chance to |
| /// reacquire any resources that it released in [deactivate]. It will then |
| /// also call [build] to give the object a chance to adapt to its new |
| /// location in the tree. If the framework does reinsert this subtree, it |
| /// will do so before the end of the animation frame in which the subtree was |
| /// removed from the tree. For this reason, [State] objects can defer |
| /// releasing most resources until the framework calls their [dispose] method. |
| /// |
| /// The framework does not call this method the first time a [State] object |
| /// is inserted into the tree. Instead, the framework calls [initState] in |
| /// that situation. |
| /// |
| /// Implementations of this method should start with a call to the inherited |
| /// method, as in `super.activate()`. |
| /// |
| /// See also: |
| /// |
| /// * [Element.activate], the corresponding method when an element |
| /// transitions from the "inactive" to the "active" lifecycle state. |
| @protected |
| @mustCallSuper |
| void activate() { } |
| |
| /// Called when this object is removed from the tree permanently. |
| /// |
| /// The framework calls this method when this [State] object will never |
| /// build again. After the framework calls [dispose], the [State] object is |
| /// considered unmounted and the [mounted] property is false. It is an error |
| /// to call [setState] at this point. This stage of the lifecycle is terminal: |
| /// there is no way to remount a [State] object that has been disposed. |
| /// |
| /// Subclasses should override this method to release any resources retained |
| /// by this object (e.g., stop any active animations). |
| /// |
| /// {@macro flutter.widgets.State.initState} |
| /// |
| /// Implementations of this method should end with a call to the inherited |
| /// method, as in `super.dispose()`. |
| /// |
| /// See also: |
| /// |
| /// * [deactivate], which is called prior to [dispose]. |
| @protected |
| @mustCallSuper |
| void dispose() { |
| assert(_debugLifecycleState == _StateLifecycle.ready); |
| assert(() { |
| _debugLifecycleState = _StateLifecycle.defunct; |
| return true; |
| }()); |
| if (kFlutterMemoryAllocationsEnabled) { |
| MemoryAllocations.instance.dispatchObjectDisposed(object: this); |
| } |
| } |
| |
| /// Describes the part of the user interface represented by this widget. |
| /// |
| /// The framework calls this method in a number of different situations. For |
| /// example: |
| /// |
| /// * After calling [initState]. |
| /// * After calling [didUpdateWidget]. |
| /// * After receiving a call to [setState]. |
| /// * After a dependency of this [State] object changes (e.g., an |
| /// [InheritedWidget] referenced by the previous [build] changes). |
| /// * After calling [deactivate] and then reinserting the [State] object into |
| /// the tree at another location. |
| /// |
| /// This method can potentially be called in every frame and should not have |
| /// any side effects beyond building a widget. |
| /// |
| /// The framework replaces the subtree below this widget with the widget |
| /// returned by this method, either by updating the existing subtree or by |
| /// removing the subtree and inflating a new subtree, depending on whether the |
| /// widget returned by this method can update the root of the existing |
| /// subtree, as determined by calling [Widget.canUpdate]. |
| /// |
| /// Typically implementations return a newly created constellation of widgets |
| /// that are configured with information from this widget's constructor, the |
| /// given [BuildContext], and the internal state of this [State] object. |
| /// |
| /// The given [BuildContext] contains information about the location in the |
| /// tree at which this widget is being built. For example, the context |
| /// provides the set of inherited widgets for this location in the tree. The |
| /// [BuildContext] argument is always the same as the [context] property of |
| /// this [State] object and will remain the same for the lifetime of this |
| /// object. The [BuildContext] argument is provided redundantly here so that |
| /// this method matches the signature for a [WidgetBuilder]. |
| /// |
| /// ## Design discussion |
| /// |
| /// ### Why is the [build] method on [State], and not [StatefulWidget]? |
| /// |
| /// Putting a `Widget build(BuildContext context)` method on [State] rather |
| /// than putting a `Widget build(BuildContext context, State state)` method |
| /// on [StatefulWidget] gives developers more flexibility when subclassing |
| /// [StatefulWidget]. |
| /// |
| /// For example, [AnimatedWidget] is a subclass of [StatefulWidget] that |
| /// introduces an abstract `Widget build(BuildContext context)` method for its |
| /// subclasses to implement. If [StatefulWidget] already had a [build] method |
| /// that took a [State] argument, [AnimatedWidget] would be forced to provide |
| /// its [State] object to subclasses even though its [State] object is an |
| /// internal implementation detail of [AnimatedWidget]. |
| /// |
| /// Conceptually, [StatelessWidget] could also be implemented as a subclass of |
| /// [StatefulWidget] in a similar manner. If the [build] method were on |
| /// [StatefulWidget] rather than [State], that would not be possible anymore. |
| /// |
| /// Putting the [build] function on [State] rather than [StatefulWidget] also |
| /// helps avoid a category of bugs related to closures implicitly capturing |
| /// `this`. If you defined a closure in a [build] function on a |
| /// [StatefulWidget], that closure would implicitly capture `this`, which is |
| /// the current widget instance, and would have the (immutable) fields of that |
| /// instance in scope: |
| /// |
| /// ```dart |
| /// // (this is not valid Flutter code) |
| /// class MyButton extends StatefulWidgetX { |
| /// MyButton({super.key, required this.color}); |
| /// |
| /// final Color color; |
| /// |
| /// @override |
| /// Widget build(BuildContext context, State state) { |
| /// return SpecialWidget( |
| /// handler: () { print('color: $color'); }, |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// For example, suppose the parent builds `MyButton` with `color` being blue, |
| /// the `$color` in the print function refers to blue, as expected. Now, |
| /// suppose the parent rebuilds `MyButton` with green. The closure created by |
| /// the first build still implicitly refers to the original widget and the |
| /// `$color` still prints blue even through the widget has been updated to |
| /// green; should that closure outlive its widget, it would print outdated |
| /// information. |
| /// |
| /// In contrast, with the [build] function on the [State] object, closures |
| /// created during [build] implicitly capture the [State] instance instead of |
| /// the widget instance: |
| /// |
| /// ```dart |
| /// class MyButton extends StatefulWidget { |
| /// const MyButton({super.key, this.color = Colors.teal}); |
| /// |
| /// final Color color; |
| /// // ... |
| /// } |
| /// |
| /// class MyButtonState extends State<MyButton> { |
| /// // ... |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return SpecialWidget( |
| /// handler: () { print('color: ${widget.color}'); }, |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// Now when the parent rebuilds `MyButton` with green, the closure created by |
| /// the first build still refers to [State] object, which is preserved across |
| /// rebuilds, but the framework has updated that [State] object's [widget] |
| /// property to refer to the new `MyButton` instance and `${widget.color}` |
| /// prints green, as expected. |
| /// |
| /// See also: |
| /// |
| /// * [StatefulWidget], which contains the discussion on performance considerations. |
| @protected |
| Widget build(BuildContext context); |
| |
| /// Called when a dependency of this [State] object changes. |
| /// |
| /// For example, if the previous call to [build] referenced an |
| /// [InheritedWidget] that later changed, the framework would call this |
| /// method to notify this object about the change. |
| /// |
| /// This method is also called immediately after [initState]. It is safe to |
| /// call [BuildContext.dependOnInheritedWidgetOfExactType] from this method. |
| /// |
| /// Subclasses rarely override this method because the framework always |
| /// calls [build] after a dependency changes. Some subclasses do override |
| /// this method because they need to do some expensive work (e.g., network |
| /// fetches) when their dependencies change, and that work would be too |
| /// expensive to do for every build. |
| @protected |
| @mustCallSuper |
| void didChangeDependencies() { } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| assert(() { |
| properties.add(EnumProperty<_StateLifecycle>('lifecycle state', _debugLifecycleState, defaultValue: _StateLifecycle.ready)); |
| return true; |
| }()); |
| properties.add(ObjectFlagProperty<T>('_widget', _widget, ifNull: 'no widget')); |
| properties.add(ObjectFlagProperty<StatefulElement>('_element', _element, ifNull: 'not mounted')); |
| } |
| } |
| |
| /// A widget that has a child widget provided to it, instead of building a new |
| /// widget. |
| /// |
| /// Useful as a base class for other widgets, such as [InheritedWidget] and |
| /// [ParentDataWidget]. |
| /// |
| /// See also: |
| /// |
| /// * [InheritedWidget], for widgets that introduce ambient state that can |
| /// be read by descendant widgets. |
| /// * [ParentDataWidget], for widgets that populate the |
| /// [RenderObject.parentData] slot of their child's [RenderObject] to |
| /// configure the parent widget's layout. |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| /// * [StatelessWidget], for widgets that always build the same way given a |
| /// particular configuration and ambient state. |
| /// * [Widget], for an overview of widgets in general. |
| abstract class ProxyWidget extends Widget { |
| /// Creates a widget that has exactly one child widget. |
| const ProxyWidget({ super.key, required this.child }); |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@template flutter.widgets.ProxyWidget.child} |
| /// This widget can only have one child. To lay out multiple children, let this |
| /// widget's child be a widget such as [Row], [Column], or [Stack], which have a |
| /// `children` property, and then provide the children to that widget. |
| /// {@endtemplate} |
| final Widget child; |
| } |
| |
| /// Base class for widgets that hook [ParentData] information to children of |
| /// [RenderObjectWidget]s. |
| /// |
| /// This can be used to provide per-child configuration for |
| /// [RenderObjectWidget]s with more than one child. For example, [Stack] uses |
| /// the [Positioned] parent data widget to position each child. |
| /// |
| /// A [ParentDataWidget] is specific to a particular kind of [ParentData]. That |
| /// class is `T`, the [ParentData] type argument. |
| /// |
| /// {@tool snippet} |
| /// |
| /// This example shows how you would build a [ParentDataWidget] to configure a |
| /// `FrogJar` widget's children by specifying a [Size] for each one. |
| /// |
| /// ```dart |
| /// class FrogSize extends ParentDataWidget<FrogJarParentData> { |
| /// const FrogSize({ |
| /// super.key, |
| /// required this.size, |
| /// required super.child, |
| /// }); |
| /// |
| /// final Size size; |
| /// |
| /// @override |
| /// void applyParentData(RenderObject renderObject) { |
| /// final FrogJarParentData parentData = renderObject.parentData! as FrogJarParentData; |
| /// if (parentData.size != size) { |
| /// parentData.size = size; |
| /// final RenderFrogJar targetParent = renderObject.parent! as RenderFrogJar; |
| /// targetParent.markNeedsLayout(); |
| /// } |
| /// } |
| /// |
| /// @override |
| /// Type get debugTypicalAncestorWidgetClass => FrogJar; |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// See also: |
| /// |
| /// * [RenderObject], the superclass for layout algorithms. |
| /// * [RenderObject.parentData], the slot that this class configures. |
| /// * [ParentData], the superclass of the data that will be placed in |
| /// [RenderObject.parentData] slots. The `T` type parameter for |
| /// [ParentDataWidget] is a [ParentData]. |
| /// * [RenderObjectWidget], the class for widgets that wrap [RenderObject]s. |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| abstract class ParentDataWidget<T extends ParentData> extends ProxyWidget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const ParentDataWidget({ super.key, required super.child }); |
| |
| @override |
| ParentDataElement<T> createElement() => ParentDataElement<T>(this); |
| |
| /// Checks if this widget can apply its parent data to the provided |
| /// `renderObject`. |
| /// |
| /// The [RenderObject.parentData] of the provided `renderObject` is |
| /// typically set up by an ancestor [RenderObjectWidget] of the type returned |
| /// by [debugTypicalAncestorWidgetClass]. |
| /// |
| /// This is called just before [applyParentData] is invoked with the same |
| /// [RenderObject] provided to that method. |
| bool debugIsValidRenderObject(RenderObject renderObject) { |
| assert(T != dynamic); |
| assert(T != ParentData); |
| return renderObject.parentData is T; |
| } |
| |
| /// The [RenderObjectWidget] that is typically used to set up the [ParentData] |
| /// that [applyParentData] will write to. |
| /// |
| /// This is only used in error messages to tell users what widget typically |
| /// wraps this ParentDataWidget. |
| Type get debugTypicalAncestorWidgetClass; |
| |
| Iterable<DiagnosticsNode> _debugDescribeIncorrectParentDataType({ |
| required ParentData? parentData, |
| RenderObjectWidget? parentDataCreator, |
| DiagnosticsNode? ownershipChain, |
| }) { |
| assert(T != dynamic); |
| assert(T != ParentData); |
| |
| final String description = 'The ParentDataWidget $this wants to apply ParentData of type $T to a RenderObject'; |
| return <DiagnosticsNode>[ |
| if (parentData == null) |
| ErrorDescription( |
| '$description, which has not been set up to receive any ParentData.', |
| ) |
| else |
| ErrorDescription( |
| '$description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.', |
| ), |
| ErrorHint( |
| 'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. ' |
| 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetClass widgets.', |
| ), |
| if (parentDataCreator != null) |
| ErrorHint( |
| 'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.', |
| ), |
| if (ownershipChain != null) |
| ErrorDescription( |
| 'The ownership chain for the RenderObject that received the incompatible parent data was:\n $ownershipChain', |
| ), |
| ]; |
| } |
| |
| /// Write the data from this widget into the given render object's parent data. |
| /// |
| /// The framework calls this function whenever it detects that the |
| /// [RenderObject] associated with the [child] has outdated |
| /// [RenderObject.parentData]. For example, if the render object was recently |
| /// inserted into the render tree, the render object's parent data might not |
| /// match the data in this widget. |
| /// |
| /// Subclasses are expected to override this function to copy data from their |
| /// fields into the [RenderObject.parentData] field of the given render |
| /// object. The render object's parent is guaranteed to have been created by a |
| /// widget of type `T`, which usually means that this function can assume that |
| /// the render object's parent data object inherits from a particular class. |
| /// |
| /// If this function modifies data that can change the parent's layout or |
| /// painting, this function is responsible for calling |
| /// [RenderObject.markNeedsLayout] or [RenderObject.markNeedsPaint] on the |
| /// parent, as appropriate. |
| @protected |
| void applyParentData(RenderObject renderObject); |
| |
| /// Whether the [ParentDataElement.applyWidgetOutOfTurn] method is allowed |
| /// with this widget. |
| /// |
| /// This should only return true if this widget represents a [ParentData] |
| /// configuration that will have no impact on the layout or paint phase. |
| /// |
| /// See also: |
| /// |
| /// * [ParentDataElement.applyWidgetOutOfTurn], which verifies this in debug |
| /// mode. |
| @protected |
| bool debugCanApplyOutOfTurn() => false; |
| } |
| |
| /// Base class for widgets that efficiently propagate information down the tree. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=og-vJqLzg2c} |
| /// |
| /// To obtain the nearest instance of a particular type of inherited widget from |
| /// a build context, use [BuildContext.dependOnInheritedWidgetOfExactType]. |
| /// |
| /// Inherited widgets, when referenced in this way, will cause the consumer to |
| /// rebuild when the inherited widget itself changes state. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=Zbm3hjPjQMk} |
| /// |
| /// {@tool snippet} |
| /// |
| /// The following is a skeleton of an inherited widget called `FrogColor`: |
| /// |
| /// ```dart |
| /// class FrogColor extends InheritedWidget { |
| /// const FrogColor({ |
| /// super.key, |
| /// required this.color, |
| /// required super.child, |
| /// }); |
| /// |
| /// final Color color; |
| /// |
| /// static FrogColor? maybeOf(BuildContext context) { |
| /// return context.dependOnInheritedWidgetOfExactType<FrogColor>(); |
| /// } |
| /// |
| /// static FrogColor of(BuildContext context) { |
| /// final FrogColor? result = maybeOf(context); |
| /// assert(result != null, 'No FrogColor found in context'); |
| /// return result!; |
| /// } |
| /// |
| /// @override |
| /// bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color; |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// ## Implementing the `of` and `maybeOf` methods |
| /// |
| /// The convention is to provide two static methods, `of` and `maybeOf`, on the |
| /// [InheritedWidget] which call |
| /// [BuildContext.dependOnInheritedWidgetOfExactType]. This allows the class to |
| /// define its own fallback logic in case there isn't a widget in scope. |
| /// |
| /// The `of` method typically returns a non-nullable instance and asserts if the |
| /// [InheritedWidget] isn't found, and the `maybeOf` method returns a nullable |
| /// instance, and returns null if the [InheritedWidget] isn't found. The `of` |
| /// method is typically implemented by calling `maybeOf` internally. |
| /// |
| /// Sometimes, the `of` and `maybeOf` methods return some data rather than the |
| /// inherited widget itself; for example, in this case it could have returned a |
| /// [Color] instead of the `FrogColor` widget. |
| /// |
| /// Occasionally, the inherited widget is an implementation detail of another |
| /// class, and is therefore private. The `of` and `maybeOf` methods in that case |
| /// are typically implemented on the public class instead. For example, [Theme] |
| /// is implemented as a [StatelessWidget] that builds a private inherited |
| /// widget; [Theme.of] looks for that private inherited widget using |
| /// [BuildContext.dependOnInheritedWidgetOfExactType] and then returns the |
| /// [ThemeData] inside it. |
| /// |
| /// ## Calling the `of` or `maybeOf` methods |
| /// |
| /// When using the `of` or `maybeOf` methods, the `context` must be a descendant |
| /// of the [InheritedWidget], meaning it must be "below" the [InheritedWidget] |
| /// in the tree. |
| /// |
| /// {@tool snippet} |
| /// |
| /// In this example, the `context` used is the one from the [Builder], which is |
| /// a child of the `FrogColor` widget, so this works. |
| /// |
| /// ```dart |
| /// // continuing from previous example... |
| /// class MyPage extends StatelessWidget { |
| /// const MyPage({super.key}); |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return Scaffold( |
| /// body: FrogColor( |
| /// color: Colors.green, |
| /// child: Builder( |
| /// builder: (BuildContext innerContext) { |
| /// return Text( |
| /// 'Hello Frog', |
| /// style: TextStyle(color: FrogColor.of(innerContext).color), |
| /// ); |
| /// }, |
| /// ), |
| /// ), |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} |
| /// |
| /// {@tool snippet} |
| /// |
| /// In this example, the `context` used is the one from the `MyOtherPage` |
| /// widget, which is a parent of the `FrogColor` widget, so this does not work, |
| /// and will assert when `FrogColor.of` is called. |
| /// |
| /// ```dart |
| /// // continuing from previous example... |
| /// |
| /// class MyOtherPage extends StatelessWidget { |
| /// const MyOtherPage({super.key}); |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return Scaffold( |
| /// body: FrogColor( |
| /// color: Colors.green, |
| /// child: Text( |
| /// 'Hello Frog', |
| /// style: TextStyle(color: FrogColor.of(context).color), |
| /// ), |
| /// ), |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// {@end-tool} {@youtube 560 315 https://www.youtube.com/watch?v=1t-8rBCGBYw} |
| /// |
| /// See also: |
| /// |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| /// * [StatelessWidget], for widgets that always build the same way given a |
| /// particular configuration and ambient state. |
| /// * [Widget], for an overview of widgets in general. |
| /// * [InheritedNotifier], an inherited widget whose value can be a |
| /// [Listenable], and which will notify dependents whenever the value sends |
| /// notifications. |
| /// * [InheritedModel], an inherited widget that allows clients to subscribe to |
| /// changes for subparts of the value. |
| abstract class InheritedWidget extends ProxyWidget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const InheritedWidget({ super.key, required super.child }); |
| |
| @override |
| InheritedElement createElement() => InheritedElement(this); |
| |
| /// Whether the framework should notify widgets that inherit from this widget. |
| /// |
| /// When this widget is rebuilt, sometimes we need to rebuild the widgets that |
| /// inherit from this widget but sometimes we do not. For example, if the data |
| /// held by this widget is the same as the data held by `oldWidget`, then we |
| /// do not need to rebuild the widgets that inherited the data held by |
| /// `oldWidget`. |
| /// |
| /// The framework distinguishes these cases by calling this function with the |
| /// widget that previously occupied this location in the tree as an argument. |
| /// The given widget is guaranteed to have the same [runtimeType] as this |
| /// object. |
| @protected |
| bool updateShouldNotify(covariant InheritedWidget oldWidget); |
| } |
| |
| /// RenderObjectWidgets provide the configuration for [RenderObjectElement]s, |
| /// which wrap [RenderObject]s, which provide the actual rendering of the |
| /// application. |
| /// |
| /// See also: |
| /// |
| /// * [MultiChildRenderObjectWidget], which configures a [RenderObject] with |
| /// a single list of children. |
| /// * [SlottedMultiChildRenderObjectWidget], which configures a |
| /// [RenderObject] that organizes its children in different named slots. |
| abstract class RenderObjectWidget extends Widget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const RenderObjectWidget({ super.key }); |
| |
| /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass. |
| @override |
| @factory |
| RenderObjectElement createElement(); |
| |
| /// Creates an instance of the [RenderObject] class that this |
| /// [RenderObjectWidget] represents, using the configuration described by this |
| /// [RenderObjectWidget]. |
| /// |
| /// This method should not do anything with the children of the render object. |
| /// That should instead be handled by the method that overrides |
| /// [RenderObjectElement.mount] in the object rendered by this object's |
| /// [createElement] method. See, for example, |
| /// [SingleChildRenderObjectElement.mount]. |
| @protected |
| @factory |
| RenderObject createRenderObject(BuildContext context); |
| |
| /// Copies the configuration described by this [RenderObjectWidget] to the |
| /// given [RenderObject], which will be of the same type as returned by this |
| /// object's [createRenderObject]. |
| /// |
| /// This method should not do anything to update the children of the render |
| /// object. That should instead be handled by the method that overrides |
| /// [RenderObjectElement.update] in the object rendered by this object's |
| /// [createElement] method. See, for example, |
| /// [SingleChildRenderObjectElement.update]. |
| @protected |
| void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { } |
| |
| /// A render object previously associated with this widget has been removed |
| /// from the tree. The given [RenderObject] will be of the same type as |
| /// returned by this object's [createRenderObject]. |
| @protected |
| void didUnmountRenderObject(covariant RenderObject renderObject) { } |
| } |
| |
| /// A superclass for RenderObjectWidgets that configure RenderObject subclasses |
| /// that have no children. |
| abstract class LeafRenderObjectWidget extends RenderObjectWidget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const LeafRenderObjectWidget({ super.key }); |
| |
| @override |
| LeafRenderObjectElement createElement() => LeafRenderObjectElement(this); |
| } |
| |
| /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses |
| /// that have a single child slot. (This superclass only provides the storage |
| /// for that child, it doesn't actually provide the updating logic.) |
| /// |
| /// Typically, the render object assigned to this widget will make use of |
| /// [RenderObjectWithChildMixin] to implement a single-child model. The mixin |
| /// exposes a [RenderObjectWithChildMixin.child] property that allows |
| /// retrieving the render object belonging to the [child] widget. |
| abstract class SingleChildRenderObjectWidget extends RenderObjectWidget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const SingleChildRenderObjectWidget({ super.key, this.child }); |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@macro flutter.widgets.ProxyWidget.child} |
| final Widget? child; |
| |
| @override |
| SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this); |
| } |
| |
| /// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses |
| /// that have a single list of children. (This superclass only provides the |
| /// storage for that child list, it doesn't actually provide the updating |
| /// logic.) |
| /// |
| /// Subclasses must return a [RenderObject] that mixes in |
| /// [ContainerRenderObjectMixin], which provides the necessary functionality to |
| /// visit the children of the container render object (the render object |
| /// belonging to the [children] widgets). Typically, subclasses will return a |
| /// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and |
| /// [RenderBoxContainerDefaultsMixin]. |
| /// |
| /// See also: |
| /// |
| /// * [Stack], which uses [MultiChildRenderObjectWidget]. |
| /// * [RenderStack], for an example implementation of the associated render |
| /// object. |
| /// * [SlottedMultiChildRenderObjectWidget], which configures a |
| /// [RenderObject] that instead of having a single list of children organizes |
| /// its children in named slots. |
| abstract class MultiChildRenderObjectWidget extends RenderObjectWidget { |
| /// Initializes fields for subclasses. |
| /// |
| /// The [children] argument must not be null and must not contain any null |
| /// objects. |
| const MultiChildRenderObjectWidget({ super.key, this.children = const <Widget>[] }); |
| |
| /// The widgets below this widget in the tree. |
| /// |
| /// If this list is going to be mutated, it is usually wise to put a [Key] on |
| /// each of the child widgets, so that the framework can match old |
| /// configurations to new configurations and maintain the underlying render |
| /// objects. |
| /// |
| /// Also, a [Widget] in Flutter is immutable, so directly modifying the |
| /// [children] such as `someMultiChildRenderObjectWidget.children.add(...)` or |
| /// as the example code below will result in incorrect behaviors. Whenever the |
| /// children list is modified, a new list object should be provided. |
| /// |
| /// ```dart |
| /// // This code is incorrect. |
| /// class SomeWidgetState extends State<SomeWidget> { |
| /// final List<Widget> _children = <Widget>[]; |
| /// |
| /// void someHandler() { |
| /// setState(() { |
| /// _children.add(const ChildWidget()); |
| /// }); |
| /// } |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// // Reusing `List<Widget> _children` here is problematic. |
| /// return Row(children: _children); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// The following code corrects the problem mentioned above. |
| /// |
| /// ```dart |
| /// class SomeWidgetState extends State<SomeWidget> { |
| /// final List<Widget> _children = <Widget>[]; |
| /// |
| /// void someHandler() { |
| /// setState(() { |
| /// // The key here allows Flutter to reuse the underlying render |
| /// // objects even if the children list is recreated. |
| /// _children.add(ChildWidget(key: UniqueKey())); |
| /// }); |
| /// } |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// // Always create a new list of children as a Widget is immutable. |
| /// return Row(children: _children.toList()); |
| /// } |
| /// } |
| /// ``` |
| final List<Widget> children; |
| |
| @override |
| MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this); |
| } |
| |
| |
| // ELEMENTS |
| |
| enum _ElementLifecycle { |
| initial, |
| active, |
| inactive, |
| defunct, |
| } |
| |
| class _InactiveElements { |
| bool _locked = false; |
| final Set<Element> _elements = HashSet<Element>(); |
| |
| void _unmount(Element element) { |
| assert(element._lifecycleState == _ElementLifecycle.inactive); |
| assert(() { |
| if (debugPrintGlobalKeyedWidgetLifecycle) { |
| if (element.widget.key is GlobalKey) { |
| debugPrint('Discarding $element from inactive elements list.'); |
| } |
| } |
| return true; |
| }()); |
| element.visitChildren((Element child) { |
| assert(child._parent == element); |
| _unmount(child); |
| }); |
| element.unmount(); |
| assert(element._lifecycleState == _ElementLifecycle.defunct); |
| } |
| |
| void _unmountAll() { |
| _locked = true; |
| final List<Element> elements = _elements.toList()..sort(Element._sort); |
| _elements.clear(); |
| try { |
| elements.reversed.forEach(_unmount); |
| } finally { |
| assert(_elements.isEmpty); |
| _locked = false; |
| } |
| } |
| |
| static void _deactivateRecursively(Element element) { |
| assert(element._lifecycleState == _ElementLifecycle.active); |
| element.deactivate(); |
| assert(element._lifecycleState == _ElementLifecycle.inactive); |
| element.visitChildren(_deactivateRecursively); |
| assert(() { |
| element.debugDeactivated(); |
| return true; |
| }()); |
| } |
| |
| void add(Element element) { |
| assert(!_locked); |
| assert(!_elements.contains(element)); |
| assert(element._parent == null); |
| if (element._lifecycleState == _ElementLifecycle.active) { |
| _deactivateRecursively(element); |
| } |
| _elements.add(element); |
| } |
| |
| void remove(Element element) { |
| assert(!_locked); |
| assert(_elements.contains(element)); |
| assert(element._parent == null); |
| _elements.remove(element); |
| assert(element._lifecycleState != _ElementLifecycle.active); |
| } |
| |
| bool debugContains(Element element) { |
| late bool result; |
| assert(() { |
| result = _elements.contains(element); |
| return true; |
| }()); |
| return result; |
| } |
| } |
| |
| /// Signature for the callback to [BuildContext.visitChildElements]. |
| /// |
| /// The argument is the child being visited. |
| /// |
| /// It is safe to call `element.visitChildElements` reentrantly within |
| /// this callback. |
| typedef ElementVisitor = void Function(Element element); |
| |
| /// Signature for the callback to [BuildContext.visitAncestorElements]. |
| /// |
| /// The argument is the ancestor being visited. |
| /// |
| /// Return false to stop the walk. |
| typedef ConditionalElementVisitor = bool Function(Element element); |
| |
| /// A handle to the location of a widget in the widget tree. |
| /// |
| /// This class presents a set of methods that can be used from |
| /// [StatelessWidget.build] methods and from methods on [State] objects. |
| /// |
| /// [BuildContext] objects are passed to [WidgetBuilder] functions (such as |
| /// [StatelessWidget.build]), and are available from the [State.context] member. |
| /// Some static functions (e.g. [showDialog], [Theme.of], and so forth) also |
| /// take build contexts so that they can act on behalf of the calling widget, or |
| /// obtain data specifically for the given context. |
| /// |
| /// Each widget has its own [BuildContext], which becomes the parent of the |
| /// widget returned by the [StatelessWidget.build] or [State.build] function. |
| /// (And similarly, the parent of any children for [RenderObjectWidget]s.) |
| /// |
| /// In particular, this means that within a build method, the build context of |
| /// the widget of the build method is not the same as the build context of the |
| /// widgets returned by that build method. This can lead to some tricky cases. |
| /// For example, [Theme.of(context)] looks for the nearest enclosing [Theme] of |
| /// the given build context. If a build method for a widget Q includes a [Theme] |
| /// within its returned widget tree, and attempts to use [Theme.of] passing its |
| /// own context, the build method for Q will not find that [Theme] object. It |
| /// will instead find whatever [Theme] was an ancestor to the widget Q. If the |
| /// build context for a subpart of the returned tree is needed, a [Builder] |
| /// widget can be used: the build context passed to the [Builder.builder] |
| /// callback will be that of the [Builder] itself. |
| /// |
| /// For example, in the following snippet, the [ScaffoldState.showBottomSheet] |
| /// method is called on the [Scaffold] widget that the build method itself |
| /// creates. If a [Builder] had not been used, and instead the `context` |
| /// argument of the build method itself had been used, no [Scaffold] would have |
| /// been found, and the [Scaffold.of] function would have returned null. |
| /// |
| /// ```dart |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// // here, Scaffold.of(context) returns null |
| /// return Scaffold( |
| /// appBar: AppBar(title: const Text('Demo')), |
| /// body: Builder( |
| /// builder: (BuildContext context) { |
| /// return TextButton( |
| /// child: const Text('BUTTON'), |
| /// onPressed: () { |
| /// Scaffold.of(context).showBottomSheet<void>( |
| /// (BuildContext context) { |
| /// return Container( |
| /// alignment: Alignment.center, |
| /// height: 200, |
| /// color: Colors.amber, |
| /// child: Center( |
| /// child: Column( |
| /// mainAxisSize: MainAxisSize.min, |
| /// children: <Widget>[ |
| /// const Text('BottomSheet'), |
| /// ElevatedButton( |
| /// child: const Text('Close BottomSheet'), |
| /// onPressed: () { |
| /// Navigator.pop(context); |
| /// }, |
| /// ) |
| /// ], |
| /// ), |
| /// ), |
| /// ); |
| /// }, |
| /// ); |
| /// }, |
| /// ); |
| /// }, |
| /// ) |
| /// ); |
| /// } |
| /// ``` |
| /// |
| /// The [BuildContext] for a particular widget can change location over time as |
| /// the widget is moved around the tree. Because of this, values returned from |
| /// the methods on this class should not be cached beyond the execution of a |
| /// single synchronous function. |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g} |
| /// |
| /// Avoid storing instances of [BuildContext]s because they may become invalid |
| /// if the widget they are associated with is unmounted from the widget tree. |
| /// {@template flutter.widgets.BuildContext.asynchronous_gap} |
| /// If a [BuildContext] is used across an asynchronous gap (i.e. after performing |
| /// an asynchronous operation), consider checking [mounted] to determine whether |
| /// the context is still valid before interacting with it: |
| /// |
| /// ```dart |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return OutlinedButton( |
| /// onPressed: () async { |
| /// await Future<void>.delayed(const Duration(seconds: 1)); |
| /// if (context.mounted) { |
| /// Navigator.of(context).pop(); |
| /// } |
| /// }, |
| /// child: const Text('Delayed pop'), |
| /// ); |
| /// } |
| /// ``` |
| /// {@endtemplate} |
| /// |
| /// [BuildContext] objects are actually [Element] objects. The [BuildContext] |
| /// interface is used to discourage direct manipulation of [Element] objects. |
| abstract class BuildContext { |
| /// The current configuration of the [Element] that is this [BuildContext]. |
| Widget get widget; |
| |
| /// The [BuildOwner] for this context. The [BuildOwner] is in charge of |
| /// managing the rendering pipeline for this context. |
| BuildOwner? get owner; |
| |
| /// Whether the [Widget] this context is associated with is currently |
| /// mounted in the widget tree. |
| /// |
| /// Accessing the properties of the [BuildContext] or calling any methods on |
| /// it is only valid while mounted is true. If mounted is false, assertions |
| /// will trigger. |
| /// |
| /// Once unmounted, a given [BuildContext] will never become mounted again. |
| /// |
| /// {@macro flutter.widgets.BuildContext.asynchronous_gap} |
| bool get mounted; |
| |
| /// Whether the [widget] is currently updating the widget or render tree. |
| /// |
| /// For [StatefulWidget]s and [StatelessWidget]s this flag is true while |
| /// their respective build methods are executing. |
| /// [RenderObjectWidget]s set this to true while creating or configuring their |
| /// associated [RenderObject]s. |
| /// Other [Widget] types may set this to true for conceptually similar phases |
| /// of their lifecycle. |
| /// |
| /// When this is true, it is safe for [widget] to establish a dependency to an |
| /// [InheritedWidget] by calling [dependOnInheritedElement] or |
| /// [dependOnInheritedWidgetOfExactType]. |
| /// |
| /// Accessing this flag in release mode is not valid. |
| bool get debugDoingBuild; |
| |
| /// The current [RenderObject] for the widget. If the widget is a |
| /// [RenderObjectWidget], this is the render object that the widget created |
| /// for itself. Otherwise, it is the render object of the first descendant |
| /// [RenderObjectWidget]. |
| /// |
| /// This method will only return a valid result after the build phase is |
| /// complete. It is therefore not valid to call this from a build method. |
| /// It should only be called from interaction event handlers (e.g. |
| /// gesture callbacks) or layout or paint callbacks. It is also not valid to |
| /// call if [State.mounted] returns false. |
| /// |
| /// If the render object is a [RenderBox], which is the common case, then the |
| /// size of the render object can be obtained from the [size] getter. This is |
| /// only valid after the layout phase, and should therefore only be examined |
| /// from paint callbacks or interaction event handlers (e.g. gesture |
| /// callbacks). |
| /// |
| /// For details on the different phases of a frame, see the discussion at |
| /// [WidgetsBinding.drawFrame]. |
| /// |
| /// Calling this method is theoretically relatively expensive (O(N) in the |
| /// depth of the tree), but in practice is usually cheap because the tree |
| /// usually has many render objects and therefore the distance to the nearest |
| /// render object is usually short. |
| RenderObject? findRenderObject(); |
| |
| /// The size of the [RenderBox] returned by [findRenderObject]. |
| /// |
| /// This getter will only return a valid result after the layout phase is |
| /// complete. It is therefore not valid to call this from a build method. |
| /// It should only be called from paint callbacks or interaction event |
| /// handlers (e.g. gesture callbacks). |
| /// |
| /// For details on the different phases of a frame, see the discussion at |
| /// [WidgetsBinding.drawFrame]. |
| /// |
| /// This getter will only return a valid result if [findRenderObject] actually |
| /// returns a [RenderBox]. If [findRenderObject] returns a render object that |
| /// is not a subtype of [RenderBox] (e.g., [RenderView]), this getter will |
| /// throw an exception in debug mode and will return null in release mode. |
| /// |
| /// Calling this getter is theoretically relatively expensive (O(N) in the |
| /// depth of the tree), but in practice is usually cheap because the tree |
| /// usually has many render objects and therefore the distance to the nearest |
| /// render object is usually short. |
| Size? get size; |
| |
| /// Registers this build context with [ancestor] such that when |
| /// [ancestor]'s widget changes this build context is rebuilt. |
| /// |
| /// Returns `ancestor.widget`. |
| /// |
| /// This method is rarely called directly. Most applications should use |
| /// [dependOnInheritedWidgetOfExactType], which calls this method after finding |
| /// the appropriate [InheritedElement] ancestor. |
| /// |
| /// All of the qualifications about when [dependOnInheritedWidgetOfExactType] can |
| /// be called apply to this method as well. |
| InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }); |
| |
| /// Returns the nearest widget of the given type `T` and creates a dependency |
| /// on it, or null if no appropriate widget is found. |
| /// |
| /// The widget found will be a concrete [InheritedWidget] subclass, and |
| /// calling [dependOnInheritedWidgetOfExactType] registers this build context |
| /// with the returned widget. When that widget changes (or a new widget of |
| /// that type is introduced, or the widget goes away), this build context is |
| /// rebuilt so that it can obtain new values from that widget. |
| /// |
| /// {@template flutter.widgets.BuildContext.dependOnInheritedWidgetOfExactType} |
| /// This is typically called implicitly from `of()` static methods, e.g. |
| /// [Theme.of]. |
| /// |
| /// This method should not be called from widget constructors or from |
| /// [State.initState] methods, because those methods would not get called |
| /// again if the inherited value were to change. To ensure that the widget |
| /// correctly updates itself when the inherited value changes, only call this |
| /// (directly or indirectly) from build methods, layout and paint callbacks, |
| /// or from [State.didChangeDependencies]. |
| /// |
| /// This method should not be called from [State.dispose] because the element |
| /// tree is no longer stable at that time. To refer to an ancestor from that |
| /// method, save a reference to the ancestor in [State.didChangeDependencies]. |
| /// It is safe to use this method from [State.deactivate], which is called |
| /// whenever the widget is removed from the tree. |
| /// |
| /// It is also possible to call this method from interaction event handlers |
| /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as |
| /// that value is not cached and/or reused later. |
| /// |
| /// Calling this method is O(1) with a small constant factor, but will lead to |
| /// the widget being rebuilt more often. |
| /// |
| /// Once a widget registers a dependency on a particular type by calling this |
| /// method, it will be rebuilt, and [State.didChangeDependencies] will be |
| /// called, whenever changes occur relating to that widget until the next time |
| /// the widget or one of its ancestors is moved (for example, because an |
| /// ancestor is added or removed). |
| /// |
| /// The [aspect] parameter is only used when `T` is an |
| /// [InheritedWidget] subclasses that supports partial updates, like |
| /// [InheritedModel]. It specifies what "aspect" of the inherited |
| /// widget this context depends on. |
| /// {@endtemplate} |
| T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect }); |
| |
| /// Returns the nearest widget of the given [InheritedWidget] subclass `T` or |
| /// null if an appropriate ancestor is not found. |
| /// |
| /// This method does not introduce a dependency the way that the more typical |
| /// [dependOnInheritedWidgetOfExactType] does, so this context will not be |
| /// rebuilt if the [InheritedWidget] changes. This function is meant for those |
| /// uncommon use cases where a dependency is undesirable. |
| /// |
| /// This method should not be called from [State.dispose] because the element |
| /// tree is no longer stable at that time. To refer to an ancestor from that |
| /// method, save a reference to the ancestor in [State.didChangeDependencies]. |
| /// It is safe to use this method from [State.deactivate], which is called |
| /// whenever the widget is removed from the tree. |
| /// |
| /// It is also possible to call this method from interaction event handlers |
| /// (e.g. gesture callbacks) or timers, to obtain a value once, as long as |
| /// that value is not cached and/or reused later. |
| /// |
| /// Calling this method is O(1) with a small constant factor. |
| T? getInheritedWidgetOfExactType<T extends InheritedWidget>(); |
| |
| /// Obtains the element corresponding to the nearest widget of the given type `T`, |
| /// which must be the type of a concrete [InheritedWidget] subclass. |
| /// |
| /// Returns null if no such element is found. |
| /// |
| /// {@template flutter.widgets.BuildContext.getElementForInheritedWidgetOfExactType} |
| /// Calling this method is O(1) with a small constant factor. |
| /// |
| /// This method does not establish a relationship with the target in the way |
| /// that [dependOnInheritedWidgetOfExactType] does. |
| /// |
| /// This method should not be called from [State.dispose] because the element |
| /// tree is no longer stable at that time. To refer to an ancestor from that |
| /// method, save a reference to the ancestor by calling |
| /// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]. It is |
| /// safe to use this method from [State.deactivate], which is called whenever |
| /// the widget is removed from the tree. |
| /// {@endtemplate} |
| InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>(); |
| |
| /// Returns the nearest ancestor widget of the given type `T`, which must be the |
| /// type of a concrete [Widget] subclass. |
| /// |
| /// {@template flutter.widgets.BuildContext.findAncestorWidgetOfExactType} |
| /// In general, [dependOnInheritedWidgetOfExactType] is more useful, since |
| /// inherited widgets will trigger consumers to rebuild when they change. This |
| /// method is appropriate when used in interaction event handlers (e.g. |
| /// gesture callbacks) or for performing one-off tasks such as asserting that |
| /// you have or don't have a widget of a specific type as an ancestor. The |
| /// return value of a Widget's build method should not depend on the value |
| /// returned by this method, because the build context will not rebuild if the |
| /// return value of this method changes. This could lead to a situation where |
| /// data used in the build method changes, but the widget is not rebuilt. |
| /// |
| /// Calling this method is relatively expensive (O(N) in the depth of the |
| /// tree). Only call this method if the distance from this widget to the |
| /// desired ancestor is known to be small and bounded. |
| /// |
| /// This method should not be called from [State.deactivate] or [State.dispose] |
| /// because the widget tree is no longer stable at that time. To refer to |
| /// an ancestor from one of those methods, save a reference to the ancestor |
| /// by calling [findAncestorWidgetOfExactType] in [State.didChangeDependencies]. |
| /// |
| /// Returns null if a widget of the requested type does not appear in the |
| /// ancestors of this context. |
| /// {@endtemplate} |
| T? findAncestorWidgetOfExactType<T extends Widget>(); |
| |
| /// Returns the [State] object of the nearest ancestor [StatefulWidget] widget |
| /// that is an instance of the given type `T`. |
| /// |
| /// {@template flutter.widgets.BuildContext.findAncestorStateOfType} |
| /// This should not be used from build methods, because the build context will |
| /// not be rebuilt if the value that would be returned by this method changes. |
| /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such |
| /// cases. This method is useful for changing the state of an ancestor widget in |
| /// a one-off manner, for example, to cause an ancestor scrolling list to |
| /// scroll this build context's widget into view, or to move the focus in |
| /// response to user interaction. |
| /// |
| /// In general, though, consider using a callback that triggers a stateful |
| /// change in the ancestor rather than using the imperative style implied by |
| /// this method. This will usually lead to more maintainable and reusable code |
| /// since it decouples widgets from each other. |
| /// |
| /// Calling this method is relatively expensive (O(N) in the depth of the |
| /// tree). Only call this method if the distance from this widget to the |
| /// desired ancestor is known to be small and bounded. |
| /// |
| /// This method should not be called from [State.deactivate] or [State.dispose] |
| /// because the widget tree is no longer stable at that time. To refer to |
| /// an ancestor from one of those methods, save a reference to the ancestor |
| /// by calling [findAncestorStateOfType] in [State.didChangeDependencies]. |
| /// {@endtemplate} |
| /// |
| /// {@tool snippet} |
| /// |
| /// ```dart |
| /// ScrollableState? scrollable = context.findAncestorStateOfType<ScrollableState>(); |
| /// ``` |
| /// {@end-tool} |
| T? findAncestorStateOfType<T extends State>(); |
| |
| /// Returns the [State] object of the furthest ancestor [StatefulWidget] widget |
| /// that is an instance of the given type `T`. |
| /// |
| /// {@template flutter.widgets.BuildContext.findRootAncestorStateOfType} |
| /// Functions the same way as [findAncestorStateOfType] but keeps visiting subsequent |
| /// ancestors until there are none of the type instance of `T` remaining. |
| /// Then returns the last one found. |
| /// |
| /// This operation is O(N) as well though N is the entire widget tree rather than |
| /// a subtree. |
| /// {@endtemplate} |
| T? findRootAncestorStateOfType<T extends State>(); |
| |
| /// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget |
| /// that is an instance of the given type `T`. |
| /// |
| /// {@template flutter.widgets.BuildContext.findAncestorRenderObjectOfType} |
| /// This should not be used from build methods, because the build context will |
| /// not be rebuilt if the value that would be returned by this method changes. |
| /// In general, [dependOnInheritedWidgetOfExactType] is more appropriate for such |
| /// cases. This method is useful only in esoteric cases where a widget needs |
| /// to cause an ancestor to change its layout or paint behavior. For example, |
| /// it is used by [Material] so that [InkWell] widgets can trigger the ink |
| /// splash on the [Material]'s actual render object. |
| /// |
| /// Calling this method is relatively expensive (O(N) in the depth of the |
| /// tree). Only call this method if the distance from this widget to the |
| /// desired ancestor is known to be small and bounded. |
| /// |
| /// This method should not be called from [State.deactivate] or [State.dispose] |
| /// because the widget tree is no longer stable at that time. To refer to |
| /// an ancestor from one of those methods, save a reference to the ancestor |
| /// by calling [findAncestorRenderObjectOfType] in [State.didChangeDependencies]. |
| /// {@endtemplate} |
| T? findAncestorRenderObjectOfType<T extends RenderObject>(); |
| |
| /// Walks the ancestor chain, starting with the parent of this build context's |
| /// widget, invoking the argument for each ancestor. |
| /// |
| /// {@template flutter.widgets.BuildContext.visitAncestorElements} |
| /// The callback is given a reference to the ancestor widget's corresponding |
| /// [Element] object. The walk stops when it reaches the root widget or when |
| /// the callback returns false. The callback must not return null. |
| /// |
| /// This is useful for inspecting the widget tree. |
| /// |
| /// Calling this method is relatively expensive (O(N) in the depth of the tree). |
| /// |
| /// This method should not be called from [State.deactivate] or [State.dispose] |
| /// because the element tree is no longer stable at that time. To refer to |
| /// an ancestor from one of those methods, save a reference to the ancestor |
| /// by calling [visitAncestorElements] in [State.didChangeDependencies]. |
| /// {@endtemplate} |
| void visitAncestorElements(ConditionalElementVisitor visitor); |
| |
| /// Walks the children of this widget. |
| /// |
| /// {@template flutter.widgets.BuildContext.visitChildElements} |
| /// This is useful for applying changes to children after they are built |
| /// without waiting for the next frame, especially if the children are known, |
| /// and especially if there is exactly one child (as is always the case for |
| /// [StatefulWidget]s or [StatelessWidget]s). |
| /// |
| /// Calling this method is very cheap for build contexts that correspond to |
| /// [StatefulWidget]s or [StatelessWidget]s (O(1), since there's only one |
| /// child). |
| /// |
| /// Calling this method is potentially expensive for build contexts that |
| /// correspond to [RenderObjectWidget]s (O(N) in the number of children). |
| /// |
| /// Calling this method recursively is extremely expensive (O(N) in the number |
| /// of descendants), and should be avoided if possible. Generally it is |
| /// significantly cheaper to use an [InheritedWidget] and have the descendants |
| /// pull data down, than it is to use [visitChildElements] recursively to push |
| /// data down to them. |
| /// {@endtemplate} |
| void visitChildElements(ElementVisitor visitor); |
| |
| /// Start bubbling this notification at the given build context. |
| /// |
| /// The notification will be delivered to any [NotificationListener] widgets |
| /// with the appropriate type parameters that are ancestors of the given |
| /// [BuildContext]. |
| void dispatchNotification(Notification notification); |
| |
| /// Returns a description of the [Element] associated with the current build context. |
| /// |
| /// The `name` is typically something like "The element being rebuilt was". |
| /// |
| /// See also: |
| /// |
| /// * [Element.describeElements], which can be used to describe a list of elements. |
| DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}); |
| |
| /// Returns a description of the [Widget] associated with the current build context. |
| /// |
| /// The `name` is typically something like "The widget being rebuilt was". |
| DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}); |
| |
| /// Adds a description of a specific type of widget missing from the current |
| /// build context's ancestry tree. |
| /// |
| /// You can find an example of using this method in [debugCheckHasMaterial]. |
| List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType }); |
| |
| /// Adds a description of the ownership chain from a specific [Element] |
| /// to the error report. |
| /// |
| /// The ownership chain is useful for debugging the source of an element. |
| DiagnosticsNode describeOwnershipChain(String name); |
| } |
| |
| /// Manager class for the widgets framework. |
| /// |
| /// This class tracks which widgets need rebuilding, and handles other tasks |
| /// that apply to widget trees as a whole, such as managing the inactive element |
| /// list for the tree and triggering the "reassemble" command when necessary |
| /// during hot reload when debugging. |
| /// |
| /// The main build owner is typically owned by the [WidgetsBinding], and is |
| /// driven from the operating system along with the rest of the |
| /// build/layout/paint pipeline. |
| /// |
| /// Additional build owners can be built to manage off-screen widget trees. |
| /// |
| /// To assign a build owner to a tree, use the |
| /// [RootElementMixin.assignOwner] method on the root element of the |
| /// widget tree. |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to build an off-screen widget tree used to measure |
| /// the layout size of the rendered tree. For some use cases, the simpler |
| /// [Offstage] widget may be a better alternative to this approach. |
| /// |
| /// ** See code in examples/api/lib/widgets/framework/build_owner.0.dart ** |
| /// {@end-tool} |
| class BuildOwner { |
| /// Creates an object that manages widgets. |
| /// |
| /// If the `focusManager` argument is not specified or is null, this will |
| /// construct a new [FocusManager] and register its global input handlers |
| /// via [FocusManager.registerGlobalHandlers], which will modify static |
| /// state. Callers wishing to avoid altering this state can explicitly pass |
| /// a focus manager here. |
| BuildOwner({ this.onBuildScheduled, FocusManager? focusManager }) : |
| focusManager = focusManager ?? (FocusManager()..registerGlobalHandlers()); |
| |
| /// Called on each build pass when the first buildable element is marked |
| /// dirty. |
| VoidCallback? onBuildScheduled; |
| |
| final _InactiveElements _inactiveElements = _InactiveElements(); |
| |
| final List<Element> _dirtyElements = <Element>[]; |
| bool _scheduledFlushDirtyElements = false; |
| |
| /// Whether [_dirtyElements] need to be sorted again as a result of more |
| /// elements becoming dirty during the build. |
| /// |
| /// This is necessary to preserve the sort order defined by [Element._sort]. |
| /// |
| /// This field is set to null when [buildScope] is not actively rebuilding |
| /// the widget tree. |
| bool? _dirtyElementsNeedsResorting; |
| |
| /// Whether [buildScope] is actively rebuilding the widget tree. |
| /// |
| /// [scheduleBuildFor] should only be called when this value is true. |
| bool get _debugIsInBuildScope => _dirtyElementsNeedsResorting != null; |
| |
| /// The object in charge of the focus tree. |
| /// |
| /// Rarely used directly. Instead, consider using [FocusScope.of] to obtain |
| /// the [FocusScopeNode] for a given [BuildContext]. |
| /// |
| /// See [FocusManager] for more details. |
| /// |
| /// This field will default to a [FocusManager] that has registered its |
| /// global input handlers via [FocusManager.registerGlobalHandlers]. Callers |
| /// wishing to avoid registering those handlers (and modifying the associated |
| /// static state) can explicitly pass a focus manager to the [BuildOwner.new] |
| /// constructor. |
| FocusManager focusManager; |
| |
| /// Adds an element to the dirty elements list so that it will be rebuilt |
| /// when [WidgetsBinding.drawFrame] calls [buildScope]. |
| void scheduleBuildFor(Element element) { |
| assert(element.owner == this); |
| assert(() { |
| if (debugPrintScheduleBuildForStacks) { |
| debugPrintStack(label: 'scheduleBuildFor() called for $element${_dirtyElements.contains(element) ? " (ALREADY IN LIST)" : ""}'); |
| } |
| if (!element.dirty) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.'), |
| element.describeElement('The method was called for the following element'), |
| ErrorDescription( |
| 'This element is not current marked as dirty. Make sure to set the dirty flag before ' |
| 'calling scheduleBuildFor().', |
| ), |
| ErrorHint( |
| 'If you did not attempt to call scheduleBuildFor() yourself, then this probably ' |
| 'indicates a bug in the widgets framework. Please report it:\n' |
| ' https://github.com/flutter/flutter/issues/new?template=2_bug.yml', |
| ), |
| ]); |
| } |
| return true; |
| }()); |
| if (element._inDirtyList) { |
| assert(() { |
| if (debugPrintScheduleBuildForStacks) { |
| debugPrintStack(label: 'BuildOwner.scheduleBuildFor() called; _dirtyElementsNeedsResorting was $_dirtyElementsNeedsResorting (now true); dirty list is: $_dirtyElements'); |
| } |
| if (!_debugIsInBuildScope) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.'), |
| ErrorHint( |
| 'The BuildOwner.scheduleBuildFor() method should only be called while the ' |
| 'buildScope() method is actively rebuilding the widget tree.', |
| ), |
| ]); |
| } |
| return true; |
| }()); |
| _dirtyElementsNeedsResorting = true; |
| return; |
| } |
| if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { |
| _scheduledFlushDirtyElements = true; |
| onBuildScheduled!(); |
| } |
| _dirtyElements.add(element); |
| element._inDirtyList = true; |
| assert(() { |
| if (debugPrintScheduleBuildForStacks) { |
| debugPrint('...dirty list is now: $_dirtyElements'); |
| } |
| return true; |
| }()); |
| } |
| |
| int _debugStateLockLevel = 0; |
| bool get _debugStateLocked => _debugStateLockLevel > 0; |
| |
| /// Whether this widget tree is in the build phase. |
| /// |
| /// Only valid when asserts are enabled. |
| bool get debugBuilding => _debugBuilding; |
| bool _debugBuilding = false; |
| Element? _debugCurrentBuildTarget; |
| |
| /// Establishes a scope in which calls to [State.setState] are forbidden, and |
| /// calls the given `callback`. |
| /// |
| /// This mechanism is used to ensure that, for instance, [State.dispose] does |
| /// not call [State.setState]. |
| void lockState(VoidCallback callback) { |
| assert(_debugStateLockLevel >= 0); |
| assert(() { |
| _debugStateLockLevel += 1; |
| return true; |
| }()); |
| try { |
| callback(); |
| } finally { |
| assert(() { |
| _debugStateLockLevel -= 1; |
| return true; |
| }()); |
| } |
| assert(_debugStateLockLevel >= 0); |
| } |
| |
| /// Establishes a scope for updating the widget tree, and calls the given |
| /// `callback`, if any. Then, builds all the elements that were marked as |
| /// dirty using [scheduleBuildFor], in depth order. |
| /// |
| /// This mechanism prevents build methods from transitively requiring other |
| /// build methods to run, potentially causing infinite loops. |
| /// |
| /// The dirty list is processed after `callback` returns, building all the |
| /// elements that were marked as dirty using [scheduleBuildFor], in depth |
| /// order. If elements are marked as dirty while this method is running, they |
| /// must be deeper than the `context` node, and deeper than any |
| /// previously-built node in this pass. |
| /// |
| /// To flush the current dirty list without performing any other work, this |
| /// function can be called with no callback. This is what the framework does |
| /// each frame, in [WidgetsBinding.drawFrame]. |
| /// |
| /// Only one [buildScope] can be active at a time. |
| /// |
| /// A [buildScope] implies a [lockState] scope as well. |
| /// |
| /// To print a console message every time this method is called, set |
| /// [debugPrintBuildScope] to true. This is useful when debugging problems |
| /// involving widgets not getting marked dirty, or getting marked dirty too |
| /// often. |
| @pragma('vm:notify-debugger-on-exception') |
| void buildScope(Element context, [ VoidCallback? callback ]) { |
| if (callback == null && _dirtyElements.isEmpty) { |
| return; |
| } |
| assert(_debugStateLockLevel >= 0); |
| assert(!_debugBuilding); |
| assert(() { |
| if (debugPrintBuildScope) { |
| debugPrint('buildScope called with context $context; dirty list is: $_dirtyElements'); |
| } |
| _debugStateLockLevel += 1; |
| _debugBuilding = true; |
| return true; |
| }()); |
| if (!kReleaseMode) { |
| Map<String, String>? debugTimelineArguments; |
| assert(() { |
| if (debugEnhanceBuildTimelineArguments) { |
| debugTimelineArguments = <String, String>{ |
| 'dirty count': '${_dirtyElements.length}', |
| 'dirty list': '$_dirtyElements', |
| 'lock level': '$_debugStateLockLevel', |
| 'scope context': '$context', |
| }; |
| } |
| return true; |
| }()); |
| FlutterTimeline.startSync( |
| 'BUILD', |
| arguments: debugTimelineArguments |
| ); |
| } |
| try { |
| _scheduledFlushDirtyElements = true; |
| if (callback != null) { |
| assert(_debugStateLocked); |
| Element? debugPreviousBuildTarget; |
| assert(() { |
| debugPreviousBuildTarget = _debugCurrentBuildTarget; |
| _debugCurrentBuildTarget = context; |
| return true; |
| }()); |
| _dirtyElementsNeedsResorting = false; |
| try { |
| callback(); |
| } finally { |
| assert(() { |
| assert(_debugCurrentBuildTarget == context); |
| _debugCurrentBuildTarget = debugPreviousBuildTarget; |
| _debugElementWasRebuilt(context); |
| return true; |
| }()); |
| } |
| } |
| _dirtyElements.sort(Element._sort); |
| _dirtyElementsNeedsResorting = false; |
| int dirtyCount = _dirtyElements.length; |
| int index = 0; |
| while (index < dirtyCount) { |
| final Element element = _dirtyElements[index]; |
| assert(element._inDirtyList); |
| assert(() { |
| if (element._lifecycleState == _ElementLifecycle.active && !element._debugIsInScope(context)) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('Tried to build dirty widget in the wrong build scope.'), |
| ErrorDescription( |
| 'A widget which was marked as dirty and is still active was scheduled to be built, ' |
| 'but the current build scope unexpectedly does not contain that widget.', |
| ), |
| ErrorHint( |
| 'Sometimes this is detected when an element is removed from the widget tree, but the ' |
| 'element somehow did not get marked as inactive. In that case, it might be caused by ' |
| 'an ancestor element failing to implement visitChildren correctly, thus preventing ' |
| 'some or all of its descendants from being correctly deactivated.', |
| ), |
| DiagnosticsProperty<Element>( |
| 'The root of the build scope was', |
| context, |
| style: DiagnosticsTreeStyle.errorProperty, |
| ), |
| DiagnosticsProperty<Element>( |
| 'The offending element (which does not appear to be a descendant of the root of the build scope) was', |
| element, |
| style: DiagnosticsTreeStyle.errorProperty, |
| ), |
| ]); |
| } |
| return true; |
| }()); |
| final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(element.widget); |
| if (isTimelineTracked) { |
| Map<String, String>? debugTimelineArguments; |
| assert(() { |
| if (kDebugMode && debugEnhanceBuildTimelineArguments) { |
| debugTimelineArguments = element.widget.toDiagnosticsNode().toTimelineArguments(); |
| } |
| return true; |
| }()); |
| FlutterTimeline.startSync( |
| '${element.widget.runtimeType}', |
| arguments: debugTimelineArguments, |
| ); |
| } |
| try { |
| element.rebuild(); |
| } catch (e, stack) { |
| _reportException( |
| ErrorDescription('while rebuilding dirty elements'), |
| e, |
| stack, |
| informationCollector: () => <DiagnosticsNode>[ |
| if (kDebugMode && index < _dirtyElements.length) |
| DiagnosticsDebugCreator(DebugCreator(element)), |
| if (index < _dirtyElements.length) |
| element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount') |
| else |
| ErrorHint('The element being rebuilt at the time was index $index of $dirtyCount, but _dirtyElements only had ${_dirtyElements.length} entries. This suggests some confusion in the framework internals.'), |
| ], |
| ); |
| } |
| if (isTimelineTracked) { |
| FlutterTimeline.finishSync(); |
| } |
| index += 1; |
| if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) { |
| _dirtyElements.sort(Element._sort); |
| _dirtyElementsNeedsResorting = false; |
| dirtyCount = _dirtyElements.length; |
| while (index > 0 && _dirtyElements[index - 1].dirty) { |
| // It is possible for previously dirty but inactive widgets to move right in the list. |
| // We therefore have to move the index left in the list to account for this. |
| // We don't know how many could have moved. However, we do know that the only possible |
| // change to the list is that nodes that were previously to the left of the index have |
| // now moved to be to the right of the right-most cleaned node, and we do know that |
| // all the clean nodes were to the left of the index. So we move the index left |
| // until just after the right-most clean node. |
| index -= 1; |
| } |
| } |
| } |
| assert(() { |
| if (_dirtyElements.any((Element element) => element._lifecycleState == _ElementLifecycle.active && element.dirty)) { |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('buildScope missed some dirty elements.'), |
| ErrorHint('This probably indicates that the dirty list should have been resorted but was not.'), |
| Element.describeElements('The list of dirty elements at the end of the buildScope call was', _dirtyElements), |
| ]); |
| } |
| return true; |
| }()); |
| } finally { |
| for (final Element element in _dirtyElements) { |
| assert(element._inDirtyList); |
| element._inDirtyList = false; |
| } |
| _dirtyElements.clear(); |
| _scheduledFlushDirtyElements = false; |
| _dirtyElementsNeedsResorting = null; |
| if (!kReleaseMode) { |
| FlutterTimeline.finishSync(); |
| } |
| assert(_debugBuilding); |
| assert(() { |
| _debugBuilding = false; |
| _debugStateLockLevel -= 1; |
| if (debugPrintBuildScope) { |
| debugPrint('buildScope finished'); |
| } |
| return true; |
| }()); |
| } |
| assert(_debugStateLockLevel >= 0); |
| } |
| |
| Map<Element, Set<GlobalKey>>? _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans; |
| |
| void _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans(Element node, GlobalKey key) { |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans ??= HashMap<Element, Set<GlobalKey>>(); |
| final Set<GlobalKey> keys = _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans! |
| .putIfAbsent(node, () => HashSet<GlobalKey>()); |
| keys.add(key); |
| } |
| |
| void _debugElementWasRebuilt(Element node) { |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.remove(node); |
| } |
| |
| final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{}; |
| |
| // In Profile/Release mode this field is initialized to `null`. The Dart compiler can |
| // eliminate unused fields, but not their initializers. |
| @_debugOnly |
| final Set<Element>? _debugIllFatedElements = kDebugMode ? HashSet<Element>() : null; |
| |
| // This map keeps track which child reserves the global key with the parent. |
| // Parent, child -> global key. |
| // This provides us a way to remove old reservation while parent rebuilds the |
| // child in the same slot. |
| // |
| // In Profile/Release mode this field is initialized to `null`. The Dart compiler can |
| // eliminate unused fields, but not their initializers. |
| @_debugOnly |
| final Map<Element, Map<Element, GlobalKey>>? _debugGlobalKeyReservations = kDebugMode ? <Element, Map<Element, GlobalKey>>{} : null; |
| |
| /// The number of [GlobalKey] instances that are currently associated with |
| /// [Element]s that have been built by this build owner. |
| int get globalKeyCount => _globalKeyRegistry.length; |
| |
| void _debugRemoveGlobalKeyReservationFor(Element parent, Element child) { |
| assert(() { |
| _debugGlobalKeyReservations?[parent]?.remove(child); |
| return true; |
| }()); |
| } |
| |
| void _registerGlobalKey(GlobalKey key, Element element) { |
| assert(() { |
| if (_globalKeyRegistry.containsKey(key)) { |
| final Element oldElement = _globalKeyRegistry[key]!; |
| assert(element.widget.runtimeType != oldElement.widget.runtimeType); |
| _debugIllFatedElements?.add(oldElement); |
| } |
| return true; |
| }()); |
| _globalKeyRegistry[key] = element; |
| } |
| |
| void _unregisterGlobalKey(GlobalKey key, Element element) { |
| assert(() { |
| if (_globalKeyRegistry.containsKey(key) && _globalKeyRegistry[key] != element) { |
| final Element oldElement = _globalKeyRegistry[key]!; |
| assert(element.widget.runtimeType != oldElement.widget.runtimeType); |
| } |
| return true; |
| }()); |
| if (_globalKeyRegistry[key] == element) { |
| _globalKeyRegistry.remove(key); |
| } |
| } |
| |
| void _debugReserveGlobalKeyFor(Element parent, Element child, GlobalKey key) { |
| assert(() { |
| _debugGlobalKeyReservations?[parent] ??= <Element, GlobalKey>{}; |
| _debugGlobalKeyReservations?[parent]![child] = key; |
| return true; |
| }()); |
| } |
| |
| void _debugVerifyGlobalKeyReservation() { |
| assert(() { |
| final Map<GlobalKey, Element> keyToParent = <GlobalKey, Element>{}; |
| _debugGlobalKeyReservations?.forEach((Element parent, Map<Element, GlobalKey> childToKey) { |
| // We ignore parent that are unmounted or detached. |
| if (parent._lifecycleState == _ElementLifecycle.defunct || parent.renderObject?.attached == false) { |
| return; |
| } |
| childToKey.forEach((Element child, GlobalKey key) { |
| // If parent = null, the node is deactivated by its parent and is |
| // not re-attached to other part of the tree. We should ignore this |
| // node. |
| if (child._parent == null) { |
| return; |
| } |
| // It is possible the same key registers to the same parent twice |
| // with different children. That is illegal, but it is not in the |
| // scope of this check. Such error will be detected in |
| // _debugVerifyIllFatedPopulation or |
| // _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans. |
| if (keyToParent.containsKey(key) && keyToParent[key] != parent) { |
| // We have duplication reservations for the same global key. |
| final Element older = keyToParent[key]!; |
| final Element newer = parent; |
| final FlutterError error; |
| if (older.toString() != newer.toString()) { |
| error = FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('Multiple widgets used the same GlobalKey.'), |
| ErrorDescription( |
| 'The key $key was used by multiple widgets. The parents of those widgets were:\n' |
| '- $older\n' |
| '- $newer\n' |
| 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
| ), |
| ]); |
| } else { |
| error = FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('Multiple widgets used the same GlobalKey.'), |
| ErrorDescription( |
| 'The key $key was used by multiple widgets. The parents of those widgets were ' |
| 'different widgets that both had the following description:\n' |
| ' $parent\n' |
| 'A GlobalKey can only be specified on one widget at a time in the widget tree.', |
| ), |
| ]); |
| } |
| // Fix the tree by removing the duplicated child from one of its |
| // parents to resolve the duplicated key issue. This allows us to |
| // tear down the tree during testing without producing additional |
| // misleading exceptions. |
| if (child._parent != older) { |
| older.visitChildren((Element currentChild) { |
| if (currentChild == child) { |
| older.forgetChild(child); |
| } |
| }); |
| } |
| if (child._parent != newer) { |
| newer.visitChildren((Element currentChild) { |
| if (currentChild == child) { |
| newer.forgetChild(child); |
| } |
| }); |
| } |
| throw error; |
| } else { |
| keyToParent[key] = parent; |
| } |
| }); |
| }); |
| _debugGlobalKeyReservations?.clear(); |
| return true; |
| }()); |
| } |
| |
| void _debugVerifyIllFatedPopulation() { |
| assert(() { |
| Map<GlobalKey, Set<Element>>? duplicates; |
| for (final Element element in _debugIllFatedElements ?? const <Element>{}) { |
| if (element._lifecycleState != _ElementLifecycle.defunct) { |
| assert(element.widget.key != null); |
| final GlobalKey key = element.widget.key! as GlobalKey; |
| assert(_globalKeyRegistry.containsKey(key)); |
| duplicates ??= <GlobalKey, Set<Element>>{}; |
| // Uses ordered set to produce consistent error message. |
| final Set<Element> elements = duplicates.putIfAbsent(key, () => LinkedHashSet<Element>()); |
| elements.add(element); |
| elements.add(_globalKeyRegistry[key]!); |
| } |
| } |
| _debugIllFatedElements?.clear(); |
| if (duplicates != null) { |
| final List<DiagnosticsNode> information = <DiagnosticsNode>[]; |
| information.add(ErrorSummary('Multiple widgets used the same GlobalKey.')); |
| for (final GlobalKey key in duplicates.keys) { |
| final Set<Element> elements = duplicates[key]!; |
| // TODO(jacobr): this will omit the '- ' before each widget name and |
| // use the more standard whitespace style instead. Please let me know |
| // if the '- ' style is a feature we want to maintain and we can add |
| // another tree style that supports it. I also see '* ' in some places |
| // so it would be nice to unify and normalize. |
| information.add(Element.describeElements('The key $key was used by ${elements.length} widgets', elements)); |
| } |
| information.add(ErrorDescription('A GlobalKey can only be specified on one widget at a time in the widget tree.')); |
| throw FlutterError.fromParts(information); |
| } |
| return true; |
| }()); |
| } |
| |
| /// Complete the element build pass by unmounting any elements that are no |
| /// longer active. |
| /// |
| /// This is called by [WidgetsBinding.drawFrame]. |
| /// |
| /// In debug mode, this also runs some sanity checks, for example checking for |
| /// duplicate global keys. |
| /// |
| /// After the current call stack unwinds, a microtask that notifies listeners |
| /// about changes to global keys will run. |
| @pragma('vm:notify-debugger-on-exception') |
| void finalizeTree() { |
| if (!kReleaseMode) { |
| FlutterTimeline.startSync('FINALIZE TREE'); |
| } |
| try { |
| lockState(_inactiveElements._unmountAll); // this unregisters the GlobalKeys |
| assert(() { |
| try { |
| _debugVerifyGlobalKeyReservation(); |
| _debugVerifyIllFatedPopulation(); |
| if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans != null && |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.isNotEmpty) { |
| final Set<GlobalKey> keys = HashSet<GlobalKey>(); |
| for (final Element element in _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys) { |
| if (element._lifecycleState != _ElementLifecycle.defunct) { |
| keys.addAll(_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans![element]!); |
| } |
| } |
| if (keys.isNotEmpty) { |
| final Map<String, int> keyStringCount = HashMap<String, int>(); |
| for (final String key in keys.map<String>((GlobalKey key) => key.toString())) { |
| if (keyStringCount.containsKey(key)) { |
| keyStringCount.update(key, (int value) => value + 1); |
| } else { |
| keyStringCount[key] = 1; |
| } |
| } |
| final List<String> keyLabels = <String>[]; |
| keyStringCount.forEach((String key, int count) { |
| if (count == 1) { |
| keyLabels.add(key); |
| } else { |
| keyLabels.add('$key ($count different affected keys had this toString representation)'); |
| } |
| }); |
| final Iterable<Element> elements = _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans!.keys; |
| final Map<String, int> elementStringCount = HashMap<String, int>(); |
| for (final String element in elements.map<String>((Element element) => element.toString())) { |
| if (elementStringCount.containsKey(element)) { |
| elementStringCount.update(element, (int value) => value + 1); |
| } else { |
| elementStringCount[element] = 1; |
| } |
| } |
| final List<String> elementLabels = <String>[]; |
| elementStringCount.forEach((String element, int count) { |
| if (count == 1) { |
| elementLabels.add(element); |
| } else { |
| elementLabels.add('$element ($count different affected elements had this toString representation)'); |
| } |
| }); |
| assert(keyLabels.isNotEmpty); |
| final String the = keys.length == 1 ? ' the' : ''; |
| final String s = keys.length == 1 ? '' : 's'; |
| final String were = keys.length == 1 ? 'was' : 'were'; |
| final String their = keys.length == 1 ? 'its' : 'their'; |
| final String respective = elementLabels.length == 1 ? '' : ' respective'; |
| final String those = keys.length == 1 ? 'that' : 'those'; |
| final String s2 = elementLabels.length == 1 ? '' : 's'; |
| final String those2 = elementLabels.length == 1 ? 'that' : 'those'; |
| final String they = elementLabels.length == 1 ? 'it' : 'they'; |
| final String think = elementLabels.length == 1 ? 'thinks' : 'think'; |
| final String are = elementLabels.length == 1 ? 'is' : 'are'; |
| // TODO(jacobr): make this error more structured to better expose which widgets had problems. |
| throw FlutterError.fromParts(<DiagnosticsNode>[ |
| ErrorSummary('Duplicate GlobalKey$s detected in widget tree.'), |
| // TODO(jacobr): refactor this code so the elements are clickable |
| // in GUI debug tools. |
| ErrorDescription( |
| 'The following GlobalKey$s $were specified multiple times in the widget tree. This will lead to ' |
| 'parts of the widget tree being truncated unexpectedly, because the second time a key is seen, ' |
| 'the previous instance is moved to the new location. The key$s $were:\n' |
| '- ${keyLabels.join("\n ")}\n' |
| 'This was determined by noticing that after$the widget$s with the above global key$s $were moved ' |
| 'out of $their$respective previous parent$s2, $those2 previous parent$s2 never updated during this frame, meaning ' |
| 'that $they either did not update at all or updated before the widget$s $were moved, in either case ' |
| 'implying that $they still $think that $they should have a child with $those global key$s.\n' |
| 'The specific parent$s2 that did not update after having one or more children forcibly removed ' |
| 'due to GlobalKey reparenting $are:\n' |
| '- ${elementLabels.join("\n ")}' |
| '\nA GlobalKey can only be specified on one widget at a time in the widget tree.', |
| ), |
| ]); |
| } |
| } |
| } finally { |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear(); |
| } |
| return true; |
| }()); |
| } catch (e, stack) { |
| // Catching the exception directly to avoid activating the ErrorWidget. |
| // Since the tree is in a broken state, adding the ErrorWidget would |
| // cause more exceptions. |
| _reportException(ErrorSummary('while finalizing the widget tree'), e, stack); |
| } finally { |
| if (!kReleaseMode) { |
| FlutterTimeline.finishSync(); |
| } |
| } |
| } |
| |
| /// Cause the entire subtree rooted at the given [Element] to be entirely |
| /// rebuilt. This is used by development tools when the application code has |
| /// changed and is being hot-reloaded, to cause the widget tree to pick up any |
| /// changed implementations. |
| /// |
| /// This is expensive and should not be called except during development. |
| void reassemble(Element root, DebugReassembleConfig? reassembleConfig) { |
| if (!kReleaseMode) { |
| FlutterTimeline.startSync('Preparing Hot Reload (widgets)'); |
| } |
| try { |
| assert(root._parent == null); |
| assert(root.owner == this); |
| root._debugReassembleConfig = reassembleConfig; |
| root.reassemble(); |
| } finally { |
| if (!kReleaseMode) { |
| FlutterTimeline.finishSync(); |
| } |
| } |
| } |
| } |
| |
| /// Mixin this class to allow receiving [Notification] objects dispatched by |
| /// child elements. |
| /// |
| /// See also: |
| /// * [NotificationListener], for a widget that allows consuming notifications. |
| mixin NotifiableElementMixin on Element { |
| /// Called when a notification of the appropriate type arrives at this |
| /// location in the tree. |
| /// |
| /// Return true to cancel the notification bubbling. Return false to |
| /// allow the notification to continue to be dispatched to further ancestors. |
| bool onNotification(Notification notification); |
| |
| @override |
| void attachNotificationTree() { |
| _notificationTree = _NotificationNode(_parent?._notificationTree, this); |
| } |
| } |
| |
| class _NotificationNode { |
| _NotificationNode(this.parent, this.current); |
| |
| NotifiableElementMixin? current; |
| _NotificationNode? parent; |
| |
| void dispatchNotification(Notification notification) { |
| if (current?.onNotification(notification) ?? true) { |
| return; |
| } |
| parent?.dispatchNotification(notification); |
| } |
| } |
| |
| bool _isProfileBuildsEnabledFor(Widget widget) { |
| return debugProfileBuildsEnabled || |
| (debugProfileBuildsEnabledUserWidgets && |
| debugIsWidgetLocalCreation(widget)); |
| } |
| |
| /// An instantiation of a [Widget] at a particular location in the tree. |
| /// |
| /// Widgets describe how to configure a subtree but the same widget can be used |
| /// to configure multiple subtrees simultaneously because widgets are immutable. |
| /// An [Element] represents the use of a widget to configure a specific location |
| /// in the tree. Over time, the widget associated with a given element can |
| /// change, for example, if the parent widget rebuilds and creates a new widget |
| /// for this location. |
| /// |
| /// Elements form a tree. Most elements have a unique child, but some widgets |
| /// (e.g., subclasses of [RenderObjectElement]) can have multiple children. |
| /// |
| /// Elements have the following lifecycle: |
| /// |
| /// * The framework creates an element by calling [Widget.createElement] on the |
| /// widget that will be used as the element's initial configuration. |
| /// * The framework calls [mount] to add the newly created element to the tree |
| /// at a given slot in a given parent. The [mount] method is responsible for |
| /// inflating any child widgets and calling [attachRenderObject] as |
| /// necessary to attach any associated render objects to the render tree. |
| /// * At this point, the element is considered "active" and might appear on |
| /// screen. |
| /// * At some point, the parent might decide to change the widget used to |
| /// configure this element, for example because the parent rebuilt with new |
| /// state. When this happens, the framework will call [update] with the new |
| /// widget. The new widget will always have the same [runtimeType] and key as |
| /// old widget. If the parent wishes to change the [runtimeType] or key of |
| /// the widget at this location in the tree, it can do so by unmounting this |
| /// element and inflating the new widget at this location. |
| /// * At some point, an ancestor might decide to remove this element (or an |
| /// intermediate ancestor) from the tree, which the ancestor does by calling |
| /// [deactivateChild] on itself. Deactivating the intermediate ancestor will |
| /// remove that element's render object from the render tree and add this |
| /// element to the [owner]'s list of inactive elements, causing the framework |
| /// to call [deactivate] on this element. |
| /// * At this point, the element is considered "inactive" and will not appear |
| /// on screen. An element can remain in the inactive state only until |
| /// the end of the current animation frame. At the end of the animation |
| /// frame, any elements that are still inactive will be unmounted. |
| /// * If the element gets reincorporated into the tree (e.g., because it or one |
| /// of its ancestors has a global key that is reused), the framework will |
| /// remove the element from the [owner]'s list of inactive elements, call |
| /// [activate] on the element, and reattach the element's render object to |
| /// the render tree. (At this point, the element is again considered "active" |
| /// and might appear on screen.) |
| /// * If the element does not get reincorporated into the tree by the end of |
| /// the current animation frame, the framework will call [unmount] on the |
| /// element. |
| /// * At this point, the element is considered "defunct" and will not be |
| /// incorporated into the tree in the future. |
| abstract class Element extends DiagnosticableTree implements BuildContext { |
| /// Creates an element that uses the given widget as its configuration. |
| /// |
| /// Typically called by an override of [Widget.createElement]. |
| Element(Widget widget) |
| : _widget = widget { |
| if (kFlutterMemoryAllocationsEnabled) { |
| MemoryAllocations.instance.dispatchObjectCreated( |
| library: _flutterWidgetsLibrary, |
| className: '$Element', |
| object: this, |
| ); |
| } |
| } |
| |
| Element? _parent; |
| DebugReassembleConfig? _debugReassembleConfig; |
| _NotificationNode? _notificationTree; |
| |
| /// Compare two widgets for equality. |
| /// |
| /// When a widget is rebuilt with another that compares equal according |
| /// to `operator ==`, it is assumed that the update is redundant and the |
| /// work to update that branch of the tree is skipped. |
| /// |
| /// It is generally discouraged to override `operator ==` on any widget that |
| /// has children, since a correct implementation would have to defer to the |
| /// children's equality operator also, and that is an O(N²) operation: each |
| /// child would need to itself walk all its children, each step of the tree. |
| /// |
| /// It is sometimes reasonable for a leaf widget (one with no children) to |
| /// implement this method, if rebuilding the widget is known to be much more |
| /// expensive than checking the widgets' parameters for equality and if the |
| /// widget is expected to often be rebuilt with identical parameters. |
| /// |
| /// In general, however, it is more efficient to cache the widgets used |
| /// in a build method if it is known that they will not change. |
| @nonVirtual |
| @override |
| // ignore: avoid_equals_and_hash_code_on_mutable_classes, hash_and_equals |
| bool operator ==(Object other) => identical(this, other); |
| |
| /// Information set by parent to define where this child fits in its parent's |
| /// child list. |
| /// |
| /// A child widget's slot is determined when the parent's [updateChild] method |
| /// is called to inflate the child widget. See [RenderObjectElement] for more |
| /// details on slots. |
| Object? get slot => _slot; |
| Object? _slot; |
| |
| /// An integer that is guaranteed to be greater than the parent's, if any. |
| /// The element at the root of the tree must have a depth greater than 0. |
| int get depth { |
| assert(() { |
| if (_lifecycleState == _ElementLifecycle.initial) { |
| throw FlutterError('Depth is only available when element has been mounted.'); |
| } |
| return true; |
| }()); |
| return _depth; |
| } |
| late int _depth; |
| |
| /// Returns result < 0 when [a] < [b], result == 0 when [a] == [b], result > 0 |
| /// when [a] > [b]. |
| static int _sort(Element a, Element b) { |
| final int diff = a.depth - b.depth; |
| // If depths are not equal, return the difference. |
| if (diff != 0) { |
| return diff; |
| } |
| // If the `dirty` values are not equal, sort with non-dirty elements being |
| // less than dirty elements. |
| final bool isBDirty = b.dirty; |
| if (a.dirty != isBDirty) { |
| return isBDirty ? -1 : 1; |
| } |
| // Otherwise, `depth`s and `dirty`s are equal. |
| return 0; |
| } |
| |
| // Return a numeric encoding of the specific `Element` concrete subtype. |
| // This is used in `Element.updateChild` to determine if a hot reload modified the |
| // superclass of a mounted element's configuration. The encoding of each `Element` |
| // must match the corresponding `Widget` encoding in `Widget._debugConcreteSubtype`. |
| static int _debugConcreteSubtype(Element element) { |
| return element is StatefulElement ? 1 : |
| element is StatelessElement ? 2 : |
| 0; |
| } |
| |
| /// The configuration for this element. |
| /// |
| /// Avoid overriding this field on [Element] subtypes to provide a more |
| /// specific widget type (i.e. [StatelessElement] and [StatelessWidget]). |
| /// Instead, cast at any call sites where the more specific type is required. |
| /// This avoids significant cast overhead on the getter which is accessed |
| /// throughout the framework internals during the build phase - and for which |
| /// the more specific type information is not used. |
| @override |
| Widget get widget => _widget!; |
| Widget? _widget; |
| |
| @override |
| bool get mounted => _widget != null; |
| |
| /// Returns true if the Element is defunct. |
| /// |
| /// This getter always returns false in profile and release builds. |
| /// See the lifecycle documentation for [Element] for additional information. |
| bool get debugIsDefunct { |
| bool isDefunct = false; |
| assert(() { |
| isDefunct = _lifecycleState == _ElementLifecycle.defunct; |
| return true; |
| }()); |
| return isDefunct; |
| } |
| |
| /// Returns true if the Element is active. |
| /// |
| /// This getter always returns false in profile and release builds. |
| /// See the lifecycle documentation for [Element] for additional information. |
| bool get debugIsActive { |
| bool isActive = false; |
| assert(() { |
| isActive = _lifecycleState == _ElementLifecycle.active; |
| return true; |
| }()); |
| return isActive; |
| } |
| |
| /// The object that manages the lifecycle of this element. |
| @override |
| BuildOwner? get owner => _owner; |
| BuildOwner? _owner; |
| |
| /// {@template flutter.widgets.Element.reassemble} |
| /// Called whenever the application is reassembled during debugging, for |
| /// example during hot reload. |
| /// |
| /// This method should rerun any initialization logic that depends on global |
| /// state, for example, image loading from asset bundles (since the asset |
| /// bundle may have changed). |
| /// |
| /// This function will only be called during development. In release builds, |
| /// the `ext.flutter.reassemble` hook is not available, and so this code will |
| /// never execute. |
| /// |
| /// Implementers should not rely on any ordering for hot reload source update, |
| /// reassemble, and build methods after a hot reload has been initiated. It is |
| /// possible that a [Timer] (e.g. an [Animation]) or a debugging session |
| /// attached to the isolate could trigger a build with reloaded code _before_ |
| /// reassemble is called. Code that expects preconditions to be set by |
| /// reassemble after a hot reload must be resilient to being called out of |
| /// order, e.g. by fizzling instead of throwing. That said, once reassemble is |
| /// called, build will be called after it at least once. |
| /// {@endtemplate} |
| /// |
| /// See also: |
| /// |
| /// * [State.reassemble] |
| /// * [BindingBase.reassembleApplication] |
| /// * [Image], which uses this to reload images. |
| @mustCallSuper |
| @protected |
| void reassemble() { |
| if (_debugShouldReassemble(_debugReassembleConfig, _widget)) { |
| markNeedsBuild(); |
| _debugReassembleConfig = null; |
| } |
| visitChildren((Element child) { |
| child._debugReassembleConfig = _debugReassembleConfig; |
| child.reassemble(); |
| }); |
| _debugReassembleConfig = null; |
| } |
| |
| bool _debugIsInScope(Element target) { |
| Element? current = this; |
| while (current != null) { |
| if (target == current) { |
| return true; |
| } |
| current = current._parent; |
| } |
| return false; |
| } |
| |
| /// The render object at (or below) this location in the tree. |
| /// |
| /// If this object is a [RenderObjectElement], the render object is the one at |
| /// this location in the tree. Otherwise, this getter will walk down the tree |
| /// until it finds a [RenderObjectElement]. |
| RenderObject? get renderObject { |
| Element? current = this; |
| while (current != null) { |
| if (current._lifecycleState == _ElementLifecycle.defunct) { |
| break; |
| } else if (current is RenderObjectElement) { |
| return current.renderObject; |
| } else { |
| Element? next; |
| current.visitChildren((Element child) { |
|