Unverified Commit 1d93e924 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clean up matters related to "offstageness". (#27425)

In particular, make debugVisitOnstageChildren only work in debug mode, and add documentation.
parent 8de5a07c
...@@ -2465,8 +2465,11 @@ class _OffstageElement extends SingleChildRenderObjectElement { ...@@ -2465,8 +2465,11 @@ class _OffstageElement extends SingleChildRenderObjectElement {
@override @override
void debugVisitOnstageChildren(ElementVisitor visitor) { void debugVisitOnstageChildren(ElementVisitor visitor) {
if (!widget.offstage) assert(() {
super.debugVisitOnstageChildren(visitor); if (!widget.offstage)
super.debugVisitOnstageChildren(visitor);
return true;
}());
} }
} }
......
...@@ -2666,6 +2666,11 @@ abstract class Element extends DiagnosticableTree implements BuildContext { ...@@ -2666,6 +2666,11 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
/// The default implementation defers to [visitChildren] and therefore treats /// The default implementation defers to [visitChildren] and therefore treats
/// the element as onstage. /// the element as onstage.
/// ///
/// This method should only be used in tests and debug code. In production,
/// the vague concept of "onstage" vs "offstage" is meaningless and should not
/// be used to affect widget behavior; this method does nothing in release
/// builds.
///
/// See also: /// See also:
/// ///
/// * [Offstage] widget that hides its children. /// * [Offstage] widget that hides its children.
...@@ -2673,7 +2678,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext { ...@@ -2673,7 +2678,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
/// * [RenderObject.visitChildrenForSemantics], in contrast to this method, /// * [RenderObject.visitChildrenForSemantics], in contrast to this method,
/// designed specifically for excluding parts of the UI from the semantics /// designed specifically for excluding parts of the UI from the semantics
/// tree. /// tree.
void debugVisitOnstageChildren(ElementVisitor visitor) => visitChildren(visitor); void debugVisitOnstageChildren(ElementVisitor visitor) {
assert(() { visitChildren(visitor); return true; }());
}
/// Wrapper around [visitChildren] for [BuildContext]. /// Wrapper around [visitChildren] for [BuildContext].
@override @override
......
...@@ -472,8 +472,11 @@ class _TheatreElement extends RenderObjectElement { ...@@ -472,8 +472,11 @@ class _TheatreElement extends RenderObjectElement {
@override @override
void debugVisitOnstageChildren(ElementVisitor visitor) { void debugVisitOnstageChildren(ElementVisitor visitor) {
if (_onstage != null) assert(() {
visitor(_onstage); if (_onstage != null)
visitor(_onstage);
return true;
}());
} }
@override @override
......
...@@ -1179,21 +1179,24 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render ...@@ -1179,21 +1179,24 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
@override @override
void debugVisitOnstageChildren(ElementVisitor visitor) { void debugVisitOnstageChildren(ElementVisitor visitor) {
_childElements.values.where((Element child) { assert(() {
final SliverMultiBoxAdaptorParentData parentData = child.renderObject.parentData; _childElements.values.where((Element child) {
double itemExtent; final SliverMultiBoxAdaptorParentData parentData = child.renderObject.parentData;
switch (renderObject.constraints.axis) { double itemExtent;
case Axis.horizontal: switch (renderObject.constraints.axis) {
itemExtent = child.renderObject.paintBounds.width; case Axis.horizontal:
break; itemExtent = child.renderObject.paintBounds.width;
case Axis.vertical: break;
itemExtent = child.renderObject.paintBounds.height; case Axis.vertical:
break; itemExtent = child.renderObject.paintBounds.height;
} break;
}
return parentData.layoutOffset < renderObject.constraints.scrollOffset + renderObject.constraints.remainingPaintExtent && final SliverConstraints constraints = renderObject.constraints;
parentData.layoutOffset + itemExtent > renderObject.constraints.scrollOffset; return parentData.layoutOffset < constraints.scrollOffset + constraints.remainingPaintExtent
}).forEach(visitor); && parentData.layoutOffset + itemExtent > constraints.scrollOffset;
}).forEach(visitor);
return true;
}());
} }
} }
......
...@@ -208,10 +208,13 @@ class _ViewportElement extends MultiChildRenderObjectElement { ...@@ -208,10 +208,13 @@ class _ViewportElement extends MultiChildRenderObjectElement {
@override @override
void debugVisitOnstageChildren(ElementVisitor visitor) { void debugVisitOnstageChildren(ElementVisitor visitor) {
children.where((Element e) { assert(() {
final RenderSliver renderSliver = e.renderObject; children.where((Element e) {
return renderSliver.geometry.visible; final RenderSliver renderSliver = e.renderObject;
}).forEach(visitor); return renderSliver.geometry.visible;
}).forEach(visitor);
return true;
}());
} }
} }
......
...@@ -32,8 +32,7 @@ class CommonFinders { ...@@ -32,8 +32,7 @@ class CommonFinders {
/// expect(find.text('Back'), findsOneWidget); /// expect(find.text('Back'), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder text(String text, { bool skipOffstage = true }) => _TextFinder(text, skipOffstage: skipOffstage); Finder text(String text, { bool skipOffstage = true }) => _TextFinder(text, skipOffstage: skipOffstage);
/// Looks for widgets that contain a [Text] descendant with `text` /// Looks for widgets that contain a [Text] descendant with `text`
...@@ -51,8 +50,7 @@ class CommonFinders { ...@@ -51,8 +50,7 @@ class CommonFinders {
/// tester.tap(find.widgetWithText(Button, 'Update')); /// tester.tap(find.widgetWithText(Button, 'Update'));
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder widgetWithText(Type widgetType, String text, { bool skipOffstage = true }) { Finder widgetWithText(Type widgetType, String text, { bool skipOffstage = true }) {
return find.ancestor( return find.ancestor(
of: find.text(text, skipOffstage: skipOffstage), of: find.text(text, skipOffstage: skipOffstage),
...@@ -68,8 +66,7 @@ class CommonFinders { ...@@ -68,8 +66,7 @@ class CommonFinders {
/// expect(find.byKey(backKey), findsOneWidget); /// expect(find.byKey(backKey), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byKey(Key key, { bool skipOffstage = true }) => _KeyFinder(key, skipOffstage: skipOffstage); Finder byKey(Key key, { bool skipOffstage = true }) => _KeyFinder(key, skipOffstage: skipOffstage);
/// Finds widgets by searching for widgets with a particular type. /// Finds widgets by searching for widgets with a particular type.
...@@ -86,8 +83,7 @@ class CommonFinders { ...@@ -86,8 +83,7 @@ class CommonFinders {
/// expect(find.byType(IconButton), findsOneWidget); /// expect(find.byType(IconButton), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byType(Type type, { bool skipOffstage = true }) => _WidgetTypeFinder(type, skipOffstage: skipOffstage); Finder byType(Type type, { bool skipOffstage = true }) => _WidgetTypeFinder(type, skipOffstage: skipOffstage);
/// Finds [Icon] widgets containing icon data equal to the `icon` /// Finds [Icon] widgets containing icon data equal to the `icon`
...@@ -99,8 +95,7 @@ class CommonFinders { ...@@ -99,8 +95,7 @@ class CommonFinders {
/// expect(find.byIcon(Icons.inbox), findsOneWidget); /// expect(find.byIcon(Icons.inbox), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byIcon(IconData icon, { bool skipOffstage = true }) => _WidgetIconFinder(icon, skipOffstage: skipOffstage); Finder byIcon(IconData icon, { bool skipOffstage = true }) => _WidgetIconFinder(icon, skipOffstage: skipOffstage);
/// Looks for widgets that contain an [Icon] descendant displaying [IconData] /// Looks for widgets that contain an [Icon] descendant displaying [IconData]
...@@ -118,8 +113,7 @@ class CommonFinders { ...@@ -118,8 +113,7 @@ class CommonFinders {
/// tester.tap(find.widgetWithIcon(Button, Icons.arrow_forward)); /// tester.tap(find.widgetWithIcon(Button, Icons.arrow_forward));
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder widgetWithIcon(Type widgetType, IconData icon, { bool skipOffstage = true }) { Finder widgetWithIcon(Type widgetType, IconData icon, { bool skipOffstage = true }) {
return find.ancestor( return find.ancestor(
of: find.byIcon(icon), of: find.byIcon(icon),
...@@ -141,8 +135,7 @@ class CommonFinders { ...@@ -141,8 +135,7 @@ class CommonFinders {
/// expect(find.byElementType(SingleChildRenderObjectElement), findsOneWidget); /// expect(find.byElementType(SingleChildRenderObjectElement), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byElementType(Type type, { bool skipOffstage = true }) => _ElementTypeFinder(type, skipOffstage: skipOffstage); Finder byElementType(Type type, { bool skipOffstage = true }) => _ElementTypeFinder(type, skipOffstage: skipOffstage);
/// Finds widgets whose current widget is the instance given by the /// Finds widgets whose current widget is the instance given by the
...@@ -160,8 +153,7 @@ class CommonFinders { ...@@ -160,8 +153,7 @@ class CommonFinders {
/// tester.tap(find.byWidget(myButton)); /// tester.tap(find.byWidget(myButton));
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byWidget(Widget widget, { bool skipOffstage = true }) => _WidgetFinder(widget, skipOffstage: skipOffstage); Finder byWidget(Widget widget, { bool skipOffstage = true }) => _WidgetFinder(widget, skipOffstage: skipOffstage);
/// Finds widgets using a widget [predicate]. /// Finds widgets using a widget [predicate].
...@@ -180,8 +172,7 @@ class CommonFinders { ...@@ -180,8 +172,7 @@ class CommonFinders {
/// fails to locate the desired widget. Otherwise, the description prints the /// fails to locate the desired widget. Otherwise, the description prints the
/// signature of the predicate function. /// signature of the predicate function.
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byWidgetPredicate(WidgetPredicate predicate, { String description, bool skipOffstage = true }) { Finder byWidgetPredicate(WidgetPredicate predicate, { String description, bool skipOffstage = true }) {
return _WidgetPredicateFinder(predicate, description: description, skipOffstage: skipOffstage); return _WidgetPredicateFinder(predicate, description: description, skipOffstage: skipOffstage);
} }
...@@ -194,8 +185,7 @@ class CommonFinders { ...@@ -194,8 +185,7 @@ class CommonFinders {
/// expect(find.byTooltip('Back'), findsOneWidget); /// expect(find.byTooltip('Back'), findsOneWidget);
/// ``` /// ```
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byTooltip(String message, { bool skipOffstage = true }) { Finder byTooltip(String message, { bool skipOffstage = true }) {
return byWidgetPredicate( return byWidgetPredicate(
(Widget widget) => widget is Tooltip && widget.message == message, (Widget widget) => widget is Tooltip && widget.message == message,
...@@ -222,8 +212,7 @@ class CommonFinders { ...@@ -222,8 +212,7 @@ class CommonFinders {
/// fails to locate the desired widget. Otherwise, the description prints the /// fails to locate the desired widget. Otherwise, the description prints the
/// signature of the predicate function. /// signature of the predicate function.
/// ///
/// If the `skipOffstage` argument is true (the default), then this skips /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder byElementPredicate(ElementPredicate predicate, { String description, bool skipOffstage = true }) { Finder byElementPredicate(ElementPredicate predicate, { String description, bool skipOffstage = true }) {
return _ElementPredicateFinder(predicate, description: description, skipOffstage: skipOffstage); return _ElementPredicateFinder(predicate, description: description, skipOffstage: skipOffstage);
} }
...@@ -242,9 +231,13 @@ class CommonFinders { ...@@ -242,9 +231,13 @@ class CommonFinders {
/// If the [matchRoot] argument is true then the widget(s) specified by [of] /// If the [matchRoot] argument is true then the widget(s) specified by [of]
/// will be matched along with the descendants. /// will be matched along with the descendants.
/// ///
/// If the [skipOffstage] argument is true (the default), then nodes that are /// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
/// [Offstage] or that are from inactive [Route]s are skipped. Finder descendant({
Finder descendant({ Finder of, Finder matching, bool matchRoot = false, bool skipOffstage = true }) { @required Finder of,
@required Finder matching,
bool matchRoot = false,
bool skipOffstage = true,
}) {
return _DescendantFinder(of, matching, matchRoot: matchRoot, skipOffstage: skipOffstage); return _DescendantFinder(of, matching, matchRoot: matchRoot, skipOffstage: skipOffstage);
} }
...@@ -296,9 +289,25 @@ abstract class Finder { ...@@ -296,9 +289,25 @@ abstract class Finder {
/// Whether this finder skips nodes that are offstage. /// Whether this finder skips nodes that are offstage.
/// ///
/// If this is true, then the elements are walked using /// By default, finders skip nodes that are considered "offstage". The term is
/// [Element.debugVisitOnstageChildren]. This skips offstage children of /// very loosely defined and is only meaningful in a debug environment; it is
/// [Offstage] widgets, as well as children of inactive [Route]s. /// not a term that applies in production. A widget is considered "offstage"
/// if it is "not really visible".
///
/// If [skipOffstage] is true, then the elements are walked using
/// [Element.debugVisitOnstageChildren]. This skips hidden children of
/// [Offstage] widgets, children of inactive [Route]s, some non-visible
/// children in viewports, and other nodes that are generally considered to be
/// "not there" when considering what the user can see.
///
/// The [skipOffstage] argument can be set to `false` to match _all_ nodes
/// regardless of this status. This is useful to test for the presence of
/// widgets being kept alive using [KeepAlive], for instance.
///
/// See also:
///
/// * [Element.debugVisitOnstageChildren], which can be overriden to
/// decide whether a child is onstage or offstage.
final bool skipOffstage; final bool skipOffstage;
/// Returns all the [Element]s that will be considered by this finder. /// Returns all the [Element]s that will be considered by this finder.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment