| // Copyright 2015 The Chromium 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 'dart:developer'; |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| import 'debug.dart'; |
| import 'focus_manager.dart'; |
| |
| export 'dart:ui' show hashValues, hashList; |
| |
| export 'package:flutter/foundation.dart' show |
| immutable, |
| mustCallSuper, |
| optionalTypeArgs, |
| protected, |
| required, |
| visibleForTesting; |
| |
| export 'package:flutter/foundation.dart' show FlutterError, debugPrint, debugPrintStack; |
| export 'package:flutter/foundation.dart' show VoidCallback, ValueChanged, ValueGetter, ValueSetter; |
| export 'package:flutter/foundation.dart' show DiagnosticLevel; |
| export 'package:flutter/foundation.dart' show Key, LocalKey, ValueKey; |
| export 'package:flutter/rendering.dart' show RenderObject, RenderBox, debugDumpRenderTree, debugDumpLayerTree; |
| |
| // Examples can assume: |
| // BuildContext context; |
| // void setState(VoidCallback fn) { } |
| |
| // Examples can assume: |
| // abstract class RenderFrogJar extends RenderObject { } |
| // abstract class FrogJar extends RenderObjectWidget { } |
| // abstract class FrogJarParentData extends ParentData { Size size; } |
| |
| // DOCUMENTATION TEMPLATES FOR THE WIDGET LIBRARY |
| |
| /// {@template flutter.widgets.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} |
| |
| // KEYS |
| |
| /// A key that is only equal to itself. |
| class UniqueKey extends LocalKey { |
| /// Creates a key that is equal only to itself. |
| // ignore: prefer_const_constructors_in_immutables , never use const for this class |
| UniqueKey(); |
| |
| @override |
| String toString() => '[#${shortHash(this)}]'; |
| } |
| |
| /// 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 the discussions at [Key] and [Widget.key]. |
| 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 ==(dynamic other) { |
| if (other.runtimeType != runtimeType) |
| return false; |
| final ObjectKey typedOther = other; |
| return identical(value, typedOther.value); |
| } |
| |
| @override |
| int get hashCode => hashValues(runtimeType, identityHashCode(value)); |
| |
| @override |
| String toString() { |
| if (runtimeType == ObjectKey) |
| return '[${describeIdentity(value)}]'; |
| return '[$runtimeType ${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 elements, such as the a [BuildContext] and, |
| /// for [StatefulWidget]s, a [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. |
| /// |
| /// Global keys are relatively expensive. 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. |
| /// |
| /// See also the discussion at [Widget.key]. |
| @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 }) => new 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(); |
| |
| static final Map<GlobalKey, Element> _registry = <GlobalKey, Element>{}; |
| static final Set<GlobalKey> _removedKeys = new HashSet<GlobalKey>(); |
| static final Set<Element> _debugIllFatedElements = new HashSet<Element>(); |
| static final Map<GlobalKey, Element> _debugReservations = <GlobalKey, Element>{}; |
| |
| void _register(Element element) { |
| assert(() { |
| if (_registry.containsKey(this)) { |
| assert(element.widget != null); |
| assert(_registry[this].widget != null); |
| assert(element.widget.runtimeType != _registry[this].widget.runtimeType); |
| _debugIllFatedElements.add(_registry[this]); |
| } |
| return true; |
| }()); |
| _registry[this] = element; |
| } |
| |
| void _unregister(Element element) { |
| assert(() { |
| if (_registry.containsKey(this) && _registry[this] != element) { |
| assert(element.widget != null); |
| assert(_registry[this].widget != null); |
| assert(element.widget.runtimeType != _registry[this].widget.runtimeType); |
| } |
| return true; |
| }()); |
| if (_registry[this] == element) { |
| _registry.remove(this); |
| _removedKeys.add(this); |
| } |
| } |
| |
| void _debugReserveFor(Element parent) { |
| assert(() { |
| assert(parent != null); |
| if (_debugReservations.containsKey(this) && _debugReservations[this] != parent) { |
| // It's possible for an element to get built multiple times in one |
| // frame, in which case it'll reserve the same child's key multiple |
| // times. We catch multiple children of one widget having the same key |
| // by verifying that an element never steals elements from itself, so we |
| // don't care to verify that here as well. |
| final String older = _debugReservations[this].toString(); |
| final String newer = parent.toString(); |
| if (older != newer) { |
| throw new FlutterError( |
| 'Multiple widgets used the same GlobalKey.\n' |
| 'The key $this 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.' |
| ); |
| } |
| throw new FlutterError( |
| 'Multiple widgets used the same GlobalKey.\n' |
| 'The key $this was used by multiple widgets. The parents of those widgets were ' |
| 'different widgets that both had the following description:\n' |
| ' $newer\n' |
| 'A GlobalKey can only be specified on one widget at a time in the widget tree.' |
| ); |
| } |
| _debugReservations[this] = parent; |
| return true; |
| }()); |
| } |
| |
| static void _debugVerifyIllFatedPopulation() { |
| assert(() { |
| Map<GlobalKey, Set<Element>> duplicates; |
| for (Element element in _debugIllFatedElements) { |
| if (element._debugLifecycleState != _ElementLifecycle.defunct) { |
| assert(element != null); |
| assert(element.widget != null); |
| assert(element.widget.key != null); |
| final GlobalKey key = element.widget.key; |
| assert(_registry.containsKey(key)); |
| duplicates ??= <GlobalKey, Set<Element>>{}; |
| final Set<Element> elements = duplicates.putIfAbsent(key, () => new HashSet<Element>()); |
| elements.add(element); |
| elements.add(_registry[key]); |
| } |
| } |
| _debugIllFatedElements.clear(); |
| _debugReservations.clear(); |
| if (duplicates != null) { |
| final StringBuffer buffer = new StringBuffer(); |
| buffer.writeln('Multiple widgets used the same GlobalKey.\n'); |
| for (GlobalKey key in duplicates.keys) { |
| final Set<Element> elements = duplicates[key]; |
| buffer.writeln('The key $key was used by ${elements.length} widgets:'); |
| for (Element element in elements) |
| buffer.writeln('- $element'); |
| } |
| buffer.write('A GlobalKey can only be specified on one widget at a time in the widget tree.'); |
| throw new FlutterError(buffer.toString()); |
| } |
| return true; |
| }()); |
| } |
| |
| Element get _currentElement => _registry[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. |
| /// |
| /// 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(Object value) : 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. |
| /// |
| /// Any [GlobalObjectKey] created for the same value will match. |
| @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 ==(dynamic other) { |
| if (other.runtimeType != runtimeType) |
| return false; |
| final GlobalObjectKey<T> typedOther = other; |
| return identical(value, typedOther.value); |
| } |
| |
| @override |
| int get hashCode => identityHashCode(value); |
| |
| @override |
| String toString() { |
| String selfType = runtimeType.toString(); |
| // const GlobalObjectKey().runtimeType.toString() 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)}]'; |
| } |
| } |
| |
| /// This class is a work-around for the "is" operator not accepting a variable value as its right operand |
| @optionalTypeArgs |
| class TypeMatcher<T> { |
| /// Creates a type matcher for the given type parameter. |
| const TypeMatcher(); |
| |
| /// Returns true if the given object is of type `T`. |
| bool check(dynamic object) => object is T; |
| } |
| |
| /// 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 |
| Element createElement(); |
| |
| /// A short, textual description of this widget. |
| @override |
| String toStringShort() { |
| return key == null ? '$runtimeType' : '$runtimeType-$key'; |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; |
| } |
| |
| |
| /// 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; |
| } |
| } |
| |
| /// 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). |
| /// |
| /// 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, 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. |
| /// |
| /// ## Sample code |
| /// |
| /// The following is a skeleton of a stateless widget subclass called `GreenFrog`: |
| /// |
| /// ```dart |
| /// class GreenFrog extends StatelessWidget { |
| /// const GreenFrog({ Key key }) : super(key: key); |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return new Container(color: const Color(0xFF2DBD3A)); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// Normally widgets have more constructor arguments, each of which corresponds |
| /// to a `final` property. The next example shows the more generic widget `Frog` |
| /// which can be given a color and a child: |
| /// |
| /// ```dart |
| /// class Frog extends StatelessWidget { |
| /// const Frog({ |
| /// Key key, |
| /// this.color: const Color(0xFF2DBD3A), |
| /// this.child, |
| /// }) : super(key: key); |
| /// |
| /// final Color color; |
| /// |
| /// final Widget child; |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return new Container(color: color, child: child); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// By convention, widget constructors only use named arguments. Named arguments |
| /// can be marked as required using [@required]. 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({ Key key }) : super(key: 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() => new 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). |
| /// |
| /// 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.inheritFromWidgetOfExactType]. |
| /// |
| /// If a widget's [build] method is to depend on anything else, use a |
| /// [StatefulWidget] instead. |
| /// |
| /// See also: |
| /// |
| /// * The discussion on performance considerations at [StatelessWidget]. |
| @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 widget 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]. |
| /// |
| /// [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. It is massively more |
| /// efficient for a widget to be re-used than for a new (but |
| /// identically-configured) widget to be created. Factoring out the stateful |
| /// part into a widget that takes a child argument is a common way of doing |
| /// this. |
| /// |
| /// * 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.) |
| /// |
| /// ## Sample code |
| /// |
| /// The following is a skeleton of a stateful widget subclass called `YellowBird`: |
| /// |
| /// ```dart |
| /// class YellowBird extends StatefulWidget { |
| /// const YellowBird({ Key key }) : super(key: key); |
| /// |
| /// @override |
| /// _YellowBirdState createState() => new _YellowBirdState(); |
| /// } |
| /// |
| /// class _YellowBirdState extends State<YellowBird> { |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return new Container(color: const Color(0xFFFFE306)); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// 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. |
| /// |
| /// The next 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({ |
| /// Key key, |
| /// this.color: const Color(0xFFFFE306), |
| /// this.child, |
| /// }) : super(key: key); |
| /// |
| /// final Color color; |
| /// |
| /// final Widget child; |
| /// |
| /// _BirdState createState() => new _BirdState(); |
| /// } |
| /// |
| /// class _BirdState extends State<Bird> { |
| /// double _size = 1.0; |
| /// |
| /// void grow() { |
| /// setState(() { _size += 0.1; }); |
| /// } |
| /// |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// return new Container( |
| /// color: widget.color, |
| /// transform: new Matrix4.diagonal3Values(_size, _size, 1.0), |
| /// child: widget.child, |
| /// ); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// By convention, widget constructors only use named arguments. Named arguments |
| /// can be marked as required using [@required]. 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({ Key key }) : super(key: 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() => new 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 |
| /// _MyState createState() => new _MyState(); |
| /// ``` |
| /// |
| /// 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 |
| 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 void StateSetter(VoidCallback fn); |
| |
| /// 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.inheritFromWidgetOfExactType] 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 callings 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. |
| /// * 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> extends 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 => _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]). |
| /// |
| /// 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 the [State] should |
| /// subscribe to that object during [initState], unsubscribe from the old |
| /// object and subscribe to the new object when it changes in |
| /// [didUpdateWidget], and then unsubscribe from the object in [dispose]. |
| /// |
| /// You cannot use [BuildContext.inheritFromWidgetOfExactType] from this |
| /// method. However, [didChangeDependencies] will be called immediately |
| /// following this method, and [BuildContext.inheritFromWidgetOfExactType] can |
| /// be used there. |
| /// |
| /// If you override this, make sure your method starts with a call to |
| /// super.initState(). |
| @protected |
| @mustCallSuper |
| void initState() { |
| assert(_debugLifecycleState == _StateLifecycle.created); |
| } |
| |
| /// Called whenever the widget configuration changes. |
| /// |
| /// If the parent widget rebuilds and request 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. |
| /// |
| /// 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 the [State] should |
| /// subscribe to that object during [initState], unsubscribe from the old |
| /// object and subscribe to the new object when it changes in |
| /// [didUpdateWidget], and then unsubscribe from the object in [dispose]. |
| /// |
| /// If you override this, make sure your method starts with a call to |
| /// super.didUpdateWidget(oldWidget). |
| @mustCallSuper |
| @protected |
| void didUpdateWidget(covariant T oldWidget) { } |
| |
| /// 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). |
| /// |
| /// 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. |
| /// |
| /// 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. |
| /// |
| /// See also: |
| /// |
| /// * [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<Null> _incrementCounter() async { |
| /// setState(() { |
| /// _counter++; |
| /// }); |
| /// Directory directory = await getApplicationDocumentsDirectory(); |
| /// final String dirName = directory.path; |
| /// await new File('$dir/counter.txt').writeAsString('$_counter'); |
| /// return null; |
| /// } |
| /// ``` |
| /// |
| /// 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(fn != null); |
| assert(() { |
| if (_debugLifecycleState == _StateLifecycle.defunct) { |
| throw new FlutterError( |
| 'setState() called after dispose(): $this\n' |
| '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. 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.\n' |
| '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 new FlutterError( |
| 'setState() called in constructor: $this\n' |
| '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 dynamic result = fn() as dynamic; |
| assert(() { |
| if (result is Future) { |
| throw new FlutterError( |
| 'setState() callback argument returned a Future.\n' |
| 'The setState() method on $this was called with a closure or method that ' |
| 'returned a Future. Maybe it is marked as "async".\n' |
| '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). 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. |
| /// |
| /// 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]). |
| /// |
| /// If you override this, make sure to end your method with a call to |
| /// 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 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). |
| /// |
| /// 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 the [State] should |
| /// subscribe to that object during [initState], unsubscribe from the old |
| /// object and subscribe to the new object when it changes in |
| /// [didUpdateWidget], and then unsubscribe from the object in [dispose]. |
| /// |
| /// If you override this, make sure to end your method with a call to |
| /// 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; }()); |
| } |
| |
| /// Describes the part of the user interface represented by this widget. |
| /// |
| /// The framework calls this method in a number of different situations: |
| /// |
| /// * 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. |
| /// |
| /// 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 |
| /// 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 |
| /// class MyButton extends StatefulWidget { |
| /// ... |
| /// final Color color; |
| /// |
| /// @override |
| /// Widget build(BuildContext context, MyButtonState state) { |
| /// ... () { 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. |
| /// |
| /// 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 MyButtonState extends State<MyButton> { |
| /// ... |
| /// @override |
| /// Widget build(BuildContext context) { |
| /// ... () { 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: |
| /// |
| /// * The discussion on performance considerations at [StatefulWidget]. |
| @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.inheritFromWidgetOfExactType] 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(new EnumProperty<_StateLifecycle>('lifecycle state', _debugLifecycleState, defaultValue: _StateLifecycle.ready)); |
| return true; |
| }()); |
| properties.add(new ObjectFlagProperty<T>('_widget', _widget, ifNull: 'no widget')); |
| properties.add(new 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({ Key key, @required this.child }) : super(key: key); |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@macro flutter.widgets.child} |
| 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 [RenderObject], and |
| /// thus also to a particular [RenderObjectWidget] class. That class is `T`, the |
| /// [ParentDataWidget] type argument. |
| /// |
| /// ## Sample code |
| /// |
| /// 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<FrogJar> { |
| /// FrogSize({ |
| /// Key key, |
| /// @required this.size, |
| /// @required Widget child, |
| /// }) : assert(child != null), |
| /// assert(size != null), |
| /// super(key: key, child: child); |
| /// |
| /// final Size size; |
| /// |
| /// @override |
| /// void applyParentData(RenderObject renderObject) { |
| /// final FrogJarParentData parentData = renderObject.parentData; |
| /// if (parentData.size != size) { |
| /// parentData.size = size; |
| /// final RenderFrogJar targetParent = renderObject.parent; |
| /// targetParent.markNeedsLayout(); |
| /// } |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// 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. |
| /// * [RenderObjectWidget], the class for widgets that wrap [RenderObject]s. |
| /// The `T` type parameter for [ParentDataWidget] is a [RenderObjectWidget]. |
| /// * [StatefulWidget] and [State], for widgets that can build differently |
| /// several times over their lifetime. |
| abstract class ParentDataWidget<T extends RenderObjectWidget> extends ProxyWidget { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const ParentDataWidget({ Key key, Widget child }) |
| : super(key: key, child: child); |
| |
| @override |
| ParentDataElement<T> createElement() => new ParentDataElement<T>(this); |
| |
| /// Subclasses should override this method to return true if the given |
| /// ancestor is a RenderObjectWidget that wraps a RenderObject that can handle |
| /// the kind of ParentData widget that the ParentDataWidget subclass handles. |
| /// |
| /// The default implementation uses the type argument. |
| bool debugIsValidAncestor(RenderObjectWidget ancestor) { |
| assert(T != dynamic); |
| assert(T != RenderObjectWidget); |
| return ancestor is T; |
| } |
| |
| /// Subclasses should override this to describe the requirements for using the |
| /// ParentDataWidget subclass. It is called when debugIsValidAncestor() |
| /// returned false for an ancestor, or when there are extraneous |
| /// [ParentDataWidget]s in the ancestor chain. |
| String debugDescribeInvalidAncestorChain({ String description, String ownershipChain, bool foundValidAncestor, Iterable<Widget> badAncestors }) { |
| assert(T != dynamic); |
| assert(T != RenderObjectWidget); |
| String result; |
| if (!foundValidAncestor) { |
| result = '$runtimeType widgets must be placed inside $T widgets.\n' |
| '$description has no $T ancestor at all.\n'; |
| } else { |
| assert(badAncestors.isNotEmpty); |
| result = '$runtimeType widgets must be placed directly inside $T widgets.\n' |
| '$description has a $T ancestor, but there are other widgets between them:\n'; |
| for (Widget ancestor in badAncestors) { |
| if (ancestor.runtimeType == runtimeType) { |
| result += '- $ancestor (this is a different $runtimeType than the one with the problem)\n'; |
| } else { |
| result += '- $ancestor\n'; |
| } |
| } |
| result += 'These widgets cannot come between a $runtimeType and its $T.\n'; |
| } |
| result += 'The ownership chain for the parent of the offending $runtimeType was:\n $ownershipChain'; |
| return result; |
| } |
| |
| /// 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. |
| /// |
| /// To obtain the nearest instance of a particular type of inherited widget from |
| /// a build context, use [BuildContext.inheritFromWidgetOfExactType]. |
| /// |
| /// Inherited widgets, when referenced in this way, will cause the consumer to |
| /// rebuild when the inherited widget itself changes state. |
| /// |
| /// ## Sample code |
| /// |
| /// The following is a skeleton of an inherited widget called `FrogColor`: |
| /// |
| /// ```dart |
| /// class FrogColor extends InheritedWidget { |
| /// const FrogColor({ |
| /// Key key, |
| /// @required this.color, |
| /// @required Widget child, |
| /// }) : assert(color != null), |
| /// assert(child != null), |
| /// super(key: key, child: child); |
| /// |
| /// final Color color; |
| /// |
| /// static FrogColor of(BuildContext context) { |
| /// return context.inheritFromWidgetOfExactType(FrogColor); |
| /// } |
| /// |
| /// @override |
| /// bool updateShouldNotify(FrogColor old) => color != old.color; |
| /// } |
| /// ``` |
| /// |
| /// The convention is to provide a static method `of` on the [InheritedWidget] |
| /// which does the call to [BuildContext.inheritFromWidgetOfExactType]. This |
| /// allows the class to define its own fallback logic in the case of there not |
| /// being a widget in scope. In the example above, the value returned will be |
| /// null in that case, but it could also have defaulted to a value. |
| /// |
| /// Sometimes, the `of` method returns the data rather than the inherited |
| /// widget; 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` method in that case is typically |
| /// put on the public class instead. For example, [Theme] is implemented as a |
| /// [StatelessWidget] that builds a private inherited widget; [Theme.of] looks |
| /// for that inherited widget using [BuildContext.inheritFromWidgetOfExactType] |
| /// and then returns the [ThemeData]. |
| /// |
| /// 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. |
| 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({ Key key, Widget child }) |
| : super(key: key, child: child); |
| |
| @override |
| InheritedElement createElement() => new 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 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. |
| 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({ Key key }) : super(key: key); |
| |
| /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass. |
| @override |
| 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 |
| 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({ Key key }) : super(key: key); |
| |
| @override |
| LeafRenderObjectElement createElement() => new LeafRenderObjectElement(this); |
| } |
| |
| /// A superclass for RenderObjectWidgets 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.) |
| 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({ Key key, this.child }) : super(key: key); |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@macro flutter.widgets.child} |
| final Widget child; |
| |
| @override |
| SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this); |
| } |
| |
| /// A superclass for RenderObjectWidgets 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.) |
| abstract class MultiChildRenderObjectWidget extends RenderObjectWidget { |
| /// Initializes fields for subclasses. |
| /// |
| /// The [children] argument must not be null and must not contain any null |
| /// objects. |
| MultiChildRenderObjectWidget({ Key key, this.children = const <Widget>[] }) |
| : assert(children != null), |
| assert(!children.any((Widget child) => child == null)), // https://github.com/dart-lang/sdk/issues/29276 |
| super(key: key); |
| |
| /// The widgets below this widget in the tree. |
| /// |
| /// If this list is going to be mutated, it is usually wise to put [Key]s on |
| /// the widgets, so that the framework can match old configurations to new |
| /// configurations and maintain the underlying render objects. |
| final List<Widget> children; |
| |
| @override |
| MultiChildRenderObjectElement createElement() => new MultiChildRenderObjectElement(this); |
| } |
| |
| |
| // ELEMENTS |
| |
| enum _ElementLifecycle { |
| initial, |
| active, |
| inactive, |
| defunct, |
| } |
| |
| class _InactiveElements { |
| bool _locked = false; |
| final Set<Element> _elements = new HashSet<Element>(); |
| |
| void _unmount(Element element) { |
| assert(element._debugLifecycleState == _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._debugLifecycleState == _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; |
| } |
| } |
| |
| void _deactivateRecursively(Element element) { |
| assert(element._debugLifecycleState == _ElementLifecycle.active); |
| element.deactivate(); |
| assert(element._debugLifecycleState == _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._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._active); |
| } |
| |
| bool debugContains(Element element) { |
| 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 void ElementVisitor(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.showSnackBar] |
| /// 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 new Scaffold( |
| /// appBar: new AppBar(title: new Text('Demo')), |
| /// body: new Builder( |
| /// builder: (BuildContext context) { |
| /// return new FlatButton( |
| /// child: new Text('BUTTON'), |
| /// onPressed: () { |
| /// // here, Scaffold.of(context) returns the locally created Scaffold |
| /// Scaffold.of(context).showSnackBar(new SnackBar( |
| /// content: new Text('Hello.') |
| /// )); |
| /// } |
| /// ); |
| /// } |
| /// ) |
| /// ); |
| /// } |
| /// ``` |
| /// |
| /// 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. |
| /// |
| /// [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; |
| |
| /// 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. |
| /// |
| /// 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 checked 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; |
| |
| /// Obtains the nearest widget of the given type, which must be the type of a |
| /// concrete [InheritedWidget] subclass, and registers this build context with |
| /// that widget such that 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. |
| /// |
| /// This is typically called implicitly from `of()` static methods, e.g. |
| /// [Theme.of]. |
| /// |
| /// This 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 from interaction event handlers (e.g. |
| /// gesture callbacks) or timers, to obtain a value once, if that value is not |
| /// going to be cached and 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). |
| InheritedWidget inheritFromWidgetOfExactType(Type targetType); |
| |
| /// Obtains the element corresponding to the nearest widget of the given type, |
| /// which must be the type of a concrete [InheritedWidget] subclass. |
| /// |
| /// 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 [inheritFromWidgetOfExactType] 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 |
| /// [inheritFromWidgetOfExactType] in [State.didChangeDependencies]. It is |
| /// safe to use this method from [State.deactivate], which is called whenever |
| /// the widget is removed from the tree. |
| InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType); |
| |
| /// Returns the nearest ancestor widget of the given type, which must be the |
| /// type of a concrete [Widget] subclass. |
| /// |
| /// 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, [inheritFromWidgetOfExactType] is more useful. This method is |
| /// appropriate when used in interaction event handlers (e.g. gesture |
| /// callbacks), or for performing one-off tasks. |
| /// |
| /// 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 [ancestorWidgetOfExactType] in [State.didChangeDependencies]. |
| Widget ancestorWidgetOfExactType(Type targetType); |
| |
| /// Returns the [State] object of the nearest ancestor [StatefulWidget] widget |
| /// that matches the given [TypeMatcher]. |
| /// |
| /// 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, [inheritFromWidgetOfExactType] 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 [ancestorStateOfType] in [State.didChangeDependencies]. |
| /// |
| /// ## Sample code |
| /// |
| /// ```dart |
| /// ScrollableState scrollable = context.ancestorStateOfType( |
| /// const TypeMatcher<ScrollableState>(), |
| /// ); |
| /// ``` |
| State ancestorStateOfType(TypeMatcher matcher); |
| |
| /// Returns the [State] object of the furthest ancestor [StatefulWidget] widget |
| /// that matches the given [TypeMatcher]. |
| /// |
| /// Functions the same way as [ancestorStateOfType] but keeps visiting subsequent |
| /// ancestors until there are none of the type matching [TypeMatcher] 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. |
| State rootAncestorStateOfType(TypeMatcher matcher); |
| |
| /// Returns the [RenderObject] object of the nearest ancestor [RenderObjectWidget] widget |
| /// that matches the given [TypeMatcher]. |
| /// |
| /// 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, [inheritFromWidgetOfExactType] 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 [ancestorRenderObjectOfType] in [State.didChangeDependencies]. |
| RenderObject ancestorRenderObjectOfType(TypeMatcher matcher); |
| |
| /// Walks the ancestor chain, starting with the parent of this build context's |
| /// widget, invoking the argument for each ancestor. 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]. |
| void visitAncestorElements(bool visitor(Element element)); |
| |
| /// Walks the children of this widget. |
| /// |
| /// 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. |
| void visitChildElements(ElementVisitor visitor); |
| } |
| |
| /// 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 |
| /// [RootRenderObjectElement.assignOwner] method on the root element of the |
| /// widget tree. |
| class BuildOwner { |
| /// Creates an object that manages widgets. |
| BuildOwner({ this.onBuildScheduled }); |
| |
| /// Called on each build pass when the first buildable element is marked |
| /// dirty. |
| VoidCallback onBuildScheduled; |
| |
| final _InactiveElements _inactiveElements = new _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. |
| final FocusManager focusManager = new 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 != null); |
| assert(element.owner == this); |
| assert(() { |
| if (debugPrintScheduleBuildForStacks) |
| debugPrintStack(label: 'scheduleBuildFor() called for $element${_dirtyElements.contains(element) ? " (ALREADY IN LIST)" : ""}'); |
| if (!element.dirty) { |
| throw new FlutterError( |
| 'scheduleBuildFor() called for a widget that is not marked as dirty.\n' |
| 'The method was called for the following element:\n' |
| ' $element\n' |
| 'This element is not current marked as dirty. Make sure to set the dirty flag before ' |
| 'calling scheduleBuildFor().\n' |
| 'If you did not attempt to call scheduleBuildFor() yourself, then this probably ' |
| 'indicates a bug in the widgets framework. Please report it: ' |
| 'https://github.com/flutter/flutter/issues/new' |
| ); |
| } |
| 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 new FlutterError( |
| 'BuildOwner.scheduleBuildFor() called inappropriately.\n' |
| '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(void callback()) { |
| assert(callback != null); |
| 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. |
| void buildScope(Element context, [VoidCallback callback]) { |
| if (callback == null && _dirtyElements.isEmpty) |
| return; |
| assert(context != null); |
| assert(_debugStateLockLevel >= 0); |
| assert(!_debugBuilding); |
| assert(() { |
| if (debugPrintBuildScope) |
| debugPrint('buildScope called with context $context; dirty list is: $_dirtyElements'); |
| _debugStateLockLevel += 1; |
| _debugBuilding = true; |
| return true; |
| }()); |
| Timeline.startSync('Build', arguments: timelineWhitelistArguments); |
| try { |
| _scheduledFlushDirtyElements = true; |
| if (callback != null) { |
| assert(_debugStateLocked); |
| Element debugPreviousBuildTarget; |
| assert(() { |
| context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true); |
| debugPreviousBuildTarget = _debugCurrentBuildTarget; |
| _debugCurrentBuildTarget = context; |
| return true; |
| }()); |
| _dirtyElementsNeedsResorting = false; |
| try { |
| callback(); |
| } finally { |
| assert(() { |
| context._debugSetAllowIgnoredCallsToMarkNeedsBuild(false); |
| 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) { |
| assert(_dirtyElements[index] != null); |
| assert(_dirtyElements[index]._inDirtyList); |
| assert(!_dirtyElements[index]._active || _dirtyElements[index]._debugIsInScope(context)); |
| try { |
| _dirtyElements[index].rebuild(); |
| } catch (e, stack) { |
| _debugReportException( |
| 'while rebuilding dirty elements', e, stack, |
| informationCollector: (StringBuffer information) { |
| information.writeln('The element being rebuilt at the time was index $index of $dirtyCount:'); |
| information.write(' ${_dirtyElements[index]}'); |
| } |
| ); |
| } |
| 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._active && element.dirty)) { |
| throw new FlutterError( |
| 'buildScope missed some dirty elements.\n' |
| 'This probably indicates that the dirty list should have been resorted but was not.\n' |
| 'The list of dirty elements at the end of the buildScope call was:\n' |
| ' $_dirtyElements' |
| ); |
| } |
| return true; |
| }()); |
| } finally { |
| for (Element element in _dirtyElements) { |
| assert(element._inDirtyList); |
| element._inDirtyList = false; |
| } |
| _dirtyElements.clear(); |
| _scheduledFlushDirtyElements = false; |
| _dirtyElementsNeedsResorting = null; |
| Timeline.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 ??= new HashMap<Element, Set<GlobalKey>>(); |
| final Set<GlobalKey> keys = _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans |
| .putIfAbsent(node, () => new HashSet<GlobalKey>()); |
| keys.add(key); |
| } |
| |
| void _debugElementWasRebuilt(Element node) { |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.remove(node); |
| } |
| |
| /// 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. |
| void finalizeTree() { |
| Timeline.startSync('Finalize tree', arguments: timelineWhitelistArguments); |
| try { |
| lockState(() { |
| _inactiveElements._unmountAll(); // this unregisters the GlobalKeys |
| }); |
| assert(() { |
| try { |
| GlobalKey._debugVerifyIllFatedPopulation(); |
| if (_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans != null && |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans.isNotEmpty) { |
| final Set<GlobalKey> keys = new HashSet<GlobalKey>(); |
| for (Element element in _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans.keys) { |
| if (element._debugLifecycleState != _ElementLifecycle.defunct) |
| keys.addAll(_debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans[element]); |
| } |
| if (keys.isNotEmpty) { |
| final Map<String, int> keyStringCount = new HashMap<String, int>(); |
| for (String key in keys.map<String>((GlobalKey key) => key.toString())) { |
| if (keyStringCount.containsKey(key)) { |
| keyStringCount[key] += 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 = new HashMap<String, int>(); |
| for (String element in elements.map<String>((Element element) => element.toString())) { |
| if (elementStringCount.containsKey(element)) { |
| elementStringCount[element] += 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'; |
| throw new FlutterError( |
| 'Duplicate GlobalKey$s detected in widget tree.\n' |
| '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 ")}\n' |
| 'A GlobalKey can only be specified on one widget at a time in the widget tree.' |
| ); |
| } |
| } |
| } finally { |
| _debugElementsThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans?.clear(); |
| } |
| return true; |
| }()); |
| } catch (e, stack) { |
| _debugReportException('while finalizing the widget tree', e, stack); |
| } finally { |
| Timeline.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) { |
| Timeline.startSync('Dirty Element Tree'); |
| try { |
| assert(root._parent == null); |
| assert(root.owner == this); |
| root._reassemble(); |
| } finally { |
| Timeline.finishSync(); |
| } |
| } |
| } |
| |
| /// 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, 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) |
| : assert(widget != null), |
| _widget = widget; |
| |
| Element _parent; |
| |
| // Custom implementation of `operator ==` optimized for the ".of" pattern |
| // used with `InheritedWidgets`. |
| @override |
| bool operator ==(Object other) => identical(this, other); |
| |
| // Custom implementation of hash code optimized for the ".of" pattern used |
| // with `InheritedWidgets`. |
| // |
| // `Element.inheritFromWidgetOfExactType` relies heavily on hash-based |
| // `Set` look-ups, putting this getter on the performance critical path. |
| // |
| // The value is designed to fit within the SMI representation. This makes |
| // the cached value use less memory (one field and no extra heap objects) and |
| // cheap to compare (no indirection). |
| // |
| // See also: |
| // |
| // * https://www.dartlang.org/articles/dart-vm/numeric-computation, which |
| // explains how numbers are represented in Dart. |
| @override |
| int get hashCode => _cachedHash; |
| final int _cachedHash = _nextHashCode = (_nextHashCode + 1) % 0xffffff; |
| static int _nextHashCode = 1; |
| |
| /// Information set by parent to define where this child fits in its parent's |
| /// child list. |
| /// |
| /// Subclasses of Element that only have one child should use null for |
| /// the slot for that child. |
| dynamic get slot => _slot; |
| dynamic _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 => _depth; |
| int _depth; |
| |
| static int _sort(Element a, Element b) { |
| if (a.depth < b.depth) |
| return -1; |
| if (b.depth < a.depth) |
| return 1; |
| if (b.dirty && !a.dirty) |
| return -1; |
| if (a.dirty && !b.dirty) |
| return 1; |
| return 0; |
| } |
| |
| /// The configuration for this element. |
| @override |
| Widget get widget => _widget; |
| Widget _widget; |
| |
| /// The object that manages the lifecycle of this element. |
| @override |
| BuildOwner get owner => _owner; |
| BuildOwner _owner; |
| |
| bool _active = false; |
| |
| @mustCallSuper |
| void _reassemble() { |
| markNeedsBuild(); |
| visitChildren((Element child) { |
| child._reassemble(); |
| }); |
| } |
| |
| 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 { |
| RenderObject result; |
| void visit(Element element) { |
| assert(result == null); // this verifies that there's only one child |
| if (element is RenderObjectElement) |
| result = element.renderObject; |
| else |
| element.visitChildren(visit); |
| } |
| visit(this); |
| return result; |
| } |
| |
| // This is used to verify that Element objects move through life in an |
| // orderly fashion. |
| _ElementLifecycle _debugLifecycleState = _ElementLifecycle.initial; |
| |
| /// Calls the argument for each child. Must be overridden by subclasses that |
| /// support having children. |
| /// |
| /// There is no guaranteed order in which the children will be visited, though |
| /// it should be consistent over time. |
| /// |
| /// Calling this during build is dangerous: the child list might still be |
| /// being updated at that point, so the children might not be constructed yet, |
| /// or might be old children that are going to be replaced. This method should |
| /// only be called if it is provable that the children are available. |
| void visitChildren(ElementVisitor visitor) { } |
| |
| /// Calls the argument for each child considered onstage. |
| /// |
| /// Classes like [Offstage] and [Overlay] override this method to hide their |
| /// children. |
| /// |
| /// Being onstage affects the element's discoverability during testing when |
| /// you use Flutter's [Finder] objects. For example, when you instruct the |
| /// test framework to tap on a widget, by default the finder will look for |
| /// onstage elements and ignore the offstage ones. |
| /// |
| /// The default implementation defers to [visitChildren] and therefore treats |
| /// the element as onstage. |
| /// |
| /// See also: |
| /// |
| /// - [Offstage] widget that hides its children. |
| /// - [Finder] that skips offstage widgets by default. |
| /// - [RenderObject.visitChildrenForSemantics], in contrast to this method, |
| /// designed specifically for excluding parts of the UI from the semantics |
| /// tree. |
| void debugVisitOnstageChildren(ElementVisitor visitor) => visitChildren(visitor); |
| |
| /// Wrapper around [visitChildren] for [BuildContext]. |
| @override |
| void visitChildElements(ElementVisitor visitor) { |
| assert(() { |
| if (owner == null || !owner._debugStateLocked) |
| return true; |
| throw new FlutterError( |
| 'visitChildElements() called during build.\n' |
| 'The BuildContext.visitChildElements() method can\'t be called during ' |
| 'build because the child list is still being updated at that point, ' |
| 'so the children might not be constructed yet, or might be old children ' |
| 'that are going to be replaced.' |
| ); |
| }()); |
| visitChildren(visitor); |
| } |
| |
| /// Update the given child with the given new configuration. |
| /// |
| /// This method is the core of the widgets system. It is called each time we |
| /// are to add, update, or remove a child based on an updated configuration. |
| /// |
| /// If the `child` is null, and the `newWidget` is not null, then we have a new |
| /// child for which we need to create an [Element], configured with `newWidget`. |
| /// |
| /// If the `newWidget` is null, and the `child` is not null, then we need to |
| /// remove it because it no longer has a configuration. |
| /// |
| /// If neither are null, then we need to update the `child`'s configuration to |
| /// be the new configuration given by `newWidget`. If `newWidget` can be given |
| /// to the existing child (as determined by [Widget.canUpdate]), then it is so |
| /// given. Otherwise, the old child needs to be disposed and a new child |
| /// created for the new configuration. |
| /// |
| /// If both are null, then we don't have a child and won't have a child, so we |
| /// do nothing. |
| /// |
| /// The [updateChild] method returns the new child, if it had to create one, |
| /// or the child that was passed in, if it just had to update the child, or |
| /// null, if it removed the child and did not replace it. |
| /// |
| /// The following table summarizes the above: |
| /// |
| /// | | **newWidget == null** | **newWidget != null** | |
| /// | :-----------------: | :--------------------- | :---------------------- | |
| /// | **child == null** | Returns null. | Returns new [Element]. | |
| /// | **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. | |
| @protected |
| Element updateChild(Element child, Widget newWidget, dynamic newSlot) { |
| assert(() { |
| if (newWidget != null && newWidget.key is GlobalKey) { |
| final GlobalKey key = newWidget.key; |
| key._debugReserveFor(this); |
| } |
| return true; |
| }()); |
| if (newWidget == null) { |
| if (child != null) |
| deactivateChild(child); |
| return null; |
| } |
| if (child != null) { |
| if (child.widget == newWidget) { |
| if (child.slot != newSlot) |
| updateSlotForChild(child, newSlot); |
| return child; |
| } |
| if (Widget.canUpdate(child.widget, newWidget)) { |
| if (child.slot != newSlot) |
| updateSlotForChild(child, newSlot); |
| child.update(newWidget); |
| assert(child.widget == newWidget); |
| assert(() { |
| child.owner._debugElementWasRebuilt(child); |
| return true; |
| }()); |
| return child; |
| } |
| deactivateChild(child); |
| assert(child._parent == null); |
| } |
| return inflateWidget(newWidget, newSlot); |
| } |
| |
| /// Add this element to the tree in the given slot of the given parent. |
| /// |
| /// The framework calls this function when a newly created element is added to |
| /// the tree for the first time. Use this method to initialize state that |
| /// depends on having a parent. State that is independent of the parent can |
| /// more easily be initialized in the constructor. |
| /// |
| /// This method transitions the element from the "initial" lifecycle state to |
| /// the "active" lifecycle state. |
| @mustCallSuper |
| void mount(Element parent, dynamic newSlot) { |
| assert(_debugLifecycleState == _ElementLifecycle.initial); |
| assert(widget != null); |
| assert(_parent == null); |
| assert(parent == null || parent._debugLifecycleState == _ElementLifecycle.active); |
| assert(slot == null); |
| assert(depth == null); |
| assert(!_active); |
| _parent = parent; |
| _slot = newSlot; |
| _depth = _parent != null ? _parent.depth + 1 : 1; |
| _active = true; |
| if (parent != null) // Only assign ownership if the parent is non-null |
| _owner = parent.owner; |
| if (widget.key is GlobalKey) { |
| final GlobalKey key = widget.key; |
| key._register(this); |
| } |
| _updateInheritance(); |
| assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }()); |
| } |
| |
| /// Change the widget used to configure this element. |
| /// |
| /// The framework calls this function when the parent wishes to use a |
| /// different widget to configure this element. The new widget is guaranteed |
| /// to have the same [runtimeType] as the old widget. |
| /// |
| /// This function is called only during the "active" lifecycle state. |
| @mustCallSuper |
| void update(covariant Widget newWidget) { |
| // This code is hot when hot reloading, so we try to |
| // only call _AssertionError._evaluateAssertion once. |
| assert(_debugLifecycleState == _ElementLifecycle.active |
| && widget != null |
| && newWidget != null |
| && newWidget != widget |
| && depth != null |
| && _active |
| && Widget.canUpdate(widget, newWidget)); |
| _widget = newWidget; |
| } |
| |
| /// Change the slot that the given child occupies in its parent. |
| /// |
| /// Called by [MultiChildRenderObjectElement], and other [RenderObjectElement] |
| /// subclasses that have multiple children, when child moves from one position |
| /// to another in this element's child list. |
| @protected |
| void updateSlotForChild(Element child, dynamic newSlot) { |
| assert(_debugLifecycleState == _ElementLifecycle.active); |
| assert(child != null); |
| assert(child._parent == this); |
| void visit(Element element) { |
| element._updateSlot(newSlot); |
| if (element is! RenderObjectElement) |
| element.visitChildren(visit); |
| } |
| visit(child); |
| } |
| |
| void _updateSlot(dynamic newSlot) { |
| assert(_debugLifecycleState == _ElementLifecycle.active); |
| assert(widget != null); |
| assert(_parent != null); |
| assert(_parent._debugLifecycleState == _ElementLifecycle.active); |
| assert(depth != null); |
| _slot = newSlot; |
| } |
| |
| void _updateDepth(int parentDepth) { |
| final int expectedDepth = parentDepth + 1; |
| if (_depth < expectedDepth) { |
| _depth = expectedDepth; |
| visitChildren((Element child) { |
| child._updateDepth(expectedDepth); |
| }); |
| } |
| } |
| |
| /// Remove [renderObject] from the render tree. |
| /// |
| /// The default implementation of this function simply calls |
| /// [detachRenderObject] recursively on its child. The |
| /// [RenderObjectElement.detachRenderObject] override does the actual work of |
| /// removing [renderObject] from the render tree. |
| /// |
| /// This is called by [deactivateChild]. |
| void detachRenderObject() { |
| visitChildren((Element child) { |
| child.detachRenderObject(); |
| }); |
| _slot = null; |
| } |
| |
| /// Add [renderObject] to the render tree at the location specified by [slot]. |
| /// |
| /// The default implementation of this function simply calls |
| /// [attachRenderObject] recursively on its child. The |
| /// [RenderObjectElement.attachRenderObject] override does the actual work of |
| /// adding [renderObject] to the render tree. |
| void attachRenderObject(dynamic newSlot) { |
| assert(_slot == null); |
| visitChildren((Element child) { |
| child.attachRenderObject(newSlot); |
| }); |
| _slot = newSlot; |
| } |
| |
| Element _retakeInactiveElement(GlobalKey key, Widget newWidget) { |
| // The "inactivity" of the element being retaken here may be forward-looking: if |
| // we are taking an element with a GlobalKey from an element that currently has |
| // it as a child, then we know that that element will soon no longer have that |
| // element as a child. The only way that assumption could be false is if the |
| // global key is being duplicated, and we'll try to track that using the |
| // _debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans call below. |
| final Element element = key._currentElement; |
| if (element == null) |
| return null; |
| if (!Widget.canUpdate(element.widget, newWidget)) |
| return null; |
| assert(() { |
| if (debugPrintGlobalKeyedWidgetLifecycle) |
| debugPrint('Attempting to take $element from ${element._parent ?? "inactive elements list"} to put in $this.'); |
| return true; |
| }()); |
| final Element parent = element._parent; |
| if (parent != null) { |
| assert(() { |
| if (parent == this) { |
| throw new FlutterError( |
| 'A GlobalKey was used multiple times inside one widget\'s child list.\n' |
| 'The offending GlobalKey was: $key\n' |
| 'The parent of the widgets with that key was:\n $parent\n' |
| 'The first child to get instantiated with that key became:\n $element\n' |
| 'The second child that was to be instantiated with that key was:\n $widget\n' |
| 'A GlobalKey can only be specified on one widget at a time in the widget tree.' |
| ); |
| } |
| parent.owner._debugTrackElementThatWillNeedToBeRebuiltDueToGlobalKeyShenanigans( |
| parent, |
| key, |
| ); |
| return true; |
| }()); |
| parent.forgetChild(element); |
| parent.deactivateChild(element); |
| } |
| assert(element._parent == null); |
| owner._inactiveElements.remove(element); |
| return element; |
| } |
| |
| /// Create an element for the given widget and add it as a child of this |
| /// element in the given slot. |
| /// |
| /// This method is typically called by [updateChild] but can be called |
| /// directly by subclasses that need finer-grained control over creating |
| /// elements. |
| /// |
| /// If the given widget has a global key and an element already exists that |
| /// has a widget with that global key, this function will reuse that element |
| /// (potentially grafting it from another location in the tree or reactivating |
| /// it from the list of inactive elements) rather than creating a new element. |
| /// |
| /// The element returned by this function will already have been mounted and |
| /// will be in the "active" lifecycle state. |
| @protected |
| Element inflateWidget(Widget newWidget, dynamic newSlot) { |
| assert(newWidget != null); |
| final Key key = newWidget.key; |
| if (key is GlobalKey) { |
| final Element newChild = _retakeInactiveElement(key, newWidget); |
| if (newChild != null) { |
| assert(newChild._parent == null); |
| assert(() { _debugCheckForCycles(newChild); return true; }()); |
| newChild._activateWithParent(this, newSlot); |
| final Element updatedChild = updateChild(newChild, newWidget, newSlot); |
| assert(newChild == updatedChild); |
| return updatedChild; |
| } |
| } |
| final Element newChild = newWidget.createElement(); |
| assert(() { _debugCheckForCycles(newChild); return true; }()); |
| newChild.mount(this, newSlot); |
| assert(newChild._debugLifecycleState == _ElementLifecycle.active); |
| return newChild; |
| } |
| |
| void _debugCheckForCycles(Element newChild) { |
| assert(newChild._parent == null); |
| assert(() { |
| Element node = this; |
| while (node._parent != null) |
| node = node._parent; |
| assert(node != newChild); // indicates we are about to create a cycle |
| return true; |
| }()); |
| } |
| |
| /// Move the given element to the list of inactive elements and detach its |
| /// render object from the render tree. |
| /// |
| /// This method stops the given element from being a child of this element by |
| /// detaching its render object from the render tree and moving the element to |
| /// the list of inactive elements. |
| /// |
| /// This method (indirectly) calls [deactivate] on the child. |
| /// |
| /// The caller is responsible for removing the child from its child model. |
| /// Typically [deactivateChild] is called by the element itself while it is |
| /// updating its child model; however, during [GlobalKey] reparenting, the new |
| /// parent proactively calls the old parent's [deactivateChild], first using |
| /// [forgetChild] to cause the old parent to update its child model. |
| @protected |
| void deactivateChild(Element child) { |
| assert(child != null); |
| assert(child._parent == this); |
| child._parent = null; |
| child.detachRenderObject(); |
| owner._inactiveElements.add(child); // this eventually calls child.deactivate() |
| assert(() { |
| if (debugPrintGlobalKeyedWidgetLifecycle) { |
| if (child.widget.key is GlobalKey) |
| debugPrint('Deactivated $child (keyed child of $this)'); |
| } |
| return true; |
| }()); |
| } |
| |
| /// Remove the given child from the element's child list, in preparation for |
| /// the child being reused elsewhere in the element tree. |
| /// |
| /// This updates the child model such that, e.g., [visitChildren] does not |
| /// walk that child anymore. |
| /// |
| /// The element will still have a valid parent when this is called. After this |
| /// is called, [deactivateChild] is called to sever the link to this object. |
| @protected |
| void forgetChild(Element child); |
| |
| void _activateWithParent(Element parent, dynamic newSlot) { |
| assert(_debugLifecycleState == _ElementLifecycle.inactive); |
| _parent = parent; |
| assert(() { |
| if (debugPrintGlobalKeyedWidgetLifecycle) |
| debugPrint('Reactivating $this (now child of $_parent).'); |
| return true; |
| }()); |
| _updateDepth(_parent.depth); |
| _activateRecursively(this); |
| attachRenderObject(newSlot); |
| assert(_debugLifecycleState == _ElementLifecycle.active); |
| } |
| |
| static void _activateRecursively(Element element) { |
| assert(element._debugLifecycleState == _ElementLifecycle.inactive); |
| element.activate(); |
| assert(element._debugLifecycleState == _ElementLifecycle.active); |
| element.visitChildren(_activateRecursively); |
| } |
| |
| /// Transition from the "inactive" to the "active" lifecycle state. |
| /// |
| /// The framework calls this method when a previously deactivated element has |
| /// been reincorporated into the tree. The framework does not call this method |
| /// the first time an element becomes active (i.e., from the "initial" |
| /// lifecycle state). Instead, the framework calls [mount] in that situation. |
| /// |
| /// See the lifecycle documentation for [Element] for additional information. |
| @mustCallSuper |
| void activate() { |
| assert(_debugLifecycleState == _ElementLifecycle.inactive); |
| assert(widget != null); |
| assert(owner != null); |
| assert(depth != null); |
| assert(!_active); |
| final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies; |
| _active = true; |
| // We unregistered our dependencies in deactivate, but never cleared the list. |
| // Since we're going to be reused, let's clear our list now. |
| _dependencies?.clear(); |
| _hadUnsatisfiedDependencies = false; |
| _updateInheritance(); |
| assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }()); |
| if (_dirty) |
| owner.scheduleBuildFor(this); |
| if (hadDependencies) |
| didChangeDependencies(); |
| } |
| |
| /// Transition from the "active" to the "inactive" lifecycle state. |
| /// |
| /// The framework calls this method when a previously active element is moved |
| /// to the list of inactive elements. While in the inactive state, the element |
| /// will not appear on screen. The element can remain in the inactive state |
| /// only until the end of the current animation frame. At the end of the |
| /// animation frame, if the element has not be reactivated, the framework will |
| /// unmount the element. |
| /// |
| /// This is (indirectly) called by [deactivateChild]. |
| /// |
| /// See the lifecycle documentation for [Element] for additional information. |
| @mustCallSuper |
| void deactivate() { |
| assert(_debugLifecycleState == _ElementLifecycle.active); |
| assert(widget != null); |
| assert(depth != null); |
| assert(_active); |
| if (_dependencies != null && _dependencies.isNotEmpty) { |
| for (InheritedElement dependency in _dependencies) |
| dependency._dependents.remove(this); |
| // For expediency, we don't actually clear the list here, even though it's |
| // no longer representative of what we are registered with. If we never |
| // get re-used, it doesn't matter. If we do, then we'll clear the list in |
| // activate(). The benefit of this is that it allows Element's activate() |
| // implementation to decide whether to rebuild based on whether we had |
| // dependencies here. |
| } |
| _inheritedWidgets = null; |
| _active = false; |
| assert(() { _debugLifecycleState = _ElementLifecycle.inactive; return true; }()); |
| } |
| |
| /// Called, in debug mode, after children have been deactivated (see [deactivate]). |
| /// |
| /// This method is not called in release builds. |
| @mustCallSuper |
| void debugDeactivated() { |
| assert(_debugLifecycleState == _ElementLifecycle.inactive); |
| } |
| |
| /// Transition from the "inactive" to the "defunct" lifecycle state. |
| /// |
| /// Called when the framework determines that an inactive element will never |
| /// be reactivated. At the end of each animation frame, the framework calls |
| /// [unmount] on any remaining inactive elements, preventing inactive elements |
| /// from remaining inactive for longer than a single animation frame. |
| /// |
| /// After this function is called, the element will not be incorporated into |
| /// the tree again. |
| /// |
| /// See the lifecycle documentation for [Element] for additional information. |
| @mustCallSuper |
| void unmount() { |
| assert(_debugLifecycleState == _ElementLifecycle.inactive); |
| assert(widget != null); |
| assert(depth != null); |
| assert(!_active); |
| if (widget.key is GlobalKey) { |
| final GlobalKey key = widget.key; |
| key._unregister(this); |
| } |
| assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; }()); |
| } |
| |
| @override |
| RenderObject findRenderObject() => renderObject; |
| |
| @override |
| Size get size { |
| assert(() { |
| if (_debugLifecycleState != _ElementLifecycle.active) { |
| throw new FlutterError( |
| 'Cannot get size of inactive element.\n' |
| 'In order for an element to have a valid size, the element must be ' |
| 'active, which means it is part of the tree. Instead, this element ' |
| 'is in the $_debugLifecycleState state.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| ); |
| } |
| if (owner._debugBuilding) { |
| throw new FlutterError( |
| 'Cannot get size during build.\n' |
| 'The size of this render object has not yet been determined because ' |
| 'the framework is still in the process of building widgets, which ' |
| 'means the render tree for this frame has not yet been determined. ' |
| 'The size getter should only be called from paint callbacks or ' |
| 'interaction event handlers (e.g. gesture callbacks).\n' |
| '\n' |
| 'If you need some sizing information during build to decide which ' |
| 'widgets to build, consider using a LayoutBuilder widget, which can ' |
| 'tell you the layout constraints at a given location in the tree. See ' |
| '<https://docs.flutter.io/flutter/widgets/LayoutBuilder-class.html> ' |
| 'for more details.\n' |
| '\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| ); |
| } |
| return true; |
| }()); |
| final RenderObject renderObject = findRenderObject(); |
| assert(() { |
| if (renderObject == null) { |
| throw new FlutterError( |
| 'Cannot get size without a render object.\n' |
| 'In order for an element to have a valid size, the element must have ' |
| 'an associated render object. This element does not have an associated ' |
| 'render object, which typically means that the size getter was called ' |
| 'too early in the pipeline (e.g., during the build phase) before the ' |
| 'framework has created the render tree.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| ); |
| } |
| if (renderObject is RenderSliver) { |
| throw new FlutterError( |
| 'Cannot get size from a RenderSliver.\n' |
| 'The render object associated with this element is a ' |
| '${renderObject.runtimeType}, which is a subtype of RenderSliver. ' |
| 'Slivers do not have a size per se. They have a more elaborate ' |
| 'geometry description, which can be accessed by calling ' |
| 'findRenderObject and then using the "geometry" getter on the ' |
| 'resulting object.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| 'The associated render sliver was:\n' |
| ' ${renderObject.toStringShallow(joiner: "\n ")}' |
| ); |
| } |
| if (renderObject is! RenderBox) { |
| throw new FlutterError( |
| 'Cannot get size from a render object that is not a RenderBox.\n' |
| 'Instead of being a subtype of RenderBox, the render object associated ' |
| 'with this element is a ${renderObject.runtimeType}. If this type of ' |
| 'render object does have a size, consider calling findRenderObject ' |
| 'and extracting its size manually.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| 'The associated render object was:\n' |
| ' ${renderObject.toStringShallow(joiner: "\n ")}' |
| ); |
| } |
| final RenderBox box = renderObject; |
| if (!box.hasSize) { |
| throw new FlutterError( |
| 'Cannot get size from a render object that has not been through layout.\n' |
| 'The size of this render object has not yet been determined because ' |
| 'this render object has not yet been through layout, which typically ' |
| 'means that the size getter was called too early in the pipeline ' |
| '(e.g., during the build phase) before the framework has determined ' |
| 'the size and position of the render objects during layout.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| 'The render object from which the size was to be obtained was:\n' |
| ' ${box.toStringShallow(joiner: "\n ")}' |
| ); |
| } |
| if (box.debugNeedsLayout) { |
| throw new FlutterError( |
| 'Cannot get size from a render object that has been marked dirty for layout.\n' |
| 'The size of this render object is ambiguous because this render object has ' |
| 'been modified since it was last laid out, which typically means that the size ' |
| 'getter was called too early in the pipeline (e.g., during the build phase) ' |
| 'before the framework has determined the size and position of the render ' |
| 'objects during layout.\n' |
| 'The size getter was called for the following element:\n' |
| ' $this\n' |
| 'The render object from which the size was to be obtained was:\n' |
| ' ${box.toStringShallow(joiner: "\n ")}\n' |
| 'Consider using debugPrintMarkNeedsLayoutStacks to determine why the render ' |
| 'object in question is dirty, if you did not expect this.' |
| ); |
| } |
| return true; |
| }()); |
| if (renderObject is RenderBox) |
| return renderObject.size; |
| return null; |
| } |
| |
| Map<Type, InheritedElement> _inheritedWidgets; |
| Set<InheritedElement> _dependencies; |
| bool _hadUnsatisfiedDependencies = false; |
| |
| bool _debugCheckStateIsActiveForAncestorLookup() { |
| assert(() { |
| if (_debugLifecycleState != _ElementLifecycle.active) { |
| throw new FlutterError( |
| 'Looking up a deactivated widget\'s ancestor is unsafe.\n' |
| 'At this point the state of the widget\'s element tree is no longer ' |
| 'stable. To safely refer to a widget\'s ancestor in its dispose() method, ' |
| 'save a reference to the ancestor by calling inheritFromWidgetOfExactType() ' |
| 'in the widget\'s didChangeDependencies() method.\n' |
| ); |
| } |
| return true; |
| }()); |
| return true; |
| } |
| |
| @override |
| InheritedWidget inheritFromWidgetOfExactType(Type targetType) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType]; |
| if (ancestor != null) { |
| assert(ancestor is InheritedElement); |
| _dependencies ??= new HashSet<InheritedElement>(); |
| _dependencies.add(ancestor); |
| ancestor._dependents.add(this); |
| return ancestor.widget; |
| } |
| _hadUnsatisfiedDependencies = true; |
| return null; |
| } |
| |
| @override |
| InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType]; |
| return ancestor; |
| } |
| |
| void _updateInheritance() { |
| assert(_active); |
| _inheritedWidgets = _parent?._inheritedWidgets; |
| } |
| |
| @override |
| Widget ancestorWidgetOfExactType(Type targetType) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| Element ancestor = _parent; |
| while (ancestor != null && ancestor.widget.runtimeType != targetType) |
| ancestor = ancestor._parent; |
| return ancestor?.widget; |
| } |
| |
| @override |
| State ancestorStateOfType(TypeMatcher matcher) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| Element ancestor = _parent; |
| while (ancestor != null) { |
| if (ancestor is StatefulElement && matcher.check(ancestor.state)) |
| break; |
| ancestor = ancestor._parent; |
| } |
| final StatefulElement statefulAncestor = ancestor; |
| return statefulAncestor?.state; |
| } |
| |
| @override |
| State rootAncestorStateOfType(TypeMatcher matcher) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| Element ancestor = _parent; |
| StatefulElement statefulAncestor; |
| while (ancestor != null) { |
| if (ancestor is StatefulElement && matcher.check(ancestor.state)) |
| statefulAncestor = ancestor; |
| ancestor = ancestor._parent; |
| } |
| return statefulAncestor?.state; |
| } |
| |
| @override |
| RenderObject ancestorRenderObjectOfType(TypeMatcher matcher) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| Element ancestor = _parent; |
| while (ancestor != null) { |
| if (ancestor is RenderObjectElement && matcher.check(ancestor.renderObject)) |
| break; |
| ancestor = ancestor._parent; |
| } |
| final RenderObjectElement renderObjectAncestor = ancestor; |
| return renderObjectAncestor?.renderObject; |
| } |
| |
| @override |
| void visitAncestorElements(bool visitor(Element element)) { |
| assert(_debugCheckStateIsActiveForAncestorLookup()); |
| Element ancestor = _parent; |
| while (ancestor != null && visitor(ancestor)) |
| ancestor = ancestor._parent; |
| } |
| |
| /// Called when a dependency of this element changes. |
| /// |
| /// The [inheritFromWidgetOfExactType] registers this element as depending on |
| /// inherited information of the given type. When the information of that type |
| /// changes at this location in the tree (e.g., because the [InheritedElement] |
| /// updated to a new [InheritedWidget] and |
| /// [InheritedWidget.updateShouldNotify] returned true), the framework calls |
| /// this function to notify this element of the change. |
| @mustCallSuper |
| void didChangeDependencies() { |
| assert(_active); // otherwise markNeedsBuild is a no-op |
| assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies')); |
| markNeedsBuild(); |
| } |
| |
| bool _debugCheckOwnerBuildTargetExists(String methodName) { |
| assert(() { |
| if (owner._debugCurrentBuildTarget == null) { |
| throw new FlutterError( |
| '$methodName for ${widget.runtimeType} was called at an ' |
| 'inappropriate time.\n' |
| 'It may only be called while the widgets are being built. A possible ' |
| 'cause of this error is when $methodName is called during ' |
| 'one of:\n' |
| ' * network I/O event\n' |
| ' * file I/O event\n' |
| ' * timer\n' |
| ' * microtask (caused by Future.then, async/await, scheduleMicrotask)' |
| ); |
| } |
| return true; |
| }()); |
| return true; |
| } |
| |
| /// Returns a description of what caused this element to be created. |
| /// |
| /// Useful for debugging the source of an element. |
| String debugGetCreatorChain(int limit) { |
| final List<String> chain = <String>[]; |
| Element node = this; |
| while (chain.length < limit && node != null) { |
| chain.add(node.toStringShort()); |
| |