From bfced97d086523a0c7cc75e0969609d2aab5cefa Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 20 Sep 2016 15:59:30 -0700 Subject: [PATCH] Add documentation for Element (#5958) --- .../flutter/lib/src/widgets/framework.dart | 183 +++++++++++++++--- 1 file changed, 155 insertions(+), 28 deletions(-) diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index c48a58b144..44732aa02b 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -391,11 +391,15 @@ abstract class Widget { return '$name(${data.join("; ")})'; } - /// Accumulates a list of strings describing the current widget's fields, one - /// field per string. + /// Add additional information to the given description for use by [toString]. /// - /// Subclasses should override this to have their information included in - /// [toString]. + /// This method makes it easier for subclasses to coordinate to provide a + /// high-quality [toString] implementation. The [toString] implementation on + /// the [State] base class calls [debugFillDescription] to collect useful + /// information from subclasses to incorporate into its return value. + /// + /// If you override this, make sure to start your method with a call to + /// `super.debugFillDescription(description)`. @protected @mustCallSuper void debugFillDescription(List description) { } @@ -923,7 +927,7 @@ abstract class State { @protected Widget build(BuildContext context); - /// Called when a dependencies of this [State] object changes. + /// 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 @@ -956,7 +960,7 @@ abstract class State { /// information from subclasses to incorporate into its return value. /// /// If you override this, make sure to start your method with a call to - /// super.debugFillDescription(description). + /// `super.debugFillDescription(description)`. @protected @mustCallSuper void debugFillDescription(List description) { @@ -1696,12 +1700,60 @@ class BuildOwner { } } -/// Elements are the instantiations of Widget configurations. +/// An instantiation of a [Widget] at a particular location in the tree. /// -/// Elements can, in principle, have children. Only subclasses of -/// RenderObjectElement are allowed to have more than one child. +/// 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 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 implements BuildContext { /// Creates an element that instantiates the given widget. + /// + /// Typically called by an override of [Widget.createElement]. Element(Widget widget) : _widget = widget { assert(widget != null); } @@ -1726,7 +1778,7 @@ abstract class Element implements BuildContext { Widget get widget => _widget; Widget _widget; - /// The owner for this node (null if unattached). + /// The object that manages the lifecycle of this element. BuildOwner get owner => _owner; BuildOwner _owner; @@ -1781,7 +1833,7 @@ abstract class Element implements BuildContext { /// Wrapper around visitChildren for BuildContext. @override - void visitChildElements(void visitor(Element element)) { + void visitChildElements(ElementVisitor visitor) { // don't allow visitChildElements() during build, since children aren't necessarily built yet assert(owner == null || !owner._debugStateLocked); visitChildren(visitor); @@ -1845,10 +1897,15 @@ abstract class Element implements BuildContext { return inflateWidget(newWidget, newSlot); } - /// Called when an Element is given a new parent shortly after having been - /// created. Use this to initialize state that depends on having a parent. For - /// state that is independent of the position in the tree, it's better to just - /// initialize the Element in the constructor. + /// 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 contructor. + /// + /// 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); @@ -1872,7 +1929,13 @@ abstract class Element implements BuildContext { assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }); } - /// Called when an Element receives a new configuration widget. + /// 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(Widget newWidget) { assert(_debugLifecycleState == _ElementLifecycle.active); @@ -1885,9 +1948,11 @@ abstract class Element implements BuildContext { _widget = newWidget; } - /// Called by MultiChildRenderObjectElement, and other RenderObjectElement - /// subclasses that have multiple children, to update the slot of a particular - /// child when the child is moved in its child list. + /// 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); @@ -1968,6 +2033,19 @@ abstract class Element implements BuildContext { 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); @@ -2001,6 +2079,13 @@ abstract class Element implements BuildContext { }); } + /// Move the given element to the list of inactive elements. + /// + /// 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. + /// + /// The caller is responsible from removing the child from its child model. @protected void deactivateChild(Element child) { assert(child != null); @@ -2038,8 +2123,14 @@ abstract class Element implements BuildContext { element.visitChildren(_activateRecursively); } - /// Called when a previously de-activated widget (see [deactivate]) is reused - /// instead of being unmounted (see [unmount]). + /// 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); @@ -2055,8 +2146,16 @@ abstract class Element implements BuildContext { assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }); } - // TODO(ianh): Define activation/deactivation thoroughly (other methods point - // here for details). + /// 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. + /// + /// See the lifecycle documentation for [Element] for additional information. @mustCallSuper void deactivate() { assert(_debugLifecycleState == _ElementLifecycle.active); @@ -2084,8 +2183,17 @@ abstract class Element implements BuildContext { assert(_debugLifecycleState == _ElementLifecycle.inactive); } - /// Called when an Element is removed from the tree permanently after having - /// been deactivated (see [deactivate]). + /// 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); @@ -2157,9 +2265,6 @@ abstract class Element implements BuildContext { return renderObjectAncestor?.renderObject; } - /// Calls visitor for each ancestor element. - /// - /// Continues until visitor reaches the root or until visitor returns false. @override void visitAncestorElements(bool visitor(Element element)) { Element ancestor = _parent; @@ -2167,9 +2272,20 @@ abstract class Element implements BuildContext { 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 dependenciesChanged() { } + /// Returns a description of what caused this element to be created. + /// + /// Useful for debugging the source of an element. String debugGetCreatorChain(int limit) { List chain = []; Element node = this; @@ -2182,6 +2298,7 @@ abstract class Element implements BuildContext { return chain.join(' \u2190 '); } + /// A short, textual description of this element. String toStringShort() { return widget != null ? '${widget.toStringShort()}' : '[$runtimeType]'; } @@ -2194,6 +2311,15 @@ abstract class Element implements BuildContext { return '$name(${data.join("; ")})'; } + /// Add additional information to the given description for use by [toString]. + /// + /// This method makes it easier for subclasses to coordinate to provide a + /// high-quality [toString] implementation. The [toString] implementation on + /// the [State] base class calls [debugFillDescription] to collect useful + /// information from subclasses to incorporate into its return value. + /// + /// If you override this, make sure to start your method with a call to + /// `super.debugFillDescription(description)`. @protected @mustCallSuper void debugFillDescription(List description) { @@ -2208,6 +2334,7 @@ abstract class Element implements BuildContext { } } + /// A detailed, textual description of this element, includings its children. String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) { String result = '$prefixLineOne$this\n'; List children = [];