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

In particular, make debugVisitOnstageChildren only work in debug mode, and add documentation.
This commit is contained in:
Ian Hickson 2019-02-13 15:36:47 -08:00 committed by GitHub
parent 8de5a07c3f
commit 1d93e924b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 52 deletions

View file

@ -2465,8 +2465,11 @@ class _OffstageElement extends SingleChildRenderObjectElement {
@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
if (!widget.offstage)
super.debugVisitOnstageChildren(visitor);
assert(() {
if (!widget.offstage)
super.debugVisitOnstageChildren(visitor);
return true;
}());
}
}

View file

@ -2666,6 +2666,11 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
/// The default implementation defers to [visitChildren] and therefore treats
/// 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:
///
/// * [Offstage] widget that hides its children.
@ -2673,7 +2678,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
/// * [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);
void debugVisitOnstageChildren(ElementVisitor visitor) {
assert(() { visitChildren(visitor); return true; }());
}
/// Wrapper around [visitChildren] for [BuildContext].
@override

View file

@ -472,8 +472,11 @@ class _TheatreElement extends RenderObjectElement {
@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
if (_onstage != null)
visitor(_onstage);
assert(() {
if (_onstage != null)
visitor(_onstage);
return true;
}());
}
@override

View file

@ -1179,21 +1179,24 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
_childElements.values.where((Element child) {
final SliverMultiBoxAdaptorParentData parentData = child.renderObject.parentData;
double itemExtent;
switch (renderObject.constraints.axis) {
case Axis.horizontal:
itemExtent = child.renderObject.paintBounds.width;
break;
case Axis.vertical:
itemExtent = child.renderObject.paintBounds.height;
break;
}
return parentData.layoutOffset < renderObject.constraints.scrollOffset + renderObject.constraints.remainingPaintExtent &&
parentData.layoutOffset + itemExtent > renderObject.constraints.scrollOffset;
}).forEach(visitor);
assert(() {
_childElements.values.where((Element child) {
final SliverMultiBoxAdaptorParentData parentData = child.renderObject.parentData;
double itemExtent;
switch (renderObject.constraints.axis) {
case Axis.horizontal:
itemExtent = child.renderObject.paintBounds.width;
break;
case Axis.vertical:
itemExtent = child.renderObject.paintBounds.height;
break;
}
final SliverConstraints constraints = renderObject.constraints;
return parentData.layoutOffset < constraints.scrollOffset + constraints.remainingPaintExtent
&& parentData.layoutOffset + itemExtent > constraints.scrollOffset;
}).forEach(visitor);
return true;
}());
}
}

View file

@ -208,10 +208,13 @@ class _ViewportElement extends MultiChildRenderObjectElement {
@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
children.where((Element e) {
final RenderSliver renderSliver = e.renderObject;
return renderSliver.geometry.visible;
}).forEach(visitor);
assert(() {
children.where((Element e) {
final RenderSliver renderSliver = e.renderObject;
return renderSliver.geometry.visible;
}).forEach(visitor);
return true;
}());
}
}

View file

@ -32,8 +32,7 @@ class CommonFinders {
/// expect(find.text('Back'), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder text(String text, { bool skipOffstage = true }) => _TextFinder(text, skipOffstage: skipOffstage);
/// Looks for widgets that contain a [Text] descendant with `text`
@ -51,8 +50,7 @@ class CommonFinders {
/// tester.tap(find.widgetWithText(Button, 'Update'));
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder widgetWithText(Type widgetType, String text, { bool skipOffstage = true }) {
return find.ancestor(
of: find.text(text, skipOffstage: skipOffstage),
@ -68,8 +66,7 @@ class CommonFinders {
/// expect(find.byKey(backKey), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byKey(Key key, { bool skipOffstage = true }) => _KeyFinder(key, skipOffstage: skipOffstage);
/// Finds widgets by searching for widgets with a particular type.
@ -86,8 +83,7 @@ class CommonFinders {
/// expect(find.byType(IconButton), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byType(Type type, { bool skipOffstage = true }) => _WidgetTypeFinder(type, skipOffstage: skipOffstage);
/// Finds [Icon] widgets containing icon data equal to the `icon`
@ -99,8 +95,7 @@ class CommonFinders {
/// expect(find.byIcon(Icons.inbox), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byIcon(IconData icon, { bool skipOffstage = true }) => _WidgetIconFinder(icon, skipOffstage: skipOffstage);
/// Looks for widgets that contain an [Icon] descendant displaying [IconData]
@ -118,8 +113,7 @@ class CommonFinders {
/// tester.tap(find.widgetWithIcon(Button, Icons.arrow_forward));
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder widgetWithIcon(Type widgetType, IconData icon, { bool skipOffstage = true }) {
return find.ancestor(
of: find.byIcon(icon),
@ -141,8 +135,7 @@ class CommonFinders {
/// expect(find.byElementType(SingleChildRenderObjectElement), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byElementType(Type type, { bool skipOffstage = true }) => _ElementTypeFinder(type, skipOffstage: skipOffstage);
/// Finds widgets whose current widget is the instance given by the
@ -160,8 +153,7 @@ class CommonFinders {
/// tester.tap(find.byWidget(myButton));
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byWidget(Widget widget, { bool skipOffstage = true }) => _WidgetFinder(widget, skipOffstage: skipOffstage);
/// Finds widgets using a widget [predicate].
@ -180,8 +172,7 @@ class CommonFinders {
/// fails to locate the desired widget. Otherwise, the description prints the
/// signature of the predicate function.
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byWidgetPredicate(WidgetPredicate predicate, { String description, bool skipOffstage = true }) {
return _WidgetPredicateFinder(predicate, description: description, skipOffstage: skipOffstage);
}
@ -194,8 +185,7 @@ class CommonFinders {
/// expect(find.byTooltip('Back'), findsOneWidget);
/// ```
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byTooltip(String message, { bool skipOffstage = true }) {
return byWidgetPredicate(
(Widget widget) => widget is Tooltip && widget.message == message,
@ -222,8 +212,7 @@ class CommonFinders {
/// fails to locate the desired widget. Otherwise, the description prints the
/// signature of the predicate function.
///
/// If the `skipOffstage` argument is true (the default), then this skips
/// nodes that are [Offstage] or that are from inactive [Route]s.
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder byElementPredicate(ElementPredicate predicate, { String description, bool skipOffstage = true }) {
return _ElementPredicateFinder(predicate, description: description, skipOffstage: skipOffstage);
}
@ -242,9 +231,13 @@ class CommonFinders {
/// If the [matchRoot] argument is true then the widget(s) specified by [of]
/// will be matched along with the descendants.
///
/// If the [skipOffstage] argument is true (the default), then nodes that are
/// [Offstage] or that are from inactive [Route]s are skipped.
Finder descendant({ Finder of, Finder matching, bool matchRoot = false, bool skipOffstage = true }) {
/// The `skipOffstage` argument maps to [Finder.skipOffstage] (q.v.).
Finder descendant({
@required Finder of,
@required Finder matching,
bool matchRoot = false,
bool skipOffstage = true,
}) {
return _DescendantFinder(of, matching, matchRoot: matchRoot, skipOffstage: skipOffstage);
}
@ -296,9 +289,25 @@ abstract class Finder {
/// Whether this finder skips nodes that are offstage.
///
/// If this is true, then the elements are walked using
/// [Element.debugVisitOnstageChildren]. This skips offstage children of
/// [Offstage] widgets, as well as children of inactive [Route]s.
/// By default, finders skip nodes that are considered "offstage". The term is
/// very loosely defined and is only meaningful in a debug environment; it is
/// 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;
/// Returns all the [Element]s that will be considered by this finder.