Implicit a11y scrolling for iOS (and caching in Viewports) (#17021)
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index d654366..f49c46f 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -2106,6 +2106,27 @@
/// that are not physically visible.
Rect describeApproximatePaintClip(covariant RenderObject child) => null;
+ /// Returns a rect in this object's coordinate system that describes
+ /// which [SemanticsNode]s produced by the `child` should be included in the
+ /// semantics tree. [SemanticsNode]s from the `child` that are positioned
+ /// outside of this rect will be dropped. Child [SemanticsNode]s that are
+ /// positioned inside this rect, but outside of [describeApproximatePaintClip]
+ /// will be included in the tree marked as hidden. Child [SemanticsNode]s
+ /// that are inside of both rect will be included in the tree as regular
+ /// nodes.
+ ///
+ /// This method only returns a non-null value if the semantics clip rect
+ /// is different from the rect returned by [describeApproximatePaintClip].
+ /// If the semantics clip rect and the paint clip rect are the same, this
+ /// method returns null.
+ ///
+ /// A viewport would typically implement this method to include semantic nodes
+ /// in the semantics tree that are currently hidden just before the leading
+ /// or just after the trailing edge. These nodes have to be included in the
+ /// semantics tree to implement implicit accessibility scrolling on iOS where
+ /// the viewport scrolls implicitly when moving the accessibility focus from
+ /// a the last visible node in the viewport to the first hidden one.
+ Rect describeSemanticsClip(covariant RenderObject child) => null;
// SEMANTICS
@@ -2279,7 +2300,10 @@
);
assert(fragment is _InterestingSemanticsFragment);
final _InterestingSemanticsFragment interestingFragment = fragment;
- final SemanticsNode node = interestingFragment.compileChildren(_semantics?.parentClipRect).single;
+ final SemanticsNode node = interestingFragment.compileChildren(
+ parentSemanticsClipRect: _semantics?.parentSemanticsClipRect,
+ parentPaintClipRect: _semantics?.parentPaintClipRect,
+ ).single;
// Fragment only wants to add this node's SemanticsNode to the parent.
assert(interestingFragment.config == null && node == _semantics);
}
@@ -3025,7 +3049,10 @@
final List<RenderObject> _ancestorChain;
/// The children to be added to the parent.
- Iterable<SemanticsNode> compileChildren(Rect parentClipRect);
+ Iterable<SemanticsNode> compileChildren({
+ @required Rect parentSemanticsClipRect,
+ @required Rect parentPaintClipRect
+ });
/// The [SemanticsConfiguration] the child wants to merge into the parent's
/// [SemanticsNode] or null if it doesn't want to merge anything.
@@ -3093,9 +3120,10 @@
}) : super(owner: owner, dropsSemanticsOfPreviousSiblings: dropsSemanticsOfPreviousSiblings);
@override
- Iterable<SemanticsNode> compileChildren(Rect parentClipRect) sync* {
+ Iterable<SemanticsNode> compileChildren({Rect parentSemanticsClipRect, Rect parentPaintClipRect}) sync* {
assert(_tagsForChildren == null || _tagsForChildren.isEmpty);
- assert(parentClipRect == null);
+ assert(parentSemanticsClipRect == null);
+ assert(parentPaintClipRect == null);
assert(_ancestorChain.length == 1);
owner._semantics ??= new SemanticsNode.root(
@@ -3104,14 +3132,18 @@
);
final SemanticsNode node = owner._semantics;
assert(MatrixUtils.matrixEquals(node.transform, new Matrix4.identity()));
- assert(node.parentClipRect == null);
+ assert(node.parentSemanticsClipRect == null);
+ assert(node.parentPaintClipRect == null);
node.rect = owner.semanticBounds;
final List<SemanticsNode> children = <SemanticsNode>[];
for (_InterestingSemanticsFragment fragment in _children) {
assert(fragment.config == null);
- children.addAll(fragment.compileChildren(parentClipRect));
+ children.addAll(fragment.compileChildren(
+ parentSemanticsClipRect: parentSemanticsClipRect,
+ parentPaintClipRect: parentPaintClipRect,
+ ));
}
node.updateWith(config: null, childrenInInversePaintOrder: children);
@@ -3171,22 +3203,22 @@
final List<_InterestingSemanticsFragment> _children = <_InterestingSemanticsFragment>[];
@override
- Iterable<SemanticsNode> compileChildren(Rect parentClipRect) sync* {
+ Iterable<SemanticsNode> compileChildren({Rect parentSemanticsClipRect, Rect parentPaintClipRect}) sync* {
if (!_isExplicit) {
owner._semantics = null;
for (_InterestingSemanticsFragment fragment in _children) {
assert(_ancestorChain.first == fragment._ancestorChain.last);
fragment._ancestorChain.addAll(_ancestorChain.sublist(1));
- yield* fragment.compileChildren(parentClipRect);
+ yield* fragment.compileChildren(parentSemanticsClipRect: parentSemanticsClipRect, parentPaintClipRect: parentPaintClipRect);
}
return;
}
final _SemanticsGeometry geometry = _needsGeometryUpdate
- ? new _SemanticsGeometry(parentClipRect: parentClipRect, ancestors: _ancestorChain)
+ ? new _SemanticsGeometry(parentSemanticsClipRect: parentSemanticsClipRect, parentPaintClipRect: parentPaintClipRect, ancestors: _ancestorChain)
: null;
- if (!_mergeIntoParent && (geometry?.isInvisible == true))
+ if (!_mergeIntoParent && (geometry?.dropFromTree == true))
return; // Drop the node, it's not going to be visible.
owner._semantics ??= new SemanticsNode(showOnScreen: owner.showOnScreen);
@@ -3199,12 +3231,17 @@
node
..rect = geometry.rect
..transform = geometry.transform
- ..parentClipRect = geometry.clipRect;
+ ..parentSemanticsClipRect = geometry.semanticsClipRect
+ ..parentPaintClipRect = geometry.paintClipRect;
+ if (!_mergeIntoParent && geometry.markAsHidden) {
+ _ensureConfigIsWritable();
+ _config.isHidden = true;
+ }
}
final List<SemanticsNode> children = <SemanticsNode>[];
for (_InterestingSemanticsFragment fragment in _children)
- children.addAll(fragment.compileChildren(node.parentClipRect));
+ children.addAll(fragment.compileChildren(parentSemanticsClipRect: node.parentSemanticsClipRect, parentPaintClipRect: node.parentPaintClipRect));
if (_config.isSemanticBoundary) {
owner.assembleSemanticsNode(node, _config, children);
@@ -3226,14 +3263,18 @@
_children.add(fragment);
if (fragment.config == null)
continue;
- if (!_isConfigWritable) {
- _config = _config.copy();
- _isConfigWritable = true;
- }
+ _ensureConfigIsWritable();
_config.absorb(fragment.config);
}
}
+ void _ensureConfigIsWritable() {
+ if (!_isConfigWritable) {
+ _config = _config.copy();
+ _isConfigWritable = true;
+ }
+ }
+
bool _isExplicit = false;
@override
@@ -3257,61 +3298,97 @@
/// (first [RenderObject] in the list) and its closest ancestor [RenderObject]
/// that also owns its own [SemanticsNode] (last [RenderObject] in the list).
_SemanticsGeometry({
- @required Rect parentClipRect,
+ @required Rect parentSemanticsClipRect,
+ @required Rect parentPaintClipRect,
@required List<RenderObject> ancestors,
}) {
- _computeValues(parentClipRect, ancestors);
+ _computeValues(parentSemanticsClipRect, parentPaintClipRect, ancestors);
}
- Rect _clipRect;
+ Rect _paintClipRect;
+ Rect _semanticsClipRect;
Matrix4 _transform;
Rect _rect;
/// Value for [SemanticsNode.transform].
Matrix4 get transform => _transform;
- /// Value for [SemanticsNode.parentClipRect].
- Rect get clipRect => _clipRect;
+ /// Value for [SemanticsNode.parentSemanticsClipRect].
+ Rect get semanticsClipRect => _semanticsClipRect;
+
+ /// Value for [SemanticsNode.parentPaintClipRect].
+ Rect get paintClipRect => _paintClipRect;
/// Value for [SemanticsNode.rect].
Rect get rect => _rect;
- void _computeValues(Rect parentClipRect, List<RenderObject> ancestors) {
+ void _computeValues(Rect parentSemanticsClipRect, Rect parentPaintClipRect, List<RenderObject> ancestors) {
assert(ancestors.length > 1);
_transform = new Matrix4.identity();
- _clipRect = parentClipRect;
+ _semanticsClipRect = parentSemanticsClipRect;
+ _paintClipRect = parentPaintClipRect;
for (int index = ancestors.length-1; index > 0; index -= 1) {
final RenderObject parent = ancestors[index];
final RenderObject child = ancestors[index-1];
- _clipRect = _intersectClipRect(parent.describeApproximatePaintClip(child));
- if (_clipRect != null) {
- if (_clipRect.isEmpty) {
- _clipRect = Rect.zero;
- } else {
- final Matrix4 clipTransform = new Matrix4.identity();
- parent.applyPaintTransform(child, clipTransform);
- _clipRect = MatrixUtils.inverseTransformRect(clipTransform, _clipRect);
- }
+ final Rect parentSemanticsClipRect = parent.describeSemanticsClip(child);
+ if (parentSemanticsClipRect != null) {
+ _semanticsClipRect = parentSemanticsClipRect;
+ _paintClipRect = _intersectRects(_paintClipRect, parent.describeApproximatePaintClip(child));
+ } else {
+ _semanticsClipRect = _intersectRects(_semanticsClipRect, parent.describeApproximatePaintClip(child));
}
+ _semanticsClipRect = _transformRect(_semanticsClipRect, parent, child);
+ _paintClipRect = _transformRect(_paintClipRect, parent, child);
parent.applyPaintTransform(child, _transform);
}
final RenderObject owner = ancestors.first;
- _rect = _clipRect == null ? owner.semanticBounds : _clipRect.intersect(owner.semanticBounds);
+ _rect = _semanticsClipRect == null ? owner.semanticBounds : _semanticsClipRect.intersect(owner.semanticBounds);
+ if (_paintClipRect != null) {
+ final Rect paintRect = _paintClipRect.intersect(_rect);
+ _markAsHidden = paintRect.isEmpty && !_rect.isEmpty;
+ if (!_markAsHidden)
+ _rect = paintRect;
+ }
}
- Rect _intersectClipRect(Rect other) {
- if (_clipRect == null)
- return other;
- if (other == null)
- return _clipRect;
- return _clipRect.intersect(other);
+ /// From parent to child coordinate system.
+ static Rect _transformRect(Rect rect, RenderObject parent, RenderObject child) {
+ if (rect == null)
+ return null;
+ if (rect.isEmpty)
+ return Rect.zero;
+ final Matrix4 transform = new Matrix4.identity();
+ parent.applyPaintTransform(child, transform);
+ return MatrixUtils.inverseTransformRect(transform, rect);
}
- /// Whether a [SemanticsNode] annotated with the geometric information tracked
- /// by this object would be visible on screen.
- bool get isInvisible {
+ static Rect _intersectRects(Rect a, Rect b) {
+ if (a == null)
+ return b;
+ if (b == null)
+ return a;
+ return a.intersect(b);
+ }
+
+ /// Whether the [SemanticsNode] annotated with the geometric information tracked
+ /// by this object can be dropped from the semantics tree without losing
+ /// semantics information.
+ bool get dropFromTree {
return _rect.isEmpty;
}
+
+ /// Whether the [SemanticsNode] annotated with the geometric information
+ /// tracked by this object should be marked as hidden because it is not
+ /// visible on screen.
+ ///
+ /// Hidden elements should still be included in the tree to work around
+ /// platform limitations (e.g. accessibility scrolling on iOS).
+ ///
+ /// See also:
+ ///
+ /// * [SemanticsFlag.isHidden] for the purpose of marking a node as hidden.
+ bool get markAsHidden => _markAsHidden;
+ bool _markAsHidden = false;
}
diff --git a/packages/flutter/lib/src/rendering/sliver.dart b/packages/flutter/lib/src/rendering/sliver.dart
index 1f65d06..1014bc7 100644
--- a/packages/flutter/lib/src/rendering/sliver.dart
+++ b/packages/flutter/lib/src/rendering/sliver.dart
@@ -104,6 +104,8 @@
@required this.crossAxisExtent,
@required this.crossAxisDirection,
@required this.viewportMainAxisExtent,
+ @required this.remainingCacheExtent,
+ @required this.cacheOrigin,
}) : assert(axisDirection != null),
assert(growthDirection != null),
assert(userScrollDirection != null),
@@ -112,7 +114,9 @@
assert(remainingPaintExtent != null),
assert(crossAxisExtent != null),
assert(crossAxisDirection != null),
- assert(viewportMainAxisExtent != null);
+ assert(viewportMainAxisExtent != null),
+ assert(remainingCacheExtent != null),
+ assert(cacheOrigin != null);
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
@@ -126,6 +130,8 @@
double crossAxisExtent,
AxisDirection crossAxisDirection,
double viewportMainAxisExtent,
+ double remainingCacheExtent,
+ double cacheOrigin,
}) {
return new SliverConstraints(
axisDirection: axisDirection ?? this.axisDirection,
@@ -137,6 +143,8 @@
crossAxisExtent: crossAxisExtent ?? this.crossAxisExtent,
crossAxisDirection: crossAxisDirection ?? this.crossAxisDirection,
viewportMainAxisExtent: viewportMainAxisExtent ?? this.viewportMainAxisExtent,
+ remainingCacheExtent: remainingCacheExtent ?? this.remainingCacheExtent,
+ cacheOrigin: cacheOrigin ?? this.cacheOrigin,
);
}
@@ -258,6 +266,47 @@
/// For a vertical list, this is the height of the viewport.
final double viewportMainAxisExtent;
+ /// Where the cache area starts relative to the [scrollOffset].
+ ///
+ /// Slivers that fall into the cache area located before the leading edge and
+ /// after the trailing edge of the viewport should still render content
+ /// because they are about to become visible when the user scrolls.
+ ///
+ /// The [cacheOrigin] describes where the [remainingCacheExtent] starts relative
+ /// to the [scrollOffset]. A cache origin of 0 means that the sliver does not
+ /// have to provide any content before the current [scrollOffset]. A
+ /// [cacheOrigin] of -250.0 means that even though the first visible part of
+ /// the sliver will be at the provided [scrollOffset], the sliver should
+ /// render content starting 250.0 before the [scrollOffset] to fill the
+ /// cache area of the viewport.
+ ///
+ /// The [cacheOrigin] is always negative or zero and will never exceed
+ /// -[scrollOffset]. In other words, a sliver is never asked to provide
+ /// content before its zero [scrollOffset].
+ ///
+ /// See also:
+ /// * [RenderViewport.cacheExtent] for a description of a viewport's cache area.
+ final double cacheOrigin;
+
+
+ /// Describes how much content the sliver should provide starting from the
+ /// [cacheOrigin].
+ ///
+ /// Not all content in the [remainingCacheExtent] will be visible as some
+ /// of it might fall into the cache area of the viewport.
+ ///
+ /// Each sliver should start laying out content at the [cacheOrigin] and
+ /// try to provide as much content as the [remainingCacheExtent] allows.
+ ///
+ /// The [remainingCacheExtent] is always larger or equal to the
+ /// [remainingPaintExtent]. Content, that falls in the [remainingCacheExtent],
+ /// but is outside of the [remainingPaintExtent] is currently not visible
+ /// in the viewport.
+ ///
+ /// See also:
+ /// * [RenderViewport.cacheExtent] for a description of a viewport's cache area.
+ final double remainingCacheExtent;
+
/// The axis along which the [scrollOffset] and [remainingPaintExtent] are measured.
Axis get axis => axisDirectionToAxis(axisDirection);
@@ -361,6 +410,8 @@
verify(axisDirectionToAxis(axisDirection) != axisDirectionToAxis(crossAxisDirection), 'The "axisDirection" and the "crossAxisDirection" are along the same axis.');
verify(viewportMainAxisExtent >= 0.0, 'The "viewportMainAxisExtent" is negative.');
verify(remainingPaintExtent >= 0.0, 'The "remainingPaintExtent" is negative.');
+ verify(remainingCacheExtent >= 0.0, 'The "remainingCacheExtent" is negative.');
+ verify(cacheOrigin <= 0.0, 'The "cacheOrigin" is positive.');
verify(isNormalized, 'The constraints are not normalized.'); // should be redundant with earlier checks
return true;
}());
@@ -382,7 +433,9 @@
&& typedOther.remainingPaintExtent == remainingPaintExtent
&& typedOther.crossAxisExtent == crossAxisExtent
&& typedOther.crossAxisDirection == crossAxisDirection
- && typedOther.viewportMainAxisExtent == viewportMainAxisExtent;
+ && typedOther.viewportMainAxisExtent == viewportMainAxisExtent
+ && typedOther.remainingCacheExtent == remainingCacheExtent
+ && typedOther.cacheOrigin == cacheOrigin;
}
@override
@@ -396,6 +449,8 @@
crossAxisExtent,
crossAxisDirection,
viewportMainAxisExtent,
+ remainingCacheExtent,
+ cacheOrigin,
);
}
@@ -408,9 +463,11 @@
'scrollOffset: ${scrollOffset.toStringAsFixed(1)}, '
'remainingPaintExtent: ${remainingPaintExtent.toStringAsFixed(1)}, ' +
(overlap != 0.0 ? 'overlap: ${overlap.toStringAsFixed(1)}, ' : '') +
- 'crossAxisExtent: ${crossAxisExtent.toStringAsFixed(1)}, ' +
- 'crossAxisDirection: $crossAxisDirection, ' +
- 'viewportMainAxisExtent: ${viewportMainAxisExtent.toStringAsFixed(1)}' +
+ 'crossAxisExtent: ${crossAxisExtent.toStringAsFixed(1)}, '
+ 'crossAxisDirection: $crossAxisDirection, '
+ 'viewportMainAxisExtent: ${viewportMainAxisExtent.toStringAsFixed(1)}, '
+ 'remainingCacheExtent: ${remainingCacheExtent.toStringAsFixed(1)} '
+ 'cacheOrigin: ${cacheOrigin.toStringAsFixed(1)} '
')';
}
}
@@ -440,6 +497,7 @@
bool visible,
this.hasVisualOverflow: false,
this.scrollOffsetCorrection,
+ double cacheExtent,
}) : assert(scrollExtent != null),
assert(paintExtent != null),
assert(paintOrigin != null),
@@ -448,6 +506,7 @@
assert(scrollOffsetCorrection != 0.0),
layoutExtent = layoutExtent ?? paintExtent,
hitTestExtent = hitTestExtent ?? paintExtent,
+ cacheExtent = cacheExtent ?? layoutExtent ?? paintExtent,
visible = visible ?? paintExtent > 0.0;
/// A sliver that occupies no space at all.
@@ -585,6 +644,18 @@
/// its offset based on this value.
final double scrollOffsetCorrection;
+ /// How many pixels the sliver has consumed in the
+ /// [SliverConstraints.remainingCacheExtent].
+ ///
+ /// This value should be equal to or larger than the [layoutExtent] because
+ /// the sliver allways consumes at least the [layoutExtent] from the
+ /// [SliverConstraints.remainingCacheExtent] and possibly more if it falls
+ /// into the cache area of the viewport.
+ ///
+ /// See also:
+ /// * [RenderViewport.cacheExtent] for a description of a viewport's cache area.
+ final double cacheExtent;
+
/// Asserts that this geometry is internally consistent.
///
/// Does nothing if asserts are disabled. Always returns true.
@@ -607,6 +678,7 @@
verify(paintOrigin != null, 'The "paintOrigin" is null.');
verify(layoutExtent != null, 'The "layoutExtent" is null.');
verify(layoutExtent >= 0.0, 'The "layoutExtent" is negative.');
+ verify(cacheExtent >= 0.0, 'The "cacheExtent" is negative.');
if (layoutExtent > paintExtent) {
verify(false,
'The "layoutExtent" exceeds the "paintExtent".\n' +
@@ -655,6 +727,7 @@
properties.add(new DoubleProperty('hitTestExtent', hitTestExtent, defaultValue: paintExtent));
properties.add(new DiagnosticsProperty<bool>('hasVisualOverflow', hasVisualOverflow, defaultValue: false));
properties.add(new DoubleProperty('scrollOffsetCorrection', scrollOffsetCorrection, defaultValue: null));
+ properties.add(new DoubleProperty('cacheExtent', cacheExtent, defaultValue: 0.0));
}
}
@@ -1123,6 +1196,22 @@
return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingPaintExtent);
}
+ /// Computes the portion of the region from `from` to `to` that is within
+ /// the cache extent of the viewport, assuming that only the region from the
+ /// [SliverConstraints.cacheOrigin] that is
+ /// [SliverConstraints.remainingCacheExtent] high is visible, and that
+ /// the relationship between scroll offsets and paint offsets is linear.
+ ///
+ /// This method is not useful if there is not a 1:1 relationship between
+ /// consumed scroll offset and consumed cache extent.
+ double calculateCacheOffset(SliverConstraints constraints, { @required double from, @required double to }) {
+ assert(from <= to);
+ final double a = constraints.scrollOffset + constraints.cacheOrigin;
+ final double b = constraints.scrollOffset + constraints.remainingCacheExtent;
+ // the clamp on the next line is to avoid floating point rounding errors
+ return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingCacheExtent);
+ }
+
/// Returns the distance from the leading _visible_ edge of the sliver to the
/// side of the given child closest to that edge.
///
@@ -1537,11 +1626,14 @@
}
assert(childExtent != null);
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: childExtent);
+ final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: childExtent);
+
assert(paintedChildSize.isFinite);
assert(paintedChildSize >= 0.0);
geometry = new SliverGeometry(
scrollExtent: childExtent,
paintExtent: paintedChildSize,
+ cacheExtent: cacheExtent,
maxPaintExtent: childExtent,
hitTestExtent: paintedChildSize,
hasVisualOverflow: childExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
diff --git a/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart b/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
index c7a41cc..e29ba36 100644
--- a/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
+++ b/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
@@ -140,11 +140,11 @@
final double itemExtent = this.itemExtent;
- final double scrollOffset = constraints.scrollOffset;
+ final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
- final double remainingPaintExtent = constraints.remainingPaintExtent;
- assert(remainingPaintExtent >= 0.0);
- final double targetEndScrollOffset = scrollOffset + remainingPaintExtent;
+ final double remainingExtent = constraints.remainingCacheExtent;
+ assert(remainingExtent >= 0.0);
+ final double targetEndScrollOffset = scrollOffset + remainingExtent;
final BoxConstraints childConstraints = constraints.asBoxConstraints(
minExtent: itemExtent,
@@ -242,9 +242,16 @@
to: trailingScrollOffset,
);
+ final double cacheExtent = calculateCacheOffset(
+ constraints,
+ from: leadingScrollOffset,
+ to: trailingScrollOffset,
+ );
+
geometry = new SliverGeometry(
scrollExtent: estimatedMaxScrollOffset,
paintExtent: paintExtent,
+ cacheExtent: cacheExtent,
maxPaintExtent: estimatedMaxScrollOffset,
// Conservative to avoid flickering away the clip during scroll.
hasVisualOverflow: (targetLastIndex != null && lastIndex >= targetLastIndex)
diff --git a/packages/flutter/lib/src/rendering/sliver_grid.dart b/packages/flutter/lib/src/rendering/sliver_grid.dart
index eae7a00..45f663c 100644
--- a/packages/flutter/lib/src/rendering/sliver_grid.dart
+++ b/packages/flutter/lib/src/rendering/sliver_grid.dart
@@ -513,11 +513,11 @@
childManager.didStartLayout();
childManager.setDidUnderflow(false);
- final double scrollOffset = constraints.scrollOffset;
+ final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
- final double remainingPaintExtent = constraints.remainingPaintExtent;
- assert(remainingPaintExtent >= 0.0);
- final double targetEndScrollOffset = scrollOffset + remainingPaintExtent;
+ final double remainingExtent = constraints.remainingCacheExtent;
+ assert(remainingExtent >= 0.0);
+ final double targetEndScrollOffset = scrollOffset + remainingExtent;
final SliverGridLayout layout = _gridDelegate.getLayout(constraints);
@@ -617,11 +617,17 @@
from: leadingScrollOffset,
to: trailingScrollOffset,
);
+ final double cacheExtent = calculateCacheOffset(
+ constraints,
+ from: leadingScrollOffset,
+ to: trailingScrollOffset,
+ );
geometry = new SliverGeometry(
scrollExtent: estimatedTotalExtent,
paintExtent: paintExtent,
maxPaintExtent: estimatedTotalExtent,
+ cacheExtent: cacheExtent,
// Conservative to avoid complexity.
hasVisualOverflow: true,
);
diff --git a/packages/flutter/lib/src/rendering/sliver_list.dart b/packages/flutter/lib/src/rendering/sliver_list.dart
index 4abff38..2fd6569 100644
--- a/packages/flutter/lib/src/rendering/sliver_list.dart
+++ b/packages/flutter/lib/src/rendering/sliver_list.dart
@@ -47,11 +47,11 @@
childManager.didStartLayout();
childManager.setDidUnderflow(false);
- final double scrollOffset = constraints.scrollOffset;
+ final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
- final double remainingPaintExtent = constraints.remainingPaintExtent;
- assert(remainingPaintExtent >= 0.0);
- final double targetEndScrollOffset = scrollOffset + remainingPaintExtent;
+ final double remainingExtent = constraints.remainingCacheExtent;
+ assert(remainingExtent >= 0.0);
+ final double targetEndScrollOffset = scrollOffset + remainingExtent;
final BoxConstraints childConstraints = constraints.asBoxConstraints();
int leadingGarbage = 0;
int trailingGarbage = 0;
@@ -269,9 +269,15 @@
from: childScrollOffset(firstChild),
to: endScrollOffset,
);
+ final double cacheExtent = calculateCacheOffset(
+ constraints,
+ from: childScrollOffset(firstChild),
+ to: endScrollOffset,
+ );
geometry = new SliverGeometry(
scrollExtent: estimatedMaxScrollOffset,
paintExtent: paintExtent,
+ cacheExtent: cacheExtent,
maxPaintExtent: estimatedMaxScrollOffset,
// Conservative to avoid flickering away the clip during scroll.
hasVisualOverflow: endScrollOffset > targetEndScrollOffset || constraints.scrollOffset > 0.0,
diff --git a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
index 8663074..a4f0ec6 100644
--- a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
+++ b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
@@ -298,36 +298,6 @@
_keepAliveBucket.values.forEach(visitor);
}
- @override
- void visitChildrenForSemantics(RenderObjectVisitor visitor) {
- switch (constraints.normalizedGrowthDirection) {
- case GrowthDirection.forward:
- super.visitChildrenForSemantics((RenderObject child) {
- // The sliver is overlapped at the leading edge; check if trailing edge is visible.
- final Offset bottomRightInViewport = MatrixUtils.transformPoint(
- child.getTransformTo(parent), child.semanticBounds.bottomRight
- );
- final double endOverlap = constraints.overlap;
- if ((constraints.axis == Axis.vertical && bottomRightInViewport.dy > endOverlap) ||
- (constraints.axis == Axis.horizontal && bottomRightInViewport.dx > endOverlap))
- visitor(child);
- });
- break;
- case GrowthDirection.reverse:
- super.visitChildrenForSemantics((RenderObject child) {
- // The sliver is overlapped at the trailing edge; check if leading edge is visible.
- final Offset topLeftInViewport = MatrixUtils.transformPoint(
- child.getTransformTo(parent), child.semanticBounds.topLeft
- );
- final double startOverlap = constraints.remainingPaintExtent - constraints.overlap;
- if ((constraints.axis == Axis.vertical && topLeftInViewport.dy < startOverlap) ||
- (constraints.axis == Axis.horizontal && topLeftInViewport.dx < startOverlap))
- visitor(child);
- });
- break;
- }
- }
-
/// Called during layout to create and add the child with the given index and
/// scroll offset.
///
diff --git a/packages/flutter/lib/src/rendering/sliver_padding.dart b/packages/flutter/lib/src/rendering/sliver_padding.dart
index 0220715..fb0e7ee 100644
--- a/packages/flutter/lib/src/rendering/sliver_padding.dart
+++ b/packages/flutter/lib/src/rendering/sliver_padding.dart
@@ -182,8 +182,10 @@
child.layout(
constraints.copyWith(
scrollOffset: math.max(0.0, constraints.scrollOffset - beforePadding),
+ cacheOrigin: math.min(0.0, constraints.cacheOrigin + beforePadding),
overlap: 0.0,
remainingPaintExtent: constraints.remainingPaintExtent - calculatePaintOffset(constraints, from: 0.0, to: beforePadding),
+ remainingCacheExtent: constraints.remainingCacheExtent - calculateCacheOffset(constraints, from: 0.0, to: beforePadding),
crossAxisExtent: math.max(0.0, constraints.crossAxisExtent - crossAxisPadding),
),
parentUsesSize: true,
@@ -206,6 +208,17 @@
to: mainAxisPadding + childLayoutGeometry.scrollExtent,
);
final double mainAxisPaddingPaintExtent = beforePaddingPaintExtent + afterPaddingPaintExtent;
+ final double beforePaddingCacheExtent = calculateCacheOffset(
+ constraints,
+ from: 0.0,
+ to: beforePadding,
+ );
+ final double afterPaddingCacheExtent = calculateCacheOffset(
+ constraints,
+ from: beforePadding + childLayoutGeometry.scrollExtent,
+ to: mainAxisPadding + childLayoutGeometry.scrollExtent,
+ );
+ final double mainAxisPaddingCacheExtent = afterPaddingCacheExtent + beforePaddingCacheExtent;
final double paintExtent = math.min(
beforePaddingPaintExtent + math.max(childLayoutGeometry.paintExtent, childLayoutGeometry.layoutExtent + afterPaddingPaintExtent),
constraints.remainingPaintExtent,
@@ -214,6 +227,7 @@
scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent,
paintExtent: paintExtent,
layoutExtent: math.min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, paintExtent),
+ cacheExtent: math.min(mainAxisPaddingCacheExtent + childLayoutGeometry.cacheExtent, constraints.remainingCacheExtent),
maxPaintExtent: mainAxisPadding + childLayoutGeometry.maxPaintExtent,
hitTestExtent: math.max(
mainAxisPaddingPaintExtent + childLayoutGeometry.paintExtent,
diff --git a/packages/flutter/lib/src/rendering/sliver_persistent_header.dart b/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
index b6ac9a6..325fa93 100644
--- a/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
+++ b/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
@@ -292,13 +292,15 @@
final bool overlapsContent = constraints.overlap > 0.0;
excludeFromSemanticsScrolling = overlapsContent || (constraints.scrollOffset > maxExtent - minExtent);
layoutChild(constraints.scrollOffset, maxExtent, overlapsContent: overlapsContent);
+ final double layoutExtent = (maxExtent - constraints.scrollOffset).clamp(0.0, constraints.remainingPaintExtent);
geometry = new SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: constraints.overlap,
paintExtent: math.min(childExtent, constraints.remainingPaintExtent),
- layoutExtent: (maxExtent - constraints.scrollOffset).clamp(0.0, constraints.remainingPaintExtent),
+ layoutExtent: layoutExtent,
maxPaintExtent: maxExtent,
maxScrollObstructionExtent: minExtent,
+ cacheExtent: layoutExtent > 0.0 ? -constraints.cacheOrigin + layoutExtent : layoutExtent,
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
);
}
diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart
index b236860..6d943ce 100644
--- a/packages/flutter/lib/src/rendering/viewport.dart
+++ b/packages/flutter/lib/src/rendering/viewport.dart
@@ -53,6 +53,14 @@
/// descendant of the viewport and there must not be any other
/// [RenderAbstractViewport] objects between the target and this object.
double getOffsetToReveal(RenderObject target, double alignment);
+
+ /// The default value for the cache extent of the viewport.
+ ///
+ /// See also:
+ ///
+ /// * [RenderViewportBase.cacheExtent] for a definition of the cache extent.
+ @protected
+ static const double defaultCacheExtent = 250.0;
}
/// A base class for render objects that are bigger on the inside.
@@ -82,13 +90,15 @@
AxisDirection axisDirection: AxisDirection.down,
@required AxisDirection crossAxisDirection,
@required ViewportOffset offset,
+ double cacheExtent,
}) : assert(axisDirection != null),
assert(crossAxisDirection != null),
assert(offset != null),
assert(axisDirectionToAxis(axisDirection) != axisDirectionToAxis(crossAxisDirection)),
_axisDirection = axisDirection,
_crossAxisDirection = crossAxisDirection,
- _offset = offset;
+ _offset = offset,
+ _cacheExtent = cacheExtent ?? RenderAbstractViewport.defaultCacheExtent;
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
@@ -99,11 +109,9 @@
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
- for (RenderSliver sliver in childrenInPaintOrder) {
- if (sliver.geometry.paintExtent != 0 &&
- sliver.constraints.overlap < sliver.geometry.paintOrigin + sliver.geometry.paintExtent)
- visitor(sliver);
- }
+ childrenInPaintOrder
+ .where((RenderSliver sliver) => sliver.geometry.visible || sliver.geometry.cacheExtent > 0.0)
+ .forEach(visitor);
}
/// The direction in which the [SliverConstraints.scrollOffset] increases.
@@ -166,6 +174,35 @@
markNeedsLayout();
}
+ /// {@template flutter.rendering.viewport.cacheExtent}
+ /// The viewport has an area before and after the visible area to cache items
+ /// that are about to become visible when the user scrolls.
+ ///
+ /// Items that fall in this cache area are laid out even though they are not
+ /// (yet) visible on screen. The [cacheExtent] describes how many pixels
+ /// the cache area extends before the leading edge and after the trailing edge
+ /// of the viewport.
+ ///
+ /// The total extent, which the viewport will try to cover with children, is
+ /// [cacheExtent] before the leading edge + extent of the main axis +
+ /// [cacheExtent] after the trailing edge.
+ ///
+ /// The cache area is also used to implement implicit accessibility scrolling
+ /// on iOS: When the accessibility focus moves from an item in the visible
+ /// viewport to an invisible item in the cache area, the framework will bring
+ /// that item into view with an (implicit) scroll action.
+ /// {@endtemplate}
+ double get cacheExtent => _cacheExtent;
+ double _cacheExtent;
+ set cacheExtent(double value) {
+ value = value ?? RenderAbstractViewport.defaultCacheExtent;
+ assert(value != null);
+ if (value == _cacheExtent)
+ return;
+ _cacheExtent = value;
+ markNeedsLayout();
+ }
+
@override
void attach(PipelineOwner owner) {
super.attach(owner);
@@ -262,17 +299,19 @@
/// encountered, if any. Otherwise returns 0.0. Typical callers will call this
/// function repeatedly until it returns 0.0.
@protected
- double layoutChildSequence(
- RenderSliver child,
- double scrollOffset,
- double overlap,
- double layoutOffset,
- double remainingPaintExtent,
- double mainAxisExtent,
- double crossAxisExtent,
- GrowthDirection growthDirection,
- RenderSliver advance(RenderSliver child),
- ) {
+ double layoutChildSequence({
+ @required RenderSliver child,
+ @required double scrollOffset,
+ @required double overlap,
+ @required double layoutOffset,
+ @required double remainingPaintExtent,
+ @required double mainAxisExtent,
+ @required double crossAxisExtent,
+ @required GrowthDirection growthDirection,
+ @required RenderSliver advance(RenderSliver child),
+ @required double remainingCacheExtent,
+ @required double cacheOrigin,
+ }) {
assert(scrollOffset.isFinite);
assert(scrollOffset >= 0.0);
final double initialLayoutOffset = layoutOffset;
@@ -280,18 +319,32 @@
applyGrowthDirectionToScrollDirection(offset.userScrollDirection, growthDirection);
assert(adjustedUserScrollDirection != null);
double maxPaintOffset = layoutOffset + overlap;
+
while (child != null) {
- assert(scrollOffset >= 0.0);
+ final double sliverScrollOffset = scrollOffset <= 0.0 ? 0.0 : scrollOffset;
+ // If the scrollOffset is too small we adjust the paddedOrigin because it
+ // doesn't make sense to ask a sliver for content before its scroll
+ // offset.
+ final double corectedCacheOrigin = math.max(cacheOrigin, -sliverScrollOffset);
+ final double cacheExtentCorrection = cacheOrigin - corectedCacheOrigin;
+
+ assert(sliverScrollOffset >= corectedCacheOrigin.abs());
+ assert(corectedCacheOrigin <= 0.0);
+ assert(sliverScrollOffset >= 0.0);
+ assert(cacheExtentCorrection <= 0.0);
+
child.layout(new SliverConstraints(
axisDirection: axisDirection,
growthDirection: growthDirection,
userScrollDirection: adjustedUserScrollDirection,
- scrollOffset: scrollOffset,
+ scrollOffset: sliverScrollOffset,
overlap: maxPaintOffset - layoutOffset,
remainingPaintExtent: math.max(0.0, remainingPaintExtent - layoutOffset + initialLayoutOffset),
crossAxisExtent: crossAxisExtent,
crossAxisDirection: crossAxisDirection,
viewportMainAxisExtent: mainAxisExtent,
+ remainingCacheExtent: math.max(0.0, remainingCacheExtent + cacheExtentCorrection),
+ cacheOrigin: corectedCacheOrigin,
), parentUsesSize: true);
final SliverGeometry childLayoutGeometry = child.geometry;
@@ -304,13 +357,23 @@
// We use the child's paint origin in our coordinate system as the
// layoutOffset we store in the child's parent data.
final double effectiveLayoutOffset = layoutOffset + childLayoutGeometry.paintOrigin;
- updateChildLayoutOffset(child, effectiveLayoutOffset, growthDirection);
+
+ // `effectiveLayoutOffset` becomes meaningless once we moved past the trailing edge
+ // because `childLayoutGeometry.layoutExtent` is zero. Using the still increasing
+ // 'scrollOffset` to roughly position these invisible slivers in the right order.
+ if (childLayoutGeometry.visible || scrollOffset > 0) {
+ updateChildLayoutOffset(child, effectiveLayoutOffset, growthDirection);
+ } else {
+ updateChildLayoutOffset(child, -scrollOffset + initialLayoutOffset, growthDirection);
+ }
+
maxPaintOffset = math.max(effectiveLayoutOffset + childLayoutGeometry.paintExtent, maxPaintOffset);
scrollOffset -= childLayoutGeometry.scrollExtent;
layoutOffset += childLayoutGeometry.layoutExtent;
-
- if (scrollOffset <= 0.0)
- scrollOffset = 0.0;
+ if (childLayoutGeometry.cacheExtent != 0.0) {
+ remainingCacheExtent -= childLayoutGeometry.cacheExtent - cacheExtentCorrection;
+ cacheOrigin = math.min(corectedCacheOrigin + childLayoutGeometry.cacheExtent, 0.0);
+ }
updateOutOfBandData(growthDirection, childLayoutGeometry);
@@ -323,6 +386,59 @@
}
@override
+ Rect describeApproximatePaintClip(RenderSliver child) {
+ final Rect viewportClip = Offset.zero & size;
+ if (child.constraints.overlap == 0) {
+ return viewportClip;
+ }
+
+ // Adjust the clip rect for this sliver by the overlap from the previous sliver.
+ double left = viewportClip.left;
+ double right = viewportClip.right;
+ double top = viewportClip.top;
+ double bottom = viewportClip.bottom;
+ final double startOfOverlap = child.constraints.viewportMainAxisExtent - child.constraints.remainingPaintExtent;
+ final double overlapCorrection = startOfOverlap + child.constraints.overlap;
+ switch (applyGrowthDirectionToAxisDirection(axisDirection, child.constraints.growthDirection)) {
+ case AxisDirection.down:
+ top += overlapCorrection;
+ break;
+ case AxisDirection.up:
+ bottom -= overlapCorrection;
+ break;
+ case AxisDirection.right:
+ left += overlapCorrection;
+ break;
+ case AxisDirection.left:
+ right -= overlapCorrection;
+ break;
+ }
+ return new Rect.fromLTRB(left, top, right, bottom);
+ }
+
+ @override
+ Rect describeSemanticsClip(RenderSliver child) {
+ assert (axis != null);
+ switch (axis) {
+ case Axis.vertical:
+ return new Rect.fromLTRB(
+ semanticBounds.left,
+ semanticBounds.top - cacheExtent,
+ semanticBounds.right,
+ semanticBounds.bottom + cacheExtent,
+ );
+ case Axis.horizontal:
+ return new Rect.fromLTRB(
+ semanticBounds.left - cacheExtent,
+ semanticBounds.top,
+ semanticBounds.right + cacheExtent,
+ semanticBounds.bottom,
+ );
+ }
+ return null;
+ }
+
+ @override
void paint(PaintingContext context, Offset offset) {
if (firstChild == null)
return;
@@ -734,11 +850,12 @@
double anchor: 0.0,
List<RenderSliver> children,
RenderSliver center,
+ double cacheExtent,
}) : assert(anchor != null),
assert(anchor >= 0.0 && anchor <= 1.0),
_anchor = anchor,
_center = center,
- super(axisDirection: axisDirection, crossAxisDirection: crossAxisDirection, offset: offset) {
+ super(axisDirection: axisDirection, crossAxisDirection: crossAxisDirection, offset: offset, cacheExtent: cacheExtent) {
addAll(children);
if (center == null && firstChild != null)
_center = firstChild;
@@ -979,26 +1096,32 @@
// centerOffset is the offset from the leading edge of the RenderViewport
// to the zero scroll offset (the line between the forward slivers and the
- // reverse slivers). The other two are that, but clamped to the visible
- // region of the viewport.
+ // reverse slivers).
final double centerOffset = mainAxisExtent * anchor - correctedOffset;
- final double clampedForwardCenter = math.max(0.0, math.min(mainAxisExtent, centerOffset));
- final double clampedReverseCenter = math.max(0.0, math.min(mainAxisExtent, mainAxisExtent - centerOffset));
+ final double reverseDirectionRemainingPaintExtent = centerOffset.clamp(0.0, mainAxisExtent);
+ final double forwardDirectionRemainingPaintExtent = (mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent);
+
+ final double fullCacheExtent = mainAxisExtent + 2 * cacheExtent;
+ final double centerCacheOffset = centerOffset + cacheExtent;
+ final double reverseDirectionRemainingCacheExtent = centerCacheOffset.clamp(0.0, fullCacheExtent);
+ final double forwardDirectionRemainingCacheExtent = (fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent);
final RenderSliver leadingNegativeChild = childBefore(center);
if (leadingNegativeChild != null) {
// negative scroll offsets
final double result = layoutChildSequence(
- leadingNegativeChild,
- math.max(mainAxisExtent, centerOffset) - mainAxisExtent,
- 0.0,
- clampedReverseCenter,
- clampedForwardCenter,
- mainAxisExtent,
- crossAxisExtent,
- GrowthDirection.reverse,
- childBefore,
+ child: leadingNegativeChild,
+ scrollOffset: math.max(mainAxisExtent, centerOffset) - mainAxisExtent,
+ overlap: 0.0,
+ layoutOffset: forwardDirectionRemainingPaintExtent,
+ remainingPaintExtent: reverseDirectionRemainingPaintExtent,
+ mainAxisExtent: mainAxisExtent,
+ crossAxisExtent: crossAxisExtent,
+ growthDirection: GrowthDirection.reverse,
+ advance: childBefore,
+ remainingCacheExtent: reverseDirectionRemainingCacheExtent,
+ cacheOrigin: (mainAxisExtent - centerOffset).clamp(-cacheExtent, 0.0),
);
if (result != 0.0)
return -result;
@@ -1006,15 +1129,17 @@
// positive scroll offsets
return layoutChildSequence(
- center,
- math.max(0.0, -centerOffset),
- leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0,
- clampedForwardCenter,
- clampedReverseCenter,
- mainAxisExtent,
- crossAxisExtent,
- GrowthDirection.forward,
- childAfter,
+ child: center,
+ scrollOffset: math.max(0.0, -centerOffset),
+ overlap: leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0,
+ layoutOffset: centerOffset >= mainAxisExtent ? centerOffset: reverseDirectionRemainingPaintExtent,
+ remainingPaintExtent: forwardDirectionRemainingPaintExtent,
+ mainAxisExtent: mainAxisExtent,
+ crossAxisExtent: crossAxisExtent,
+ growthDirection: GrowthDirection.forward,
+ advance: childAfter,
+ remainingCacheExtent: forwardDirectionRemainingCacheExtent,
+ cacheOrigin: centerOffset.clamp(-cacheExtent, 0.0),
);
}
@@ -1333,15 +1458,17 @@
_shrinkWrapExtent = 0.0;
_hasVisualOverflow = false;
return layoutChildSequence(
- firstChild,
- math.max(0.0, correctedOffset),
- math.min(0.0, correctedOffset),
- 0.0,
- mainAxisExtent,
- mainAxisExtent,
- crossAxisExtent,
- GrowthDirection.forward,
- childAfter,
+ child: firstChild,
+ scrollOffset: math.max(0.0, correctedOffset),
+ overlap: math.min(0.0, correctedOffset),
+ layoutOffset: 0.0,
+ remainingPaintExtent: mainAxisExtent,
+ mainAxisExtent: mainAxisExtent,
+ crossAxisExtent: crossAxisExtent,
+ growthDirection: GrowthDirection.forward,
+ advance: childAfter,
+ remainingCacheExtent: mainAxisExtent + 2 * cacheExtent,
+ cacheOrigin: -cacheExtent,
);
}
diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart
index a8ff461..4504a85 100644
--- a/packages/flutter/lib/src/semantics/semantics.dart
+++ b/packages/flutter/lib/src/semantics/semantics.dart
@@ -794,11 +794,39 @@
}
}
- /// The clip rect from an ancestor that was applied to this node.
+ /// The semantic clip from an ancestor that was applied to this node.
///
/// Expressed in the coordinate system of the node. May be null if no clip has
/// been applied.
- Rect parentClipRect;
+ ///
+ /// Descendant [SemanticsNode]s that are positioned outside of this rect will
+ /// be excluded from the semantics tree. Descendant [SemanticsNode]s that are
+ /// overlapping with this rect, but are outside of [parentPaintClipRect] will
+ /// be included in the tree, but they will be marked as hidden because they
+ /// are assumed to be not visible on screen.
+ ///
+ /// If this rect is null, all descendant [SemanticsNode]s outside of
+ /// [parentPaintClipRect] will be excluded from the tree.
+ ///
+ /// If this rect is non-null it has to completely enclose
+ /// [parentPaintClipRect]. If [parentPaintClipRect] is null this property is
+ /// also null.
+ Rect parentSemanticsClipRect;
+
+ /// The paint clip from an ancestor that was applied to this node.
+ ///
+ /// Expressed in the coordinate system of the node. May be null if no clip has
+ /// been applied.
+ ///
+ /// Descendant [SemanticsNode]s that are positioned outside of this rect will
+ /// either be excluded from the semantics tree (if they have no overlap with
+ /// [parentSemanticsClipRect]) or they will be included and marked as hidden
+ /// (if they are overlapping with [parentSemanticsClipRect]).
+ ///
+ /// This rect is completely enclosed by [parentSemanticsClipRect].
+ ///
+ /// If this rect is null [parentSemanticsClipRect] also has to be null.
+ Rect parentPaintClipRect;
/// Whether the node is invisible.
///
diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart
index 485a7ba..4ea9a93 100644
--- a/packages/flutter/lib/src/widgets/page_view.dart
+++ b/packages/flutter/lib/src/widgets/page_view.dart
@@ -564,6 +564,7 @@
physics: physics,
viewportBuilder: (BuildContext context, ViewportOffset position) {
return new Viewport(
+ cacheExtent: 0.0,
axisDirection: axisDirection,
offset: position,
slivers: <Widget>[
diff --git a/packages/flutter/lib/src/widgets/scroll_view.dart b/packages/flutter/lib/src/widgets/scroll_view.dart
index 9d67371..0e0c88f 100644
--- a/packages/flutter/lib/src/widgets/scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/scroll_view.dart
@@ -56,6 +56,7 @@
bool primary,
ScrollPhysics physics,
this.shrinkWrap: false,
+ this.cacheExtent,
}) : assert(reverse != null),
assert(shrinkWrap != null),
assert(!(controller != null && primary == true),
@@ -165,6 +166,9 @@
/// Defaults to false.
final bool shrinkWrap;
+ /// {@macro flutter.rendering.viewport.cacheExtent}
+ final double cacheExtent;
+
/// Returns the [AxisDirection] in which the scroll view scrolls.
///
/// Combines the [scrollDirection] with the [reverse] boolean to obtain the
@@ -211,6 +215,7 @@
axisDirection: axisDirection,
offset: offset,
slivers: slivers,
+ cacheExtent: cacheExtent,
);
}
@@ -333,6 +338,7 @@
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
+ double cacheExtent,
this.slivers: const <Widget>[],
}) : super(
key: key,
@@ -342,6 +348,7 @@
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
+ cacheExtent: cacheExtent,
);
/// The slivers to place inside the viewport.
@@ -372,6 +379,7 @@
ScrollPhysics physics,
bool shrinkWrap: false,
this.padding,
+ double cacheExtent,
}) : super(
key: key,
scrollDirection: scrollDirection,
@@ -380,6 +388,7 @@
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
+ cacheExtent: cacheExtent,
);
/// The amount of space by which to inset the children.
@@ -594,6 +603,7 @@
this.itemExtent,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
+ double cacheExtent,
List<Widget> children: const <Widget>[],
}) : childrenDelegate = new SliverChildListDelegate(
children,
@@ -608,6 +618,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// Creates a scrollable, linear array of widgets that are created on demand.
@@ -648,6 +659,7 @@
int itemCount,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
+ double cacheExtent,
}) : childrenDelegate = new SliverChildBuilderDelegate(
itemBuilder,
childCount: itemCount,
@@ -662,6 +674,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent
);
/// Creates a scrollable, linear array of widgets with a custom child model.
@@ -679,6 +692,7 @@
EdgeInsetsGeometry padding,
this.itemExtent,
@required this.childrenDelegate,
+ double cacheExtent,
}) : assert(childrenDelegate != null),
super(
key: key,
@@ -689,6 +703,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// If non-null, forces the children to have the given extent in the scroll
@@ -878,6 +893,7 @@
@required this.gridDelegate,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
+ double cacheExtent,
List<Widget> children: const <Widget>[],
}) : assert(gridDelegate != null),
childrenDelegate = new SliverChildListDelegate(
@@ -894,6 +910,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// Creates a scrollable, 2D array of widgets that are created on demand.
@@ -929,6 +946,7 @@
int itemCount,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
+ double cacheExtent,
}) : assert(gridDelegate != null),
childrenDelegate = new SliverChildBuilderDelegate(
itemBuilder,
@@ -945,6 +963,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// Creates a scrollable, 2D array of widgets with both a custom
@@ -965,6 +984,7 @@
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required this.childrenDelegate,
+ double cacheExtent,
}) : assert(gridDelegate != null),
assert(childrenDelegate != null),
super(
@@ -976,6 +996,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
@@ -1007,6 +1028,7 @@
double childAspectRatio: 1.0,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
+ double cacheExtent,
List<Widget> children: const <Widget>[],
}) : gridDelegate = new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
@@ -1027,6 +1049,7 @@
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
+ cacheExtent: cacheExtent,
);
/// Creates a scrollable, 2D array of widgets with tiles that each have a
diff --git a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
index 278448a..f23e9bb 100644
--- a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
+++ b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart
@@ -323,11 +323,14 @@
_RenderSingleChildViewport({
AxisDirection axisDirection: AxisDirection.down,
@required ViewportOffset offset,
+ double cacheExtent: RenderAbstractViewport.defaultCacheExtent,
RenderBox child,
}) : assert(axisDirection != null),
assert(offset != null),
+ assert(cacheExtent != null),
_axisDirection = axisDirection,
- _offset = offset {
+ _offset = offset,
+ _cacheExtent = cacheExtent {
this.child = child;
}
@@ -357,6 +360,17 @@
markNeedsLayout();
}
+ /// {@macro flutter.rendering.viewport.cacheExtent}
+ double get cacheExtent => _cacheExtent;
+ double _cacheExtent;
+ set cacheExtent(double value) {
+ assert(value != null);
+ if (value == _cacheExtent)
+ return;
+ _cacheExtent = value;
+ markNeedsLayout();
+ }
+
void _hasScrolled() {
markNeedsPaint();
markNeedsSemanticsUpdate();
@@ -588,4 +602,26 @@
// Make sure the viewport itself is on screen.
super.showOnScreen();
}
+
+ @override
+ Rect describeSemanticsClip(RenderObject child) {
+ assert(axis != null);
+ switch (axis) {
+ case Axis.vertical:
+ return new Rect.fromLTRB(
+ semanticBounds.left,
+ semanticBounds.top - cacheExtent,
+ semanticBounds.right,
+ semanticBounds.bottom + cacheExtent,
+ );
+ case Axis.horizontal:
+ return new Rect.fromLTRB(
+ semanticBounds.left - cacheExtent,
+ semanticBounds.top,
+ semanticBounds.right + cacheExtent,
+ semanticBounds.bottom,
+ );
+ }
+ return null;
+ }
}
diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart
index 9074396..38a3cba 100644
--- a/packages/flutter/lib/src/widgets/sliver.dart
+++ b/packages/flutter/lib/src/widgets/sliver.dart
@@ -888,6 +888,25 @@
assert(!_childElements.values.any((Element child) => child == null));
_childElements.values.toList().forEach(visitor);
}
+
+ @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);
+ }
}
/// A sliver that contains a single box child that fills the remaining space in
diff --git a/packages/flutter/lib/src/widgets/viewport.dart b/packages/flutter/lib/src/widgets/viewport.dart
index cb508cc..16f1ea8 100644
--- a/packages/flutter/lib/src/widgets/viewport.dart
+++ b/packages/flutter/lib/src/widgets/viewport.dart
@@ -56,6 +56,7 @@
this.anchor: 0.0,
@required this.offset,
this.center,
+ this.cacheExtent,
List<Widget> slivers: const <Widget>[],
}) : assert(offset != null),
assert(slivers != null),
@@ -108,6 +109,9 @@
/// The [center] must be the key of a child of the viewport.
final Key center;
+ /// {@macro flutter.rendering.viewport.cacheExtent}
+ final double cacheExtent;
+
/// Given a [BuildContext] and an [AxisDirection], determine the correct cross
/// axis direction.
///
@@ -135,6 +139,7 @@
crossAxisDirection: crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection),
anchor: anchor,
offset: offset,
+ cacheExtent: cacheExtent,
);
}
@@ -144,7 +149,8 @@
..axisDirection = axisDirection
..crossAxisDirection = crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection)
..anchor = anchor
- ..offset = offset;
+ ..offset = offset
+ ..cacheExtent = cacheExtent;
}
@override
@@ -199,6 +205,14 @@
renderObject.center = null;
}
}
+
+ @override
+ void debugVisitOnstageChildren(ElementVisitor visitor) {
+ children.where((Element e) {
+ final RenderSliver renderSliver = e.renderObject;
+ return renderSliver.geometry.visible;
+ }).forEach(visitor);
+ }
}
/// A widget that is bigger on the inside and shrink wraps its children in the
diff --git a/packages/flutter/test/cupertino/refresh_test.dart b/packages/flutter/test/cupertino/refresh_test.dart
index c91c039..ef6317d 100644
--- a/packages/flutter/test/cupertino/refresh_test.dart
+++ b/packages/flutter/test/cupertino/refresh_test.dart
@@ -430,11 +430,11 @@
// Now the sliver is scrolled off screen.
expect(
- tester.getTopLeft(find.widgetWithText(Center, '-1')).dy,
+ tester.getTopLeft(find.widgetWithText(Center, '-1', skipOffstage: false)).dy,
moreOrLessEquals(-175.38461538461536),
);
expect(
- tester.getBottomLeft(find.widgetWithText(Center, '-1')).dy,
+ tester.getBottomLeft(find.widgetWithText(Center, '-1', skipOffstage: false)).dy,
moreOrLessEquals(-115.38461538461536),
);
expect(
@@ -720,11 +720,11 @@
// Now the sliver is scrolled off screen.
expect(
- tester.getTopLeft(find.widgetWithText(Center, '-1')).dy,
+ tester.getTopLeft(find.widgetWithText(Center, '-1', skipOffstage: false)).dy,
moreOrLessEquals(-175.38461538461536),
);
expect(
- tester.getBottomLeft(find.widgetWithText(Center, '-1')).dy,
+ tester.getBottomLeft(find.widgetWithText(Center, '-1', skipOffstage: false)).dy,
moreOrLessEquals(-115.38461538461536),
);
@@ -872,7 +872,7 @@
);
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
@@ -907,7 +907,7 @@
await tester.pump(const Duration(seconds: 2));
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
@@ -1147,7 +1147,7 @@
moreOrLessEquals(-145.0332383665717),
);
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.refresh,
);
@@ -1155,7 +1155,7 @@
// The sliver layout extent is removed on next frame.
await tester.pump();
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
// Nothing moved.
@@ -1208,7 +1208,7 @@
await tester.pump(const Duration(seconds: 5));
// In refresh mode but has no UI.
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.refresh,
);
expect(
@@ -1221,7 +1221,7 @@
await tester.pump();
// Goes to inactive right away since the sliver is already collapsed.
expect(
- CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
+ CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
diff --git a/packages/flutter/test/material/app_bar_test.dart b/packages/flutter/test/material/app_bar_test.dart
index 54b9a84..927134c 100644
--- a/packages/flutter/test/material/app_bar_test.dart
+++ b/packages/flutter/test/material/app_bar_test.dart
@@ -54,16 +54,11 @@
return PrimaryScrollController.of(tester.element(find.byType(CustomScrollView)));
}
-bool appBarIsVisible(WidgetTester tester) {
- final RenderSliver sliver = tester.element(find.byType(SliverAppBar)).findRenderObject();
- return sliver.geometry.visible;
-}
+double appBarHeight(WidgetTester tester) => tester.getSize(find.byType(AppBar, skipOffstage: false)).height;
+double appBarTop(WidgetTester tester) => tester.getTopLeft(find.byType(AppBar, skipOffstage: false)).dy;
+double appBarBottom(WidgetTester tester) => tester.getBottomLeft(find.byType(AppBar, skipOffstage: false)).dy;
-double appBarHeight(WidgetTester tester) => tester.getSize(find.byType(AppBar)).height;
-double appBarTop(WidgetTester tester) => tester.getTopLeft(find.byType(AppBar)).dy;
-double appBarBottom(WidgetTester tester) => tester.getBottomLeft(find.byType(AppBar)).dy;
-
-double tabBarHeight(WidgetTester tester) => tester.getSize(find.byType(TabBar)).height;
+double tabBarHeight(WidgetTester tester) => tester.getSize(find.byType(TabBar, skipOffstage: false)).height;
void main() {
setUp(() {
@@ -592,7 +587,7 @@
final ScrollController controller = primaryScrollController(tester);
expect(controller.offset, 0.0);
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
final double initialAppBarHeight = appBarHeight(tester);
final double initialTabBarHeight = tabBarHeight(tester);
@@ -600,21 +595,21 @@
// Scroll the not-pinned appbar partially out of view
controller.jumpTo(50.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), initialAppBarHeight);
expect(tabBarHeight(tester), initialTabBarHeight);
// Scroll the not-pinned appbar out of view
controller.jumpTo(600.0);
await tester.pump();
- expect(appBarIsVisible(tester), false);
+ expect(find.byType(SliverAppBar), findsNothing);
expect(appBarHeight(tester), initialAppBarHeight);
expect(tabBarHeight(tester), initialTabBarHeight);
// Scroll the not-pinned appbar back into view
controller.jumpTo(0.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), initialAppBarHeight);
expect(tabBarHeight(tester), initialTabBarHeight);
});
@@ -629,7 +624,7 @@
final ScrollController controller = primaryScrollController(tester);
expect(controller.offset, 0.0);
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), 128.0);
const double initialAppBarHeight = 128.0;
@@ -639,7 +634,7 @@
// point both the toolbar and the tabbar are visible.
controller.jumpTo(600.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(tabBarHeight(tester), initialTabBarHeight);
expect(appBarHeight(tester), lessThan(initialAppBarHeight));
expect(appBarHeight(tester), greaterThan(initialTabBarHeight));
@@ -647,7 +642,7 @@
// Scroll the not-pinned appbar back into view
controller.jumpTo(0.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), initialAppBarHeight);
expect(tabBarHeight(tester), initialTabBarHeight);
});
@@ -662,7 +657,7 @@
final ScrollController controller = primaryScrollController(tester);
expect(controller.offset, 0.0);
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), 128.0);
const double initialAppBarHeight = 128.0;
@@ -672,7 +667,7 @@
// point only the tabBar is visible.
controller.jumpTo(600.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(tabBarHeight(tester), initialTabBarHeight);
expect(appBarHeight(tester), lessThan(initialAppBarHeight));
expect(appBarHeight(tester), initialTabBarHeight);
@@ -680,7 +675,7 @@
// Scroll the floating-pinned appbar back into view
controller.jumpTo(0.0);
await tester.pump();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), initialAppBarHeight);
expect(tabBarHeight(tester), initialTabBarHeight);
});
@@ -692,7 +687,7 @@
snap: true,
expandedHeight: 128.0,
));
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarTop(tester), 0.0);
expect(appBarHeight(tester), 128.0);
expect(appBarBottom(tester), 128.0);
@@ -701,7 +696,7 @@
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
position.jumpTo(256.00);
await tester.pumpAndSettle();
- expect(appBarIsVisible(tester), false);
+ expect(find.byType(SliverAppBar), findsNothing);
expect(appBarTop(tester), lessThanOrEqualTo(-128.0));
// Drag the scrollable up and down. The app bar should not snap open, its
@@ -773,7 +768,7 @@
snap: true,
expandedHeight: 128.0,
));
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarTop(tester), 0.0);
expect(appBarHeight(tester), 128.0);
expect(appBarBottom(tester), 128.0);
@@ -783,7 +778,7 @@
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
position.jumpTo(256.0);
await tester.pumpAndSettle();
- expect(appBarIsVisible(tester), true);
+ expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarTop(tester), 0.0);
expect(appBarHeight(tester), kTextTabBarHeight);
diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart
index 918376a..87fa409 100644
--- a/packages/flutter/test/material/tabs_test.dart
+++ b/packages/flutter/test/material/tabs_test.dart
@@ -289,7 +289,7 @@
}
StateMarkerState findStateMarkerState(String name) {
- return tester.state(find.widgetWithText(StateMarker, name));
+ return tester.state(find.widgetWithText(StateMarker, name, skipOffstage: false));
}
await tester.pumpWidget(builder());
diff --git a/packages/flutter/test/material/text_field_focus_test.dart b/packages/flutter/test/material/text_field_focus_test.dart
index 8d4ee76..98e7145 100644
--- a/packages/flutter/test/material/text_field_focus_test.dart
+++ b/packages/flutter/test/material/text_field_focus_test.dart
@@ -162,7 +162,7 @@
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0));
await tester.pump();
- expect(find.byType(TextField), findsOneWidget);
+ expect(find.byType(TextField, skipOffstage: false), findsOneWidget);
expect(tester.testTextInput.isVisible, isTrue);
focusNode.unfocus();
@@ -201,10 +201,10 @@
expect(find.byType(TextField), findsOneWidget);
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0));
await tester.pump();
- expect(find.byType(TextField), findsOneWidget);
+ expect(find.byType(TextField, skipOffstage: false), findsOneWidget);
await tester.pumpWidget(makeTest('test'));
await tester.pump(); // in case the AutomaticKeepAlive widget thinks it needs a cleanup frame
- expect(find.byType(TextField), findsOneWidget);
+ expect(find.byType(TextField, skipOffstage: false), findsOneWidget);
});
testWidgets('TextField with decoration:null', (WidgetTester tester) async {
diff --git a/packages/flutter/test/rendering/sliver_cache_test.dart b/packages/flutter/test/rendering/sliver_cache_test.dart
new file mode 100644
index 0000000..91ca7a8
--- /dev/null
+++ b/packages/flutter/test/rendering/sliver_cache_test.dart
@@ -0,0 +1,971 @@
+// Copyright 2018 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 'package:flutter/foundation.dart';
+import 'package:flutter/rendering.dart';
+import 'package:test/test.dart';
+
+import 'rendering_tester.dart';
+
+void main() {
+ test('RenderViewport calculates correct constraints, RenderSliverToBoxAdapter calculates correct geometry', () {
+ final List<RenderSliver> children = new List<RenderSliver>.generate(30, (int index) {
+ return new RenderSliverToBoxAdapter(
+ child: new RenderSizedBox(const Size(400.0, 100.0)),
+ );
+ });
+
+ // Viewport is 800x600, can show 6 children at a time.
+
+ final RenderViewport root = new RenderViewport(
+ axisDirection: AxisDirection.down,
+ crossAxisDirection: AxisDirection.right,
+ offset: new ViewportOffset.zero(),
+ cacheExtent: 250.0,
+ children: children,
+ );
+ layout(root);
+
+ RenderSliver firstVisible = children[0];
+ expectSliverConstraints(
+ sliver: firstVisible,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisible,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ RenderSliver lastVisible = children[5];
+ expectSliverConstraints(
+ sliver: lastVisible,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 100.0,
+ remainingCacheExtent: 350.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisible,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ RenderSliver firstInCache = children[6];
+ expectSliverConstraints(
+ sliver: firstInCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstInCache,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ RenderSliver lastInCache = children[8];
+ expectSliverConstraints(
+ sliver: lastInCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 50.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastInCache,
+ paintExtent: 0.0,
+ cacheExtent: 50.0,
+ visible: false,
+ );
+
+ RenderSliver outsideCache = children[9];
+ expectSliverConstraints(
+ sliver: outsideCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 0.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: outsideCache,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ // scroll down half a sliver
+ root.offset = new ViewportOffset.fixed(50.0);
+ pumpFrame();
+
+ firstVisible = children[0];
+ expectSliverConstraints(
+ sliver: firstVisible,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisible,
+ paintExtent: 50.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ lastVisible = children[6];
+ expectSliverConstraints(
+ sliver: lastVisible,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 50.0,
+ remainingCacheExtent: 300.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisible,
+ paintExtent: 50.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ firstInCache = children[7];
+ expectSliverConstraints(
+ sliver: firstInCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 200.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstInCache,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ lastInCache = children[8];
+ expectSliverConstraints(
+ sliver: lastInCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 100.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastInCache,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ outsideCache = children[9];
+ expectSliverConstraints(
+ sliver: outsideCache,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 0.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: outsideCache,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ // scroll down 1.5 slivers
+ root.offset = new ViewportOffset.fixed(150.0);
+ pumpFrame();
+
+ RenderSliver firstInPreCache = children[0];
+ expectSliverConstraints(
+ sliver: firstInPreCache,
+ cacheOrigin: -150.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 150.0 + 600.0 + 250.0,
+ scrollOffset: 150.0,
+ );
+ expectSliverGeometry(
+ sliver: firstInPreCache,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ firstVisible = children[1];
+ expectSliverConstraints(
+ sliver: firstVisible,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisible,
+ paintExtent: 50.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ // scroll down 10 slivers
+ root.offset = new ViewportOffset.fixed(1000.0);
+ pumpFrame();
+
+ final RenderSliver first = children[0];
+ expectSliverConstraints(
+ sliver: first,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 1000.0,
+ );
+ expectSliverGeometry(
+ sliver: first,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ firstInPreCache = children[7];
+ expectSliverConstraints(
+ sliver: firstInPreCache,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 300.0,
+ );
+ expectSliverGeometry(
+ sliver: firstInPreCache,
+ paintExtent: 0.0,
+ cacheExtent: 50.0,
+ visible: false,
+ );
+
+ final RenderSliver lastInPreCache = children[9];
+ expectSliverConstraints(
+ sliver: lastInPreCache,
+ cacheOrigin: -100.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 100.0 + 600.0 + 250.0,
+ scrollOffset: 100.0,
+ );
+ expectSliverGeometry(
+ sliver: lastInPreCache,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ firstVisible = children[10];
+ expectSliverConstraints(
+ sliver: firstVisible,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisible,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+ });
+
+ test('RenderSliverFixedExtentList calculates correct geometry', () {
+ // Viewport is 800x600, can show 6 full children at a time
+ final List<RenderBox> children = new List<RenderBox>.generate(30, (int index) {
+ return new RenderSizedBox(const Size(400.0, 100.0));
+ });
+ final TestRenderSliverBoxChildManager childManager = new TestRenderSliverBoxChildManager(
+ children: children,
+ );
+ RenderSliverFixedExtentList inner;
+ final RenderViewport root = new RenderViewport(
+ axisDirection: AxisDirection.down,
+ crossAxisDirection: AxisDirection.right,
+ offset: new ViewportOffset.zero(),
+ cacheExtent: 250.0,
+ children: <RenderSliver>[
+ inner = childManager.createRenderSliverFixedExtentList(),
+ ],
+ );
+ layout(root);
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
+
+ // scroll half an item down
+ root.offset = new ViewportOffset.fixed(50.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 900.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
+
+
+ // scroll to the middle
+ root.offset = new ViewportOffset.fixed(1500.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 1500.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 1100.0,
+ visible: true,
+ );
+
+ expect(children.sublist(0, 12).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(12, 24).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(24, 30).any((RenderBox r) => r.attached), false);
+
+ // scroll to the end
+ root.offset = new ViewportOffset.fixed(2400.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 2400.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 21).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(21, 30).every((RenderBox r) => r.attached), true);
+ });
+
+ test('RenderSliverList calculates correct geometry', () {
+ // Viewport is 800x600, can show 6 full children at a time
+ final List<RenderBox> children = new List<RenderBox>.generate(30, (int index) {
+ return new RenderSizedBox(const Size(400.0, 100.0));
+ });
+ final TestRenderSliverBoxChildManager childManager = new TestRenderSliverBoxChildManager(
+ children: children,
+ );
+ RenderSliverList inner;
+ final RenderViewport root = new RenderViewport(
+ axisDirection: AxisDirection.down,
+ crossAxisDirection: AxisDirection.right,
+ offset: new ViewportOffset.zero(),
+ cacheExtent: 250.0,
+ children: <RenderSliver>[
+ inner = childManager.createRenderSliverList(),
+ ],
+ );
+ layout(root);
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
+
+ // scroll half an item down
+ root.offset = new ViewportOffset.fixed(50.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 900.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
+
+
+ // scroll to the middle
+ root.offset = new ViewportOffset.fixed(1500.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 1500.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 1100.0,
+ visible: true,
+ );
+
+ expect(children.sublist(0, 12).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(12, 24).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(24, 30).any((RenderBox r) => r.attached), false);
+
+ // scroll to the end
+ root.offset = new ViewportOffset.fixed(2400.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 2400.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 21).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(21, 30).every((RenderBox r) => r.attached), true);
+ });
+
+ test('RenderSliverGrid calculates correct geometry', () {
+ // Viewport is 800x600, each grid element is 400x100, giving us space for 12 visible children
+ final List<RenderBox> children = new List<RenderBox>.generate(60, (int index) {
+ return new RenderSizedBox(const Size(400.0, 100.0));
+ });
+ final TestRenderSliverBoxChildManager childManager = new TestRenderSliverBoxChildManager(
+ children: children,
+ );
+ RenderSliverGrid inner;
+ final RenderViewport root = new RenderViewport(
+ axisDirection: AxisDirection.down,
+ crossAxisDirection: AxisDirection.right,
+ offset: new ViewportOffset.zero(),
+ cacheExtent: 250.0,
+ children: <RenderSliver>[
+ inner = childManager.createRenderSliverGrid(),
+ ],
+ );
+ layout(root);
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 18).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(18, 60).any((RenderBox r) => r.attached), false);
+
+ // scroll half an item down
+ root.offset = new ViewportOffset.fixed(50.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 900.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 18).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(18, 60).any((RenderBox r) => r.attached), false);
+
+
+ // scroll to the middle
+ root.offset = new ViewportOffset.fixed(1500.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 1500.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 1100.0,
+ visible: true,
+ );
+
+ expect(children.sublist(0, 24).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(24, 48).every((RenderBox r) => r.attached), true);
+ expect(children.sublist(48, 60).any((RenderBox r) => r.attached), false);
+
+ // scroll to the end
+ root.offset = new ViewportOffset.fixed(2400.0);
+ pumpFrame();
+
+ expectSliverConstraints(
+ sliver: inner,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 2400.0,
+ );
+ expectSliverGeometry(
+ sliver: inner,
+ paintExtent: 600.0,
+ cacheExtent: 850.0,
+ visible: true,
+ );
+ expect(children.sublist(0, 42).any((RenderBox r) => r.attached), false);
+ expect(children.sublist(42, 60).every((RenderBox r) => r.attached), true);
+ });
+
+ test('RenderSliverPadding calculates correct geometry', () {
+ // Viewport is 800x600, each item is 100px high with 50px before and after = 200px
+
+ final List<RenderSliverToBoxAdapter> adapters = <RenderSliverToBoxAdapter>[];
+ final List<RenderSliverPadding> paddings = new List<RenderSliverPadding>.generate(30, (int index) {
+ RenderSliverToBoxAdapter adapter;
+ final RenderSliverPadding padding = new RenderSliverPadding(
+ padding: const EdgeInsets.symmetric(vertical: 50.0),
+ child: adapter = new RenderSliverToBoxAdapter(
+ child: new RenderSizedBox(const Size(400.0, 100.0)),
+ ),
+ );
+ adapters.add(adapter);
+ return padding;
+ });
+
+
+ final RenderViewport root = new RenderViewport(
+ axisDirection: AxisDirection.down,
+ crossAxisDirection: AxisDirection.right,
+ offset: new ViewportOffset.zero(),
+ cacheExtent: 250.0,
+ children: paddings,
+ );
+ layout(root);
+
+ RenderSliverPadding firstVisiblePadding = paddings[0];
+ expectSliverConstraints(
+ sliver: firstVisiblePadding,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisiblePadding,
+ paintExtent: 200.0,
+ cacheExtent: 200.0,
+ visible: true,
+ );
+ RenderSliverToBoxAdapter firstVisiblePadded = adapters[0];
+ expectSliverConstraints(
+ sliver: firstVisiblePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 550.0,
+ remainingCacheExtent: 600.0 + 250.0 - 50.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisiblePadded,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ RenderSliverPadding lastVisiblePadding = paddings[2];
+ expectSliverConstraints(
+ sliver: lastVisiblePadding,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 200.0,
+ remainingCacheExtent: 200.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisiblePadding,
+ paintExtent: 200.0,
+ cacheExtent: 200.0,
+ visible: true,
+ );
+ RenderSliverToBoxAdapter lastVisiblePadded = adapters[2];
+ expectSliverConstraints(
+ sliver: lastVisiblePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 150.0,
+ remainingCacheExtent: 200.0 + 250.0 - 50.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisiblePadded,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ final RenderSliverPadding firstCachePadding = paddings[3];
+ expectSliverConstraints(
+ sliver: firstCachePadding,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstCachePadding,
+ paintExtent: 0.0,
+ cacheExtent: 200.0,
+ visible: false,
+ );
+ final RenderSliverToBoxAdapter firstCachePadded = adapters[3];
+ expectSliverConstraints(
+ sliver: firstCachePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 250.0 - 50.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstCachePadded,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ final RenderSliverPadding lastCachePadding = paddings[4];
+ expectSliverConstraints(
+ sliver: lastCachePadding,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 50.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastCachePadding,
+ paintExtent: 0.0,
+ cacheExtent: 50.0,
+ visible: false,
+ );
+ final RenderSliverToBoxAdapter lastCachePadded = adapters[4];
+ expectSliverConstraints(
+ sliver: lastCachePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 0.0,
+ remainingCacheExtent: 0.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastCachePadded,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ // scroll first padding off screen
+ root.offset = new ViewportOffset.fixed(50.0);
+ pumpFrame();
+
+ firstVisiblePadding = paddings[0];
+ expectSliverConstraints(
+ sliver: firstVisiblePadding,
+ cacheOrigin: -50.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 50.0 + 600.0 + 250.0,
+ scrollOffset: 50.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisiblePadding,
+ paintExtent: 150.0,
+ cacheExtent: 200.0,
+ visible: true,
+ );
+ firstVisiblePadded = adapters[0];
+ expectSliverConstraints(
+ sliver: firstVisiblePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 600.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: firstVisiblePadded,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+
+ // scroll to the end
+ root.offset = new ViewportOffset.fixed(5400.0);
+ pumpFrame();
+
+
+ final RenderSliverPadding firstPadding = paddings[0];
+ expectSliverConstraints(
+ sliver: firstPadding,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 5400.0,
+ );
+ expectSliverGeometry(
+ sliver: firstPadding,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+ final RenderSliverToBoxAdapter firstPadded = adapters[0];
+ expectSliverConstraints(
+ sliver: firstPadded,
+ cacheOrigin: -200.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 5350.0,
+ );
+ expectSliverGeometry(
+ sliver: firstPadded,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ final RenderSliverPadding firstPreCachePadding = paddings[25];
+ expectSliverConstraints(
+ sliver: firstPreCachePadding,
+ cacheOrigin: -250.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 400.0,
+ );
+ expectSliverGeometry(
+ sliver: firstPreCachePadding,
+ paintExtent: 0.0,
+ cacheExtent: 50.0,
+ visible: false,
+ );
+ final RenderSliverToBoxAdapter firstPreCachePadded = adapters[25];
+ expectSliverConstraints(
+ sliver: firstPreCachePadded,
+ cacheOrigin: -200.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 250.0 + 600.0 + 250.0,
+ scrollOffset: 350.0,
+ );
+ expectSliverGeometry(
+ sliver: firstPreCachePadded,
+ paintExtent: 0.0,
+ cacheExtent: 0.0,
+ visible: false,
+ );
+
+ final RenderSliverPadding lastPreCachePadding = paddings[26];
+ expectSliverConstraints(
+ sliver: lastPreCachePadding,
+ cacheOrigin: -200.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 200.0 + 600.0 + 250.0,
+ scrollOffset: 200.0,
+ );
+ expectSliverGeometry(
+ sliver: lastPreCachePadding,
+ paintExtent: 0.0,
+ cacheExtent: 200.0,
+ visible: false,
+ );
+ final RenderSliverToBoxAdapter lastPreCachePadded = adapters[26];
+ expectSliverConstraints(
+ sliver: lastPreCachePadded,
+ cacheOrigin: -150.0,
+ remainingPaintExtent: 600.0,
+ remainingCacheExtent: 150.0 + 600.0 + 250.0,
+ scrollOffset: 150.0,
+ );
+ expectSliverGeometry(
+ sliver: lastPreCachePadded,
+ paintExtent: 0.0,
+ cacheExtent: 100.0,
+ visible: false,
+ );
+
+ lastVisiblePadding = paddings[29];
+ expectSliverConstraints(
+ sliver: lastVisiblePadding,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 200.0,
+ remainingCacheExtent: 200.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisiblePadding,
+ paintExtent: 200.0,
+ cacheExtent: 200.0,
+ visible: true,
+ );
+ lastVisiblePadded = adapters[29];
+ expectSliverConstraints(
+ sliver: lastVisiblePadded,
+ cacheOrigin: 0.0,
+ remainingPaintExtent: 150.0,
+ remainingCacheExtent: 150.0 + 250.0,
+ scrollOffset: 0.0,
+ );
+ expectSliverGeometry(
+ sliver: lastVisiblePadded,
+ paintExtent: 100.0,
+ cacheExtent: 100.0,
+ visible: true,
+ );
+ });
+}
+
+void expectSliverConstraints({RenderSliver sliver, double cacheOrigin, double remainingPaintExtent, double remainingCacheExtent, double scrollOffset}) {
+ expect(sliver.constraints.cacheOrigin, cacheOrigin, reason: 'cacheOrigin');
+ expect(sliver.constraints.remainingPaintExtent, remainingPaintExtent, reason: 'remainingPaintExtent');
+ expect(sliver.constraints.remainingCacheExtent, remainingCacheExtent, reason: 'remainingCacheExtent');
+ expect(sliver.constraints.scrollOffset, scrollOffset, reason: 'scrollOffset');
+}
+
+void expectSliverGeometry({RenderSliver sliver, double paintExtent, double cacheExtent, bool visible}) {
+ expect(sliver.geometry.paintExtent, paintExtent, reason: 'paintExtent');
+ expect(sliver.geometry.cacheExtent, cacheExtent, reason: 'cacheExtent');
+ expect(sliver.geometry.visible, visible, reason: 'visible');
+}
+
+class TestRenderSliverBoxChildManager extends RenderSliverBoxChildManager {
+ TestRenderSliverBoxChildManager({
+ this.children,
+ });
+
+ RenderSliverMultiBoxAdaptor _renderObject;
+ List<RenderBox> children;
+
+ RenderSliverList createRenderSliverList() {
+ assert(_renderObject == null);
+ _renderObject = new RenderSliverList(childManager: this);
+ return _renderObject;
+ }
+
+ RenderSliverFixedExtentList createRenderSliverFixedExtentList() {
+ assert(_renderObject == null);
+ _renderObject = new RenderSliverFixedExtentList(
+ childManager: this,
+ itemExtent: 100.0,
+ );
+ return _renderObject;
+ }
+
+ RenderSliverGrid createRenderSliverGrid() {
+ assert(_renderObject == null);
+ _renderObject = new RenderSliverGrid(
+ childManager: this,
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 2,
+ childAspectRatio: 4.0,
+ ),
+ );
+ return _renderObject;
+ }
+
+ int _currentlyUpdatingChildIndex;
+
+ @override
+ void createChild(int index, { @required RenderBox after }) {
+ if (index < 0 || index >= children.length)
+ return null;
+ try {
+ _currentlyUpdatingChildIndex = index;
+ _renderObject.insert(children[index], after: after);
+ } finally {
+ _currentlyUpdatingChildIndex = null;
+ }
+ }
+
+ @override
+ void removeChild(RenderBox child) {
+ _renderObject.remove(child);
+ }
+
+ @override
+ double estimateMaxScrollOffset(SliverConstraints constraints, {
+ int firstIndex,
+ int lastIndex,
+ double leadingScrollOffset,
+ double trailingScrollOffset,
+ }) {
+ assert(lastIndex >= firstIndex);
+ return children.length * (trailingScrollOffset - leadingScrollOffset) / (lastIndex - firstIndex + 1);
+ }
+
+ @override
+ int get childCount => children.length;
+
+ @override
+ void didAdoptChild(RenderBox child) {
+ assert(_currentlyUpdatingChildIndex != null);
+ final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
+ childParentData.index = _currentlyUpdatingChildIndex;
+ }
+
+ @override
+ void setDidUnderflow(bool value) { }
+}
diff --git a/packages/flutter/test/rendering/slivers_block_test.dart b/packages/flutter/test/rendering/slivers_block_test.dart
index db28533..f815cd5 100644
--- a/packages/flutter/test/rendering/slivers_block_test.dart
+++ b/packages/flutter/test/rendering/slivers_block_test.dart
@@ -83,6 +83,7 @@
axisDirection: AxisDirection.down,
crossAxisDirection: AxisDirection.right,
offset: new ViewportOffset.zero(),
+ cacheExtent: 0.0,
children: <RenderSliver>[
inner = childManager.createRenderObject(),
],
@@ -161,6 +162,7 @@
children: <RenderSliver>[
inner = childManager.createRenderObject(),
],
+ cacheExtent: 0.0,
);
layout(root);
diff --git a/packages/flutter/test/rendering/slivers_helpers_test.dart b/packages/flutter/test/rendering/slivers_helpers_test.dart
index 230e24c..6efe4f1 100644
--- a/packages/flutter/test/rendering/slivers_helpers_test.dart
+++ b/packages/flutter/test/rendering/slivers_helpers_test.dart
@@ -27,6 +27,8 @@
crossAxisExtent: 0.0,
crossAxisDirection: AxisDirection.right,
viewportMainAxisExtent: 0.0,
+ cacheOrigin: 0.0,
+ remainingCacheExtent: 0.0,
);
final SliverConstraints b = a.copyWith();
expect(a, equals(b));
@@ -55,6 +57,8 @@
crossAxisExtent: 40.0,
crossAxisDirection: AxisDirection.right,
viewportMainAxisExtent: 30.0,
+ cacheOrigin: 0.0,
+ remainingCacheExtent: 0.0,
);
expect(c, equals(d));
expect(c.normalizedGrowthDirection, equals(GrowthDirection.forward));
diff --git a/packages/flutter/test/rendering/slivers_test.dart b/packages/flutter/test/rendering/slivers_test.dart
index 550ef31..7fa4f0e 100644
--- a/packages/flutter/test/rendering/slivers_test.dart
+++ b/packages/flutter/test/rendering/slivers_test.dart
@@ -86,9 +86,10 @@
' │ │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ │ crossAxisDirection: AxisDirection.right,\n'
- ' │ │ viewportMainAxisExtent: 600.0)\n'
+ ' │ │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
+ ' │ │ cacheOrigin: 0.0 )\n'
' │ │ geometry: SliverGeometry(scrollExtent: 400.0, paintExtent: 400.0,\n'
- ' │ │ maxPaintExtent: 400.0)\n'
+ ' │ │ maxPaintExtent: 400.0, cacheExtent: 400.0)\n'
' │ │\n'
' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
' │ parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
@@ -101,9 +102,11 @@
' │ │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ │ 0.0, remainingPaintExtent: 200.0, crossAxisExtent: 800.0,\n'
' │ │ crossAxisDirection: AxisDirection.right,\n'
- ' │ │ viewportMainAxisExtent: 600.0)\n'
+ ' │ │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 450.0\n'
+ ' │ │ cacheOrigin: 0.0 )\n'
' │ │ geometry: SliverGeometry(scrollExtent: 400.0, paintExtent: 200.0,\n'
- ' │ │ maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
+ ' │ │ maxPaintExtent: 400.0, hasVisualOverflow: true, cacheExtent:\n'
+ ' │ │ 400.0)\n'
' │ │\n'
' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
' │ parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
@@ -111,14 +114,16 @@
' │ size: Size(800.0, 400.0)\n'
' │\n'
' ├─child 2: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
- ' │ │ parentData: paintOffset=Offset(0.0, 600.0) (can use size)\n'
+ ' │ │ parentData: paintOffset=Offset(0.0, 800.0) (can use size)\n'
' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
' │ │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ │ 0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
' │ │ crossAxisDirection: AxisDirection.right,\n'
- ' │ │ viewportMainAxisExtent: 600.0)\n'
+ ' │ │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 50.0\n'
+ ' │ │ cacheOrigin: 0.0 )\n'
' │ │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
- ' │ │ maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
+ ' │ │ maxPaintExtent: 400.0, hasVisualOverflow: true, cacheExtent:\n'
+ ' │ │ 50.0)\n'
' │ │\n'
' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
' │ parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
@@ -126,12 +131,13 @@
' │ size: Size(800.0, 400.0)\n'
' │\n'
' ├─child 3: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
- ' │ │ parentData: paintOffset=Offset(0.0, 600.0) (can use size)\n'
+ ' │ │ parentData: paintOffset=Offset(0.0, 1200.0) (can use size)\n'
' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
' │ │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ │ 0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
' │ │ crossAxisDirection: AxisDirection.right,\n'
- ' │ │ viewportMainAxisExtent: 600.0)\n'
+ ' │ │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 0.0\n'
+ ' │ │ cacheOrigin: 0.0 )\n'
' │ │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
' │ │ maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
' │ │\n'
@@ -141,12 +147,13 @@
' │ size: Size(800.0, 400.0)\n'
' │\n'
' └─child 4: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
- ' │ parentData: paintOffset=Offset(0.0, 600.0) (can use size)\n'
+ ' │ parentData: paintOffset=Offset(0.0, 1600.0) (can use size)\n'
' │ constraints: SliverConstraints(AxisDirection.down,\n'
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 0.0\n'
+ ' │ cacheOrigin: 0.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
' │ maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
' │\n'
@@ -158,23 +165,23 @@
);
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
- expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1200.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1600.0));
expect(a.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 400.0));
expect(b.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 800.0));
- expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
- expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
- expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
+ expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1200.0));
+ expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1600.0));
+ expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 2000.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1400.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -182,7 +189,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -190,7 +197,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
final HitTestResult result = new HitTestResult();
root.hitTest(result, position: const Offset(130.0, 150.0));
@@ -218,17 +225,17 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
- expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1000.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1400.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1200.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -236,7 +243,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -244,7 +251,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
final HitTestResult result = new HitTestResult();
root.hitTest(result, position: const Offset(150.0, 350.0));
@@ -284,28 +291,28 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1200.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1600.0, 0.0));
expect(_getPaintOrigin(sliverA), const Offset(0.0, 0.0));
expect(_getPaintOrigin(sliverB), const Offset(400.0, 0.0));
expect(_getPaintOrigin(sliverC), const Offset(800.0, 0.0));
- expect(_getPaintOrigin(sliverD), const Offset(800.0, 0.0));
- expect(_getPaintOrigin(sliverE), const Offset(800.0, 0.0));
+ expect(_getPaintOrigin(sliverD), const Offset(1200.0, 0.0));
+ expect(_getPaintOrigin(sliverE), const Offset(1600.0, 0.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1400.0, 0.0));
expect(_getPaintOrigin(sliverA), const Offset(000.0, 0.0));
expect(_getPaintOrigin(sliverB), const Offset(200.0, 0.0));
expect(_getPaintOrigin(sliverC), const Offset(600.0, 0.0));
- expect(_getPaintOrigin(sliverD), const Offset(800.0, 0.0));
- expect(_getPaintOrigin(sliverE), const Offset(800.0, 0.0));
+ expect(_getPaintOrigin(sliverD), const Offset(1000.0, 0.0));
+ expect(_getPaintOrigin(sliverE), const Offset(1400.0, 0.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -313,13 +320,13 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
expect(_getPaintOrigin(sliverA), const Offset(000.0, 0.0));
expect(_getPaintOrigin(sliverB), const Offset(000.0, 0.0));
expect(_getPaintOrigin(sliverC), const Offset(200.0, 0.0));
expect(_getPaintOrigin(sliverD), const Offset(600.0, 0.0));
- expect(_getPaintOrigin(sliverE), const Offset(800.0, 0.0));
+ expect(_getPaintOrigin(sliverE), const Offset(1000.0, 0.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -362,16 +369,16 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-800.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1200.0, 0.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1000.0, 0.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -379,7 +386,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -429,23 +436,23 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
- expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1200.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1600.0));
expect(a.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 400.0));
expect(b.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 800.0));
- expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
- expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
- expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1000.0));
+ expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1200.0));
+ expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1600.0));
+ expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 2000.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1400.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -453,7 +460,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -461,7 +468,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
final HitTestResult result = new HitTestResult();
root.hitTest(result, position: const Offset(130.0, 150.0));
@@ -489,17 +496,17 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
- expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1000.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1400.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1200.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -507,7 +514,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -515,7 +522,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
final HitTestResult result = new HitTestResult();
root.hitTest(result, position: const Offset(150.0, 350.0));
@@ -544,16 +551,16 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1200.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1600.0, 0.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1400.0, 0.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -561,7 +568,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -598,16 +605,16 @@
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-800.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1200.0, 0.0));
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
- expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1000.0, 0.0));
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
@@ -615,7 +622,7 @@
expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
- expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
+ expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
@@ -684,7 +691,7 @@
visible: true,
).toString(),
equals(
- 'SliverGeometry(scrollExtent: 100.0, paintExtent: 50.0, layoutExtent: 20.0, maxPaintExtent: 0.0)',
+ 'SliverGeometry(scrollExtent: 100.0, paintExtent: 50.0, layoutExtent: 20.0, maxPaintExtent: 0.0, cacheExtent: 20.0)',
),
);
expect(
@@ -694,7 +701,7 @@
layoutExtent: 20.0,
).toString(),
equals(
- 'SliverGeometry(scrollExtent: 100.0, hidden, layoutExtent: 20.0, maxPaintExtent: 0.0)',
+ 'SliverGeometry(scrollExtent: 100.0, hidden, layoutExtent: 20.0, maxPaintExtent: 0.0, cacheExtent: 20.0)',
),
);
});
diff --git a/packages/flutter/test/widgets/animated_list_test.dart b/packages/flutter/test/widgets/animated_list_test.dart
index 08bde08..ffd43ed 100644
--- a/packages/flutter/test/widgets/animated_list_test.dart
+++ b/packages/flutter/test/widgets/animated_list_test.dart
@@ -60,9 +60,9 @@
),
);
- double itemHeight(int index) => tester.getSize(find.byKey(new ValueKey<int>(index))).height;
- double itemTop(int index) => tester.getTopLeft(find.byKey(new ValueKey<int>(index))).dy;
- double itemBottom(int index) => tester.getBottomLeft(find.byKey(new ValueKey<int>(index))).dy;
+ double itemHeight(int index) => tester.getSize(find.byKey(new ValueKey<int>(index), skipOffstage: false)).height;
+ double itemTop(int index) => tester.getTopLeft(find.byKey(new ValueKey<int>(index), skipOffstage: false)).dy;
+ double itemBottom(int index) => tester.getBottomLeft(find.byKey(new ValueKey<int>(index), skipOffstage: false)).dy;
listKey.currentState.insertItem(0, duration: const Duration(milliseconds: 100));
await tester.pump();
diff --git a/packages/flutter/test/widgets/automatic_keep_alive_test.dart b/packages/flutter/test/widgets/automatic_keep_alive_test.dart
index 9fb9b04..1d46c12 100644
--- a/packages/flutter/test/widgets/automatic_keep_alive_test.dart
+++ b/packages/flutter/test/widgets/automatic_keep_alive_test.dart
@@ -71,41 +71,43 @@
addAutomaticKeepAlives: impliedMode,
addRepaintBoundaries: impliedMode,
itemExtent: 12.3, // about 50 widgets visible
+ cacheExtent: 0.0,
children: generateList(const Placeholder(), impliedMode: impliedMode),
),
),
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
testWidgets('AutomaticKeepAlive with ListView without itemExtent', (WidgetTester tester) async {
@@ -115,6 +117,7 @@
child: new ListView(
addAutomaticKeepAlives: impliedMode,
addRepaintBoundaries: impliedMode,
+ cacheExtent: 0.0,
children: generateList(
new Container(height: 12.3, child: const Placeholder()), // about 50 widgets visible
impliedMode: impliedMode,
@@ -124,35 +127,36 @@
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
testWidgets('AutomaticKeepAlive with GridView', (WidgetTester tester) async {
@@ -164,6 +168,7 @@
addRepaintBoundaries: impliedMode,
crossAxisCount: 2,
childAspectRatio: 400.0 / 24.6, // about 50 widgets visible
+ cacheExtent: 0.0,
children: generateList(
new Container(child: const Placeholder()),
impliedMode: impliedMode,
@@ -173,35 +178,36 @@
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(GridView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(GridView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
}
@@ -216,6 +222,7 @@
child: new ListView(
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
+ cacheExtent: 0.0,
children: <Widget>[
new AutomaticKeepAlive(
child: new Container(
@@ -245,11 +252,11 @@
expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0)); // move to bottom
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
await tester.drag(find.byType(ListView), const Offset(0.0, 1000.0)); // move to top
@@ -257,41 +264,48 @@
expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0)); // move to bottom
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- const GlobalObjectKey<_LeafState>(1).currentState.setKeepAlive(true);
- await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(false);
- await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- const GlobalObjectKey<_LeafState>(1).currentState.setKeepAlive(false);
- await tester.pump();
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
+ const GlobalObjectKey<_LeafState>(1).currentState.setKeepAlive(true);
+ await tester.pump();
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
+ const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(false);
+ await tester.pump();
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
+ const GlobalObjectKey<_LeafState>(1).currentState.setKeepAlive(false);
+ await tester.pump();
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
});
- testWidgets('AutomaticKeepAlive double', (WidgetTester tester) async {
+ testWidgets('AutomaticKeepAlive double 2', (WidgetTester tester) async {
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
+ cacheExtent: 0.0,
children: <Widget>[
new AutomaticKeepAlive(
child: new Container(
@@ -328,13 +342,15 @@
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(5)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(4), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(5), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0)); // move to bottom
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsOneWidget);
@@ -344,6 +360,7 @@
child: new ListView(
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
+ cacheExtent: 0.0,
children: <Widget>[
new AutomaticKeepAlive(
child: new Container(
@@ -376,7 +393,7 @@
),
));
await tester.pump(); // Sometimes AutomaticKeepAlive needs an extra pump to clean things up.
- expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(1), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsOneWidget);
@@ -387,22 +404,26 @@
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(5)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(4), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(5), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(5)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(5)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(4), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(5), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsNothing);
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
+ cacheExtent: 0.0,
children: <Widget>[
new AutomaticKeepAlive(
child: new Container(
@@ -437,10 +458,10 @@
await tester.pump(); // Sometimes AutomaticKeepAlive needs an extra pump to clean things up.
expect(find.byKey(const GlobalObjectKey<_LeafState>(1)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(2)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(4)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(5)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(0)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(4), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(5), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(0), skipOffstage: false), findsNothing);
});
testWidgets('AutomaticKeepAlive with keepAlive set to true before initState', (WidgetTester tester) async {
@@ -469,9 +490,9 @@
expect(find.byKey(const GlobalObjectKey<_AlwaysKeepAliveState>(0)), findsOneWidget);
await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0)); // move to bottom
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_AlwaysKeepAliveState>(0)), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_AlwaysKeepAliveState>(0), skipOffstage: false), findsOneWidget);
- expect(find.text('keep me alive'), findsOneWidget);
+ expect(find.text('keep me alive', skipOffstage: false), findsOneWidget);
expect(find.text('FooBar 1'), findsNothing);
expect(find.text('FooBar 2'), findsNothing);
});
diff --git a/packages/flutter/test/widgets/grid_view_test.dart b/packages/flutter/test/widgets/grid_view_test.dart
index c40e083..6e8641a 100644
--- a/packages/flutter/test/widgets/grid_view_test.dart
+++ b/packages/flutter/test/widgets/grid_view_test.dart
@@ -168,9 +168,17 @@
0, 1, 2, // col 0
3, 4, 5, // col 1
6, 7, 8, // col 2
+ 9, 10, 11, // col 3 (in cached area)
]));
log.clear();
+ for (int i = 0; i < 9; i++) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i = 9; i < 80; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
+
final ScrollableState state = tester.state(find.byType(Scrollable));
final ScrollPosition position = state.position;
position.jumpTo(3025.0);
@@ -179,25 +187,49 @@
await tester.pump();
expect(log, equals(<int>[
+ 30, 31, 32, // col 10 (in cached area)
33, 34, 35, // col 11
36, 37, 38, // col 12
39, 40, 41, // col 13
42, 43, 44, // col 14
+ 45, 46, 47, // col 15 (in cached area)
]));
log.clear();
+ for (int i = 0; i < 33; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
+ for (int i = 33; i < 45; i++) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i = 45; i < 80; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
+
position.jumpTo(975.0);
expect(log, isEmpty);
await tester.pump();
expect(log, equals(<int>[
+ 6, 7, 8, // col2 (in cached area)
9, 10, 11, // col 3
12, 13, 14, // col 4
15, 16, 17, // col 5
18, 19, 20, // col 6
+ 21, 22, 23, // col 7 (in cached area)
]));
log.clear();
+
+ for (int i = 0; i < 9; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
+ for (int i = 9; i < 21; i++) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i = 21; i < 80; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
});
testWidgets('GridView - change crossAxisCount', (WidgetTester tester) async {
@@ -230,7 +262,15 @@
0, 1, 2, 3, // row 0
4, 5, 6, 7, // row 1
8, 9, 10, 11, // row 2
+ 12, 13, 14, 15, // row 3 (in cached area)
+ 16, 17, 18, 19, // row 4 (in cached area)
]));
+ for (int i = 0; i < 12; i++) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i = 12; i < 40; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
log.clear();
await tester.pumpWidget(
@@ -258,6 +298,8 @@
0, 1, 2, 3, // row 0
4, 5, 6, 7, // row 1
8, 9, 10, 11, // row 2
+ 12, 13, 14, 15, // row 3 (in cached area)
+ 16, 17, 18, 19, // row 4 (in cached area)
]));
log.clear();
@@ -295,7 +337,15 @@
0, 1, 2, 3, // row 0
4, 5, 6, 7, // row 1
8, 9, 10, 11, // row 2
+ 12, 13, 14, 15, // row 3 (in cached area)
+ 16, 17, 18, 19, // row 4 (in cached area)
]));
+ for (int i = 0; i < 12; i++) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i = 12; i < 40; i++) {
+ expect(find.text('$i'), findsNothing);
+ }
log.clear();
await tester.pumpWidget(
@@ -323,6 +373,8 @@
0, 1, 2, 3, // row 0
4, 5, 6, 7, // row 1
8, 9, 10, 11, // row 2
+ 12, 13, 14, 15, // row 3 (in cached area)
+ 16, 17, 18, 19, // row 4 (in cached area)
]));
log.clear();
@@ -346,6 +398,7 @@
child: new SizedBox(
height: 200.0,
child: new GridView.count(
+ cacheExtent: 0.0,
crossAxisCount: 2,
children: <Widget>[ container, container, container, container ],
),
@@ -379,7 +432,7 @@
),
);
- expect(find.text('0'), findsOneWidget);
+ expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
});
diff --git a/packages/flutter/test/widgets/heroes_test.dart b/packages/flutter/test/widgets/heroes_test.dart
index 85a2afc..7ed838d 100644
--- a/packages/flutter/test/widgets/heroes_test.dart
+++ b/packages/flutter/test/widgets/heroes_test.dart
@@ -781,6 +781,7 @@
builder: (BuildContext context) {
return new Material(
child: new ListView(
+ cacheExtent: 0.0,
children: <Widget>[
const SizedBox(height: 100.0),
// This container will appear at Y=100
diff --git a/packages/flutter/test/widgets/keep_alive_test.dart b/packages/flutter/test/widgets/keep_alive_test.dart
index fb4d8a1..566fa0e 100644
--- a/packages/flutter/test/widgets/keep_alive_test.dart
+++ b/packages/flutter/test/widgets/keep_alive_test.dart
@@ -47,6 +47,7 @@
new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(
+ cacheExtent: 0.0,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
itemExtent: 12.3, // about 50 widgets visible
@@ -56,35 +57,36 @@
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
testWidgets('KeepAlive with ListView without itemExtent', (WidgetTester tester) async {
@@ -92,6 +94,7 @@
new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(
+ cacheExtent: 0.0,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
children: generateList(new Container(height: 12.3, child: const Placeholder())), // about 50 widgets visible
@@ -100,35 +103,36 @@
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
testWidgets('KeepAlive with GridView', (WidgetTester tester) async {
@@ -136,6 +140,7 @@
new Directionality(
textDirection: TextDirection.ltr,
child: new GridView.count(
+ cacheExtent: 0.0,
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
crossAxisCount: 2,
@@ -146,35 +151,36 @@
);
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
await tester.drag(find.byType(GridView), const Offset(0.0, -300.0)); // about 25 widgets' worth
await tester.pump();
- expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
await tester.drag(find.byType(GridView), const Offset(0.0, 300.0)); // back to top
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
await tester.pump();
expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsNothing);
- expect(find.byKey(const GlobalObjectKey<_LeafState>(90)), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
+ expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
});
testWidgets('KeepAlive render tree description', (WidgetTester tester) async {
@@ -270,10 +276,12 @@
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
+ ' │ cacheOrigin: 0.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
- ' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
- ' │ currently live children: 0 to 1\n'
+ ' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
+ ' │ cacheExtent: 850.0)\n'
+ ' │ currently live children: 0 to 2\n'
' │\n'
' ├─child with index 0: RenderLimitedBox#00000\n'
' │ │ parentData: index=0; layoutOffset=0.0\n'
@@ -287,8 +295,20 @@
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
' │ size: Size(800.0, 400.0)\n'
' │\n'
- ' └─child with index 1: RenderLimitedBox#00000\n' // <----- no dashed line starts here
- ' │ parentData: index=1; layoutOffset=400.0\n'
+ ' ├─child with index 1: RenderLimitedBox#00000\n' // <----- no dashed line starts here
+ ' │ │ parentData: index=1; layoutOffset=400.0\n'
+ ' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ │ size: Size(800.0, 400.0)\n'
+ ' │ │ maxWidth: 400.0\n'
+ ' │ │ maxHeight: 400.0\n'
+ ' │ │\n'
+ ' │ └─child: RenderCustomPaint#00000\n'
+ ' │ parentData: <none> (can use size)\n'
+ ' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ size: Size(800.0, 400.0)\n'
+ ' │\n'
+ ' └─child with index 2: RenderLimitedBox#00000\n'
+ ' │ parentData: index=2; layoutOffset=800.0\n'
' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
' │ size: Size(800.0, 400.0)\n'
' │ maxWidth: 400.0\n'
@@ -385,10 +405,24 @@
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 2000.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 1100.0\n'
+ ' │ cacheOrigin: -250.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
- ' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true)\n'
- ' │ currently live children: 5 to 6\n'
+ ' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
+ ' │ cacheExtent: 1100.0)\n'
+ ' │ currently live children: 4 to 7\n'
+ ' │\n'
+ ' ├─child with index 4: RenderLimitedBox#00000\n'
+ ' │ │ parentData: index=4; layoutOffset=1600.0\n'
+ ' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ │ size: Size(800.0, 400.0)\n'
+ ' │ │ maxWidth: 400.0\n'
+ ' │ │ maxHeight: 400.0\n'
+ ' │ │\n'
+ ' │ └─child: RenderCustomPaint#00000\n'
+ ' │ parentData: <none> (can use size)\n'
+ ' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ size: Size(800.0, 400.0)\n'
' │\n'
' ├─child with index 5: RenderLimitedBox#00000\n' // <----- this is index 5, not 0
' │ │ parentData: index=5; layoutOffset=2000.0\n'
@@ -403,7 +437,19 @@
' │ size: Size(800.0, 400.0)\n'
' │\n'
' ├─child with index 6: RenderLimitedBox#00000\n'
- ' ╎ │ parentData: index=6; layoutOffset=2400.0\n'
+ ' │ │ parentData: index=6; layoutOffset=2400.0\n'
+ ' │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ │ size: Size(800.0, 400.0)\n'
+ ' │ │ maxWidth: 400.0\n'
+ ' │ │ maxHeight: 400.0\n'
+ ' │ │\n'
+ ' │ └─child: RenderCustomPaint#00000\n'
+ ' │ parentData: <none> (can use size)\n'
+ ' │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
+ ' │ size: Size(800.0, 400.0)\n'
+ ' │\n'
+ ' ├─child with index 7: RenderLimitedBox#00000\n'
+ ' ╎ │ parentData: index=7; layoutOffset=2800.0\n'
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
' ╎ │ size: Size(800.0, 400.0)\n'
' ╎ │ maxWidth: 400.0\n'
diff --git a/packages/flutter/test/widgets/list_view_builder_test.dart b/packages/flutter/test/widgets/list_view_builder_test.dart
index 26e7e78..384de2a 100644
--- a/packages/flutter/test/widgets/list_view_builder_test.dart
+++ b/packages/flutter/test/widgets/list_view_builder_test.dart
@@ -38,7 +38,11 @@
final FlipWidgetState testWidget = tester.state(find.byType(FlipWidget));
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2, 3, 4, 5, // visible in viewport
+ 6, 7, 8, // in caching area
+ ]));
+ check(visible: <int>[0, 1, 2, 3, 4, 5], hidden: <int>[ 6, 7, 8]);
callbackTracker.clear();
testWidget.flip();
@@ -50,7 +54,11 @@
testWidget.flip();
await tester.pump();
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, // in caching area
+ ]));
+ check(visible: <int>[0, 1, 2, 3, 4, 5], hidden: <int>[ 6, 7, 8]);
});
testWidgets('ListView.builder vertical', (WidgetTester tester) async {
@@ -91,7 +99,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[1, 2, 3, 4]));
+ expect(callbackTracker, equals(<int>[
+ 0, // in caching area
+ 1, 2, 3, 4,
+ 5, // in caching area
+ ]));
+ check(visible: <int>[1, 2, 3, 4], hidden: <int>[0, 5]);
callbackTracker.clear();
jumpTo(400.0);
@@ -99,12 +112,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[1, 2, 3, 4]));
- callbackTracker.clear();
-
- await tester.pumpWidget(buildWidget());
-
- expect(callbackTracker, equals(<int>[2, 3, 4]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, // in caching area
+ 2, 3, 4,
+ 5, 6, // in caching area
+ ]));
+ check(visible: <int>[2, 3, 4], hidden: <int>[0, 1, 5, 6]);
callbackTracker.clear();
jumpTo(500.0);
@@ -112,7 +125,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, // in caching area
+ 2, 3, 4, 5,
+ 6, // in caching area
+ ]));
+ check(visible: <int>[2, 3, 4, 5], hidden: <int>[0, 1, 6]);
callbackTracker.clear();
});
@@ -155,8 +173,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5]));
-
+ expect(callbackTracker, equals(<int>[
+ 0, // in caching area
+ 1, 2, 3, 4, 5,
+ 6, // in caching area
+ ]));
+ check(visible: <int>[1, 2, 3, 4, 5], hidden: <int>[0, 6]);
callbackTracker.clear();
jumpTo(400.0);
@@ -164,12 +186,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5]));
- callbackTracker.clear();
-
- await tester.pumpWidget(buildWidget());
-
- expect(callbackTracker, equals(<int>[2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, // in caching area
+ 2, 3, 4, 5,
+ 6, 7, // in caching area
+ ]));
+ check(visible: <int>[2, 3, 4, 5], hidden: <int>[0, 1, 6, 7]);
callbackTracker.clear();
jumpTo(500.0);
@@ -177,7 +199,12 @@
await tester.pumpWidget(buildWidget());
- expect(callbackTracker, equals(<int>[2, 3, 4, 5, 6]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, // in caching area
+ 2, 3, 4, 5, 6,
+ 7, // in caching area
+ ]));
+ check(visible: <int>[2, 3, 4, 5, 6], hidden: <int>[0, 1, 7]);
callbackTracker.clear();
});
@@ -208,26 +235,39 @@
}
await tester.pumpWidget(testWidget);
- expect(callbackTracker, equals(<int>[0, 1]));
+ expect(callbackTracker, equals(<int>[0, 1, 2]));
+ check(visible: <int>[0, 1], hidden: <int>[2]);
callbackTracker.clear();
jumpTo(150.0);
await tester.pump();
- expect(callbackTracker, equals(<int>[2]));
+ expect(callbackTracker, equals(<int>[3]));
+ check(visible: <int>[0, 1, 2], hidden: <int>[3]);
callbackTracker.clear();
jumpTo(600.0);
await tester.pump();
- expect(callbackTracker, equals(<int>[3]));
+ expect(callbackTracker, equals(<int>[4]));
+ check(visible: <int>[2, 3], hidden: <int>[0, 1, 4]);
callbackTracker.clear();
jumpTo(750.0);
await tester.pump();
- expect(callbackTracker, equals(<int>[4]));
+ expect(callbackTracker, equals(<int>[5]));
+ check(visible: <int>[2, 3, 4], hidden: <int>[0, 1, 5]);
callbackTracker.clear();
});
}
+
+void check({List<int> visible: const <int>[], List<int> hidden: const <int>[]}) {
+ for (int i in visible) {
+ expect(find.text('$i'), findsOneWidget);
+ }
+ for (int i in hidden) {
+ expect(find.text('$i'), findsNothing);
+ }
+}
diff --git a/packages/flutter/test/widgets/list_view_correction_test.dart b/packages/flutter/test/widgets/list_view_correction_test.dart
index 7f9c239..cb5c777 100644
--- a/packages/flutter/test/widgets/list_view_correction_test.dart
+++ b/packages/flutter/test/widgets/list_view_correction_test.dart
@@ -12,6 +12,65 @@
new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(
+ cacheExtent: 0.0,
+ controller: controller,
+ children: <Widget>[
+ new Container(height: 400.0, child: const Text('1')),
+ new Container(height: 400.0, child: const Text('2')),
+ new Container(height: 400.0, child: const Text('3')),
+ new Container(height: 400.0, child: const Text('4')),
+ new Container(height: 400.0, child: const Text('5')),
+ new Container(height: 400.0, child: const Text('6')),
+ ],
+ ),
+ ),
+ );
+
+ controller.jumpTo(1000.0);
+ await tester.pump();
+
+ expect(tester.getTopLeft(find.text('4')).dy, equals(200.0));
+
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new ListView(
+ cacheExtent: 0.0,
+ controller: controller,
+ children: <Widget>[
+ new Container(height: 200.0, child: const Text('1')),
+ new Container(height: 400.0, child: const Text('2')),
+ new Container(height: 400.0, child: const Text('3')),
+ new Container(height: 400.0, child: const Text('4')),
+ new Container(height: 400.0, child: const Text('5')),
+ new Container(height: 400.0, child: const Text('6')),
+ ],
+ ),
+ ),
+ );
+
+ expect(controller.offset, equals(1000.0));
+ expect(tester.getTopLeft(find.text('4')).dy, equals(200.0));
+
+ controller.jumpTo(300.0);
+ await tester.pump();
+
+ expect(controller.offset, equals(300.0));
+ expect(tester.getTopLeft(find.text('2')).dy, equals(100.0));
+
+ controller.jumpTo(50.0);
+ await tester.pump();
+
+ expect(controller.offset, equals(0.0));
+ expect(tester.getTopLeft(find.text('2')).dy, equals(200.0));
+ });
+
+ testWidgets('ListView can handle shrinking top elements with cache extent', (WidgetTester tester) async {
+ final ScrollController controller = new ScrollController();
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new ListView(
controller: controller,
children: <Widget>[
new Container(height: 400.0, child: const Text('1')),
@@ -53,14 +112,14 @@
controller.jumpTo(300.0);
await tester.pump();
- expect(tester.getTopLeft(find.text('2')).dy, equals(100.0));
+ expect(controller.offset, equals(250.0));
+ expect(tester.getTopLeft(find.text('2')).dy, equals(-50.0));
controller.jumpTo(50.0);
-
await tester.pump();
- expect(controller.offset, equals(0.0));
- expect(tester.getTopLeft(find.text('2')).dy, equals(200.0));
+ expect(controller.offset, equals(50.0));
+ expect(tester.getTopLeft(find.text('2')).dy, equals(150.0));
});
testWidgets('ListView can handle inserts at 0', (WidgetTester tester) async {
diff --git a/packages/flutter/test/widgets/list_view_misc_test.dart b/packages/flutter/test/widgets/list_view_misc_test.dart
index c3898d6..d3ed59a 100644
--- a/packages/flutter/test/widgets/list_view_misc_test.dart
+++ b/packages/flutter/test/widgets/list_view_misc_test.dart
@@ -150,7 +150,7 @@
),
);
- final SliverMultiBoxAdaptorElement element = tester.element(find.byType(SliverList));
+ final SliverMultiBoxAdaptorElement element = tester.element(find.byType(SliverList, skipOffstage: false));
final double maxScrollOffset = element.estimateMaxScrollOffset(
null,
diff --git a/packages/flutter/test/widgets/list_view_test.dart b/packages/flutter/test/widgets/list_view_test.dart
index 3836199..4fbb483 100644
--- a/packages/flutter/test/widgets/list_view_test.dart
+++ b/packages/flutter/test/widgets/list_view_test.dart
@@ -96,7 +96,7 @@
),
);
- expect(log, equals(<int>[0, 1, 2]));
+ expect(log, equals(<int>[0, 1, 2, 3, 4]));
log.clear();
final ScrollableState state = tester.state(find.byType(Scrollable));
@@ -106,7 +106,7 @@
expect(log, isEmpty);
await tester.pump();
- expect(log, equals(<int>[10, 11, 12, 13]));
+ expect(log, equals(<int>[8, 9, 10, 11, 12, 13, 14]));
log.clear();
position.jumpTo(975.0);
@@ -114,7 +114,7 @@
expect(log, isEmpty);
await tester.pump();
- expect(log, equals(<int>[4, 5, 6, 7]));
+ expect(log, equals(<int>[7, 6, 5, 4, 3]));
log.clear();
});
@@ -196,7 +196,7 @@
),
),
);
- expect(find.text('padded'), findsOneWidget);
+ expect(find.text('padded', skipOffstage: false), findsOneWidget);
});
testWidgets('ListView with itemExtent in unbounded context', (WidgetTester tester) async {
@@ -240,7 +240,7 @@
),
);
- expect(delegate.log, equals(<String>['didFinishLayout firstIndex=0 lastIndex=5']));
+ expect(delegate.log, equals(<String>['didFinishLayout firstIndex=0 lastIndex=7']));
delegate.log.clear();
await tester.pumpWidget(
@@ -253,7 +253,7 @@
),
);
- expect(delegate.log, equals(<String>['didFinishLayout firstIndex=0 lastIndex=2']));
+ expect(delegate.log, equals(<String>['didFinishLayout firstIndex=0 lastIndex=4']));
delegate.log.clear();
await tester.drag(find.byType(ListView), const Offset(0.0, -600.0));
@@ -262,7 +262,7 @@
await tester.pump();
- expect(delegate.log, equals(<String>['didFinishLayout firstIndex=2 lastIndex=5']));
+ expect(delegate.log, equals(<String>['didFinishLayout firstIndex=1 lastIndex=6']));
delegate.log.clear();
});
diff --git a/packages/flutter/test/widgets/list_view_viewporting_test.dart b/packages/flutter/test/widgets/list_view_viewporting_test.dart
index 518695e..fc8fbf7 100644
--- a/packages/flutter/test/widgets/list_view_viewporting_test.dart
+++ b/packages/flutter/test/widgets/list_view_viewporting_test.dart
@@ -38,7 +38,10 @@
final FlipWidgetState testWidget = tester.state(find.byType(FlipWidget));
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2, 3, 4, 5, // visible
+ 6, 7, 8 // in cached area
+ ]));
callbackTracker.clear();
testWidget.flip();
@@ -50,7 +53,10 @@
testWidget.flip();
await tester.pump();
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2, 3, 4, 5, // visible
+ 6, 7, 8, // in cached area
+ ]));
});
testWidgets('ListView vertical', (WidgetTester tester) async {
@@ -86,22 +92,34 @@
await tester.pumpWidget(builder());
// 0 is built to find its height
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2, 3, 4,
+ 5, // in cached area
+ ]));
callbackTracker.clear();
final ScrollableState scrollable = tester.state(find.byType(Scrollable));
- scrollable.position.jumpTo(400.0); // now only 3 should fit, numbered 2-4.
+ scrollable.position.jumpTo(600.0); // now only 3 should fit, numbered 3-5.
await tester.pumpWidget(builder());
// We build the visible children to find their new size.
- expect(callbackTracker, equals(<int>[1, 2, 3, 4]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2,
+ 3, 4, 5, //visible
+ 6, 7
+ ]));
callbackTracker.clear();
await tester.pumpWidget(builder());
// 0 isn't built because they're not visible.
- expect(callbackTracker, equals(<int>[1, 2, 3, 4]));
+ expect(callbackTracker, equals(<int>[
+ 1, 2,
+ 3, 4, 5, // visible
+ 6, 7,
+ ]
+ ));
callbackTracker.clear();
});
@@ -128,7 +146,7 @@
child: new FlipWidget(
left: new ListView.builder(
scrollDirection: Axis.horizontal,
- controller: new ScrollController(initialScrollOffset: 300.0),
+ controller: new ScrollController(initialScrollOffset: 500.0),
itemBuilder: itemBuilder,
),
right: const Text('Not Today'),
@@ -139,23 +157,23 @@
await tester.pumpWidget(builder());
// 0 is built to find its width
- expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[0, 1, 2, 3, 4, 5, 6, 7]));
callbackTracker.clear();
final ScrollableState scrollable = tester.state(find.byType(Scrollable));
- scrollable.position.jumpTo(400.0); // now only 4 should fit, numbered 2-5.
+ scrollable.position.jumpTo(600.0); // now only 4 should fit, numbered 2-5.
await tester.pumpWidget(builder());
// We build the visible children to find their new size.
- expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5, 6, 7, 8]));
callbackTracker.clear();
await tester.pumpWidget(builder());
// 0 isn't built because they're not visible.
- expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5]));
+ expect(callbackTracker, equals(<int>[1, 2, 3, 4, 5, 6, 7, 8]));
callbackTracker.clear();
});
@@ -189,18 +207,24 @@
await tester.pumpWidget(builder());
- expect(callbackTracker, equals(<int>[0, 1, 2]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2,
+ 3, // in cached area
+ ]));
callbackTracker.clear();
tester.allWidgets.forEach(collectText);
- expect(text, equals(<String>['0', '1', '2']));
+ expect(text, equals(<String>['0', '1', '2', '3']));
text.clear();
await tester.pumpWidget(builder());
- expect(callbackTracker, equals(<int>[0, 1, 2]));
+ expect(callbackTracker, equals(<int>[
+ 0, 1, 2,
+ 3, // in cached area
+ ]));
callbackTracker.clear();
tester.allWidgets.forEach(collectText);
- expect(text, equals(<String>['0', '1', '2']));
+ expect(text, equals(<String>['0', '1', '2', '3']));
text.clear();
});
@@ -308,9 +332,10 @@
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
+ ' │ cacheOrigin: 0.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 300.0, paintExtent: 300.0,\n'
- ' │ maxPaintExtent: 300.0)\n'
+ ' │ maxPaintExtent: 300.0, cacheExtent: 300.0)\n'
' │ currently live children: 0 to 2\n'
' │\n'
' ├─child with index 0: RenderRepaintBoundary#00000 relayoutBoundary=up2\n'
diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart
index b879116..a08e742 100644
--- a/packages/flutter/test/widgets/nested_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart
@@ -339,7 +339,7 @@
final Offset point1 = tester.getCenter(find.text('AA'));
await tester.dragFrom(point1, const Offset(0.0, 200.0));
await tester.pump(const Duration(milliseconds: 20));
- final Offset point2 = tester.getCenter(find.text('AA'));
+ final Offset point2 = tester.getCenter(find.text('AA', skipOffstage: false));
expect(point1.dy, greaterThan(point2.dy));
});
@@ -646,4 +646,4 @@
}
@override
bool shouldRebuild(TestHeader oldDelegate) => false;
-}
\ No newline at end of file
+}
diff --git a/packages/flutter/test/widgets/page_view_test.dart b/packages/flutter/test/widgets/page_view_test.dart
index 6a4fbe8..ead8fab 100644
--- a/packages/flutter/test/widgets/page_view_test.dart
+++ b/packages/flutter/test/widgets/page_view_test.dart
@@ -257,7 +257,7 @@
),
));
- expect(find.text('Alabama'), findsOneWidget);
+ expect(find.text('Alabama', skipOffstage: false), findsOneWidget);
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
diff --git a/packages/flutter/test/widgets/scrollable_semantics_test.dart b/packages/flutter/test/widgets/scrollable_semantics_test.dart
index a2ca2cd..f3a458f 100644
--- a/packages/flutter/test/widgets/scrollable_semantics_test.dart
+++ b/packages/flutter/test/widgets/scrollable_semantics_test.dart
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:ui';
+
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
@@ -362,6 +364,12 @@
label: r'item 1',
textDirection: TextDirection.ltr,
),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'item 2',
+ ),
],
),
],
diff --git a/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart b/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart
new file mode 100644
index 0000000..4211575
--- /dev/null
+++ b/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart
@@ -0,0 +1,813 @@
+// Copyright 2018 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:ui';
+
+import 'package:flutter/rendering.dart';
+import 'package:flutter/semantics.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter/widgets.dart';
+
+import 'semantics_tester.dart';
+
+void main() {
+ testWidgets('Traversal Order of SliverList', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ final List<Widget> listChildren = new List<Widget>.generate(30, (int i) {
+ return new Container(
+ height: 200.0,
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ new Semantics(
+ container: true,
+ child: new Text('Item ${i}a'),
+ ),
+ new Semantics(
+ container: true,
+ child: new Text('item ${i}b'),
+ ),
+ ],
+ ),
+ );
+ });
+ await tester.pumpWidget(
+ new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: new ScrollController(initialScrollOffset: 3000.0),
+ slivers: <Widget>[
+ new SliverList(
+ delegate: new SliverChildListDelegate(listChildren),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 13a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 13b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 14a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 14b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 15a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 15b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 16a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 16b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 17a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 17b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 18a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 18b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 19a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 19b',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ childOrder: DebugSemanticsDumpOrder.traversalOrder,
+ ignoreId: true,
+ ignoreTransform: true,
+ ignoreRect: true,
+ ));
+
+ semantics.dispose();
+ });
+
+ testWidgets('Traversal Order of SliverFixedExtentList', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ final List<Widget> listChildren = new List<Widget>.generate(30, (int i) {
+ return new Container(
+ height: 200.0,
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ new Semantics(
+ container: true,
+ child: new Text('Item ${i}a'),
+ ),
+ new Semantics(
+ container: true,
+ child: new Text('item ${i}b'),
+ ),
+ ],
+ ),
+ );
+ });
+ await tester.pumpWidget(
+ new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: new ScrollController(initialScrollOffset: 3000.0),
+ slivers: <Widget>[
+ new SliverFixedExtentList(
+ itemExtent: 200.0,
+ delegate: new SliverChildListDelegate(listChildren),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 13a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 13b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 14a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 14b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 15a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 15b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 16a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 16b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 17a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 17b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 18a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 18b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 19a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 19b',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ childOrder: DebugSemanticsDumpOrder.traversalOrder,
+ ignoreId: true,
+ ignoreTransform: true,
+ ignoreRect: true,
+ ));
+
+ semantics.dispose();
+ });
+
+ testWidgets('Traversal Order of SliverGrid', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ final List<Widget> listChildren = new List<Widget>.generate(30, (int i) {
+ return new Container(
+ height: 200.0,
+ child: new Text('Item $i'),
+ );
+ });
+ await tester.pumpWidget(
+ new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: new ScrollController(initialScrollOffset: 1600.0),
+ slivers: <Widget>[
+ new SliverGrid.count(
+ crossAxisCount: 2,
+ crossAxisSpacing: 400.0,
+ children: listChildren,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 12',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 13',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 14',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 15',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 16',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 17',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 18',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 19',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 20',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 21',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 22',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 23',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 24',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 25',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ childOrder: DebugSemanticsDumpOrder.traversalOrder,
+ ignoreId: true,
+ ignoreTransform: true,
+ ignoreRect: true,
+ ));
+
+ semantics.dispose();
+ });
+
+ testWidgets('Traversal Order of List of individual slivers', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ final List<Widget> listChildren = new List<Widget>.generate(30, (int i) {
+ return new SliverToBoxAdapter(
+ child: new Container(
+ height: 200.0,
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ new Semantics(
+ container: true,
+ child: new Text('Item ${i}a'),
+ ),
+ new Semantics(
+ container: true,
+ child: new Text('item ${i}b'),
+ ),
+ ],
+ ),
+ ),
+ );
+ });
+ await tester.pumpWidget(
+ new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: new ScrollController(initialScrollOffset: 3000.0),
+ slivers: listChildren,
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 13a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 13b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 14a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 14b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 15a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 15b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 16a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 16b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 17a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 17b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 18a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 18b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 19a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 19b',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ childOrder: DebugSemanticsDumpOrder.traversalOrder,
+ ignoreId: true,
+ ignoreTransform: true,
+ ignoreRect: true,
+ ));
+
+ semantics.dispose();
+ });
+
+ testWidgets('Traversal Order of in a SingleChildScrollView', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ final List<Widget> listChildren = new List<Widget>.generate(30, (int i) {
+ return new Container(
+ height: 200.0,
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ new Semantics(
+ container: true,
+ child: new Text('Item ${i}a'),
+ ),
+ new Semantics(
+ container: true,
+ child: new Text('item ${i}b'),
+ ),
+ ],
+ ),
+ );
+ });
+ await tester.pumpWidget(
+ new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new SingleChildScrollView(
+ controller: new ScrollController(initialScrollOffset: 3000.0),
+ child: new Column(
+ children: listChildren,
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 13a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 13b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 14a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 14b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 15a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 15b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 16a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 16b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 17a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'item 17b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 18a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 18b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 19a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'item 19b',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ childOrder: DebugSemanticsDumpOrder.traversalOrder,
+ ignoreId: true,
+ ignoreTransform: true,
+ ignoreRect: true,
+ ));
+
+ semantics.dispose();
+ });
+
+ testWidgets('Traversal Order with center child', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+
+ await tester.pumpWidget(new Semantics(
+ textDirection: TextDirection.ltr,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new Scrollable(
+ viewportBuilder: (BuildContext context, ViewportOffset offset) {
+ return new Viewport(
+ offset: offset,
+ center: const ValueKey<int>(0),
+ slivers: new List<Widget>.generate(30, (int i) {
+ final int item = i - 15;
+ return new SliverToBoxAdapter(
+ key: new ValueKey<int>(item),
+ child: new Container(
+ height: 200.0,
+ child: new Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ new Semantics(
+ container: true,
+ child: new Text('${item}a'),
+ ),
+ new Semantics(
+ container: true,
+ child: new Text('${item}b'),
+ ),
+ ],
+ ),
+ ),
+ );
+ }),
+ );
+ },
+ ),
+ ),
+ ),
+ ));
+
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '-2a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '-2b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '-1a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '-1b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '0a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '0b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '1a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '1b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '2a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: '2b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '3a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '3b',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '4a',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: '4b',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ignoreRect: true,
+ ignoreTransform: true,
+ ignoreId: true,
+ ));
+
+ semantics.dispose();
+
+ });
+}
diff --git a/packages/flutter/test/widgets/single_child_scroll_view_test.dart b/packages/flutter/test/widgets/single_child_scroll_view_test.dart
index f3d937b..126d3de 100644
--- a/packages/flutter/test/widgets/single_child_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/single_child_scroll_view_test.dart
@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:ui';
+
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
+import 'semantics_tester.dart';
+
class TestScrollPosition extends ScrollPositionWithSingleContext {
TestScrollPosition({
ScrollPhysics physics,
@@ -170,4 +174,171 @@
);
expect(innerScrollable.controller, isNull);
});
+
+ testWidgets('SingleChildScrollView semantics', (WidgetTester tester) async {
+ final SemanticsTester semantics = new SemanticsTester(tester);
+ final ScrollController controller = new ScrollController();
+
+ await tester.pumpWidget(
+ new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new SingleChildScrollView(
+ controller: controller,
+ child: new Column(
+ children: new List<Widget>.generate(30, (int i) {
+ return new Container(
+ height: 200.0,
+ child: new Text('Tile $i'),
+ );
+ }),
+ ),
+ ),
+ ),
+ );
+
+ expect(semantics, hasSemantics(
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ label: r'Tile 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,],
+ label: r'Tile 4',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ignoreRect: true, ignoreTransform: true, ignoreId: true,
+ ));
+
+ controller.jumpTo(3000.0);
+ await tester.pumpAndSettle();
+
+ expect(semantics, hasSemantics(
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 13',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 14',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 15',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 16',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 17',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 18',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 19',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ignoreRect: true, ignoreTransform: true, ignoreId: true,
+ ));
+
+ controller.jumpTo(6000.0);
+ await tester.pumpAndSettle();
+
+ expect(semantics, hasSemantics(
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 25',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[
+ SemanticsFlag.isHidden,
+ ],
+ label: r'Tile 26',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 27',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 28',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: r'Tile 29',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ignoreRect: true, ignoreTransform: true, ignoreId: true,
+ ));
+
+ semantics.dispose();
+ });
}
diff --git a/packages/flutter/test/widgets/sliver_fill_viewport_test.dart b/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
index ae0850d..3ba4b77 100644
--- a/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
+++ b/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
@@ -70,17 +70,42 @@
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
+ ' │ cacheOrigin: 0.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 12000.0, paintExtent:\n'
- ' │ 600.0, maxPaintExtent: 12000.0, hasVisualOverflow: true)\n'
- ' │ currently live children: 0 to 0\n'
+ ' │ 600.0, maxPaintExtent: 12000.0, hasVisualOverflow: true,\n'
+ ' │ cacheExtent: 850.0)\n'
+ ' │ currently live children: 0 to 1\n'
' │\n'
- ' └─child with index 0: RenderRepaintBoundary#00000\n'
- ' │ parentData: index=0; layoutOffset=0.0\n'
+ ' ├─child with index 0: RenderRepaintBoundary#00000\n'
+ ' │ │ parentData: index=0; layoutOffset=0.0\n'
+ ' │ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' │ │ layer: OffsetLayer#00000\n'
+ ' │ │ size: Size(800.0, 600.0)\n'
+ ' │ │ metrics: 75.0% useful (1 bad vs 3 good)\n'
+ ' │ │ diagnosis: insufficient data to draw conclusion (less than five\n'
+ ' │ │ repaints)\n'
+ ' │ │\n'
+ ' │ └─child: RenderParagraph#00000\n'
+ ' │ │ parentData: <none> (can use size)\n'
+ ' │ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
+ ' │ │ size: Size(800.0, 600.0)\n'
+ ' │ │ textAlign: start\n'
+ ' │ │ textDirection: ltr\n'
+ ' │ │ softWrap: wrapping at box width\n'
+ ' │ │ overflow: clip\n'
+ ' │ │ maxLines: unlimited\n'
+ ' │ ╘═╦══ text ═══\n'
+ ' │ ║ TextSpan:\n'
+ ' │ ║ <all styles inherited>\n'
+ ' │ ║ "0"\n'
+ ' │ ╚═══════════\n'
+ ' └─child with index 1: RenderRepaintBoundary#00000\n'
+ ' │ parentData: index=1; layoutOffset=600.0\n'
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
' │ layer: OffsetLayer#00000\n'
' │ size: Size(800.0, 600.0)\n'
- ' │ metrics: 50.0% useful (1 bad vs 1 good)\n'
+ ' │ metrics: 75.0% useful (1 bad vs 3 good)\n'
' │ diagnosis: insufficient data to draw conclusion (less than five\n'
' │ repaints)\n'
' │\n'
@@ -96,7 +121,7 @@
' ╘═╦══ text ═══\n'
' ║ TextSpan:\n'
' ║ <all styles inherited>\n'
- ' ║ "0"\n'
+ ' ║ "1"\n'
' ╚═══════════\n'
),
);
diff --git a/packages/flutter/test/widgets/sliver_prototype_item_extent_test.dart b/packages/flutter/test/widgets/sliver_prototype_item_extent_test.dart
index 113f80d..8be2d77 100644
--- a/packages/flutter/test/widgets/sliver_prototype_item_extent_test.dart
+++ b/packages/flutter/test/widgets/sliver_prototype_item_extent_test.dart
@@ -131,7 +131,7 @@
);
// Item 0 exists in the list and as the prototype item.
- expect(tester.widgetList(find.text('Item 0')).length, 2);
+ expect(tester.widgetList(find.text('Item 0', skipOffstage: false)).length, 2);
for (int i = 1; i < 10; i += 1)
expect(find.text('Item $i'), findsOneWidget);
diff --git a/packages/flutter/test/widgets/sliver_semantics_test.dart b/packages/flutter/test/widgets/sliver_semantics_test.dart
index 8825058..9ff1110 100644
--- a/packages/flutter/test/widgets/sliver_semantics_test.dart
+++ b/packages/flutter/test/widgets/sliver_semantics_test.dart
@@ -8,7 +8,6 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
-import 'package:vector_math/vector_math_64.dart';
import 'semantics_tester.dart';
@@ -36,22 +35,25 @@
);
});
await tester.pumpWidget(
- new Directionality(
+ new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new CustomScrollView(
- controller: scrollController,
- slivers: <Widget>[
- const SliverAppBar(
- pinned: true,
- expandedHeight: appBarExpandedHeight,
- title: const Text('Semantics Test with Slivers'),
- ),
- new SliverList(
- delegate: new SliverChildListDelegate(listChildren),
- ),
- ],
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: scrollController,
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: appBarExpandedHeight,
+ title: const Text('Semantics Test with Slivers'),
+ ),
+ new SliverList(
+ delegate: new SliverChildListDelegate(listChildren),
+ ),
+ ],
+ ),
),
),
),
@@ -61,29 +63,51 @@
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
+ new TestSemantics(
id: 1,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 6,
- actions: <SemanticsAction>[SemanticsAction.scrollUp],
+ id: 2,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- label: r'Item 0',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 3,
- label: r'Item 1',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 4,
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- label: r'Semantics Test with Slivers',
- textDirection: TextDirection.ltr,
+ id: 9,
+ actions: <SemanticsAction>[SemanticsAction.scrollUp],
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 7,
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 8,
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'Semantics Test with Slivers',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ id: 3,
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 4,
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 5,
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 6,
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
@@ -103,37 +127,63 @@
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
+ new TestSemantics(
id: 1,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 6,
- actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
+ id: 2,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- label: r'Item 0',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 3,
- label: r'Item 1',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
id: 7,
- label: r'Item 2',
- textDirection: TextDirection.ltr,
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 8,
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'Semantics Test with Slivers',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ id: 9,
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 3,
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 4,
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 5,
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 6,
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 10,
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
- new TestSemantics(
- id: 4,
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- label: r'Semantics Test with Slivers',
- textDirection: TextDirection.ltr,
- ),
],
),
],
@@ -150,34 +200,53 @@
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
+ new TestSemantics(
id: 1,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 6,
- actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
+ id: 2,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- label: r'Item 0',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 3,
- label: r'Item 1',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 7,
- label: r'Item 2',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 4,
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- label: r'Semantics Test with Slivers',
- textDirection: TextDirection.ltr,
+ id: 9,
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 7,
+ children: <TestSemantics>[
+ new TestSemantics(
+ id: 8,
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'Semantics Test with Slivers',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ id: 3,
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 4,
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 5,
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ id: 6,
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
@@ -192,7 +261,7 @@
semantics.dispose();
});
- testWidgets('Offscreen sliver are not included in semantics tree', (WidgetTester tester) async {
+ testWidgets('Offscreen sliver are hidden in semantics tree', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
const double containerHeight = 200.0;
@@ -209,14 +278,17 @@
);
});
await tester.pumpWidget(
- new Directionality(
+ new Semantics(
textDirection: TextDirection.ltr,
- child: new Center(
- child: new SizedBox(
- height: containerHeight,
- child: new CustomScrollView(
- controller: scrollController,
- slivers: slivers,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new Center(
+ child: new SizedBox(
+ height: containerHeight,
+ child: new CustomScrollView(
+ controller: scrollController,
+ slivers: slivers,
+ ),
),
),
),
@@ -226,23 +298,36 @@
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 4,
- actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- label: 'Item 2',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 3,
- label: 'Item 1',
- textDirection: TextDirection.ltr,
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
@@ -252,6 +337,7 @@
),
ignoreRect: true,
ignoreTransform: true,
+ ignoreId: true,
));
semantics.dispose();
@@ -269,10 +355,13 @@
);
});
await tester.pumpWidget(
- new Directionality(
+ new Semantics(
textDirection: TextDirection.ltr,
- child: new CustomScrollView(
- slivers: slivers,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new CustomScrollView(
+ slivers: slivers,
+ ),
),
),
);
@@ -280,37 +369,34 @@
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 7,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- label: 'Item 4',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 3,
- label: 'Item 3',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 4,
- label: 'Item 2',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 5,
- label: 'Item 1',
- textDirection: TextDirection.ltr,
- ),
- new TestSemantics(
- id: 6,
- label: 'Item 0',
- textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
@@ -320,6 +406,8 @@
),
ignoreRect: true,
ignoreTransform: true,
+ ignoreId: true,
+ childOrder: DebugSemanticsDumpOrder.inverseHitTest,
));
semantics.dispose();
@@ -335,89 +423,97 @@
);
});
final ScrollController controller = new ScrollController(initialScrollOffset: 280.0);
- await tester.pumpWidget(new Directionality(
+ await tester.pumpWidget(new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new CustomScrollView(
- slivers: <Widget>[
- const SliverAppBar(
- pinned: true,
- expandedHeight: 100.0,
- title: const Text('AppBar'),
- ),
- new SliverList(
- delegate: new SliverChildListDelegate(listChildren),
- ),
- ],
- controller: controller,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: 100.0,
+ title: const Text('AppBar'),
+ ),
+ new SliverList(
+ delegate: new SliverChildListDelegate(listChildren),
+ ),
+ ],
+ controller: controller,
+ ),
),
),
));
- // 'Item 0' is covered by app bar.
- expect(semantics, isNot(includesNodeWith(label: 'Item 0')));
-
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- rect: TestSemantics.fullScreen,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 7,
- actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
- rect: TestSemantics.fullScreen,
- children: <TestSemantics>[
- // Item 0 is missing because its covered by the app bar.
- new TestSemantics(
- id: 2,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- // Item 1 starts 20.0dp below edge, so there would be room for Item 0.
- transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)),
- label: 'Item 1',
- ),
- new TestSemantics(
- id: 3,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)),
- label: 'Item 2',
- ),
- new TestSemantics(
- id: 4,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)),
- label: 'Item 3',
- ),
- ],
- ),
- new TestSemantics(
- id: 5,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
children: <TestSemantics>[
new TestSemantics(
- id: 6,
- label: 'AppBar',
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- textDirection: TextDirection.ltr,
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'AppBar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
],
- )
+ ),
],
),
ignoreTransform: true,
+ ignoreId: true,
+ ignoreRect: true,
));
semantics.dispose();
});
- testWidgets('Slivers fully covered by another overlapping sliver are excluded', (WidgetTester tester) async {
+ testWidgets('Slivers fully covered by another overlapping sliver are hidden', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final ScrollController controller = new ScrollController(initialScrollOffset: 280.0);
@@ -429,79 +525,88 @@
),
);
});
- await tester.pumpWidget(new Directionality(
+ await tester.pumpWidget(new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new CustomScrollView(
- controller: controller,
- slivers: <Widget>[
- const SliverAppBar(
- pinned: true,
- expandedHeight: 100.0,
- title: const Text('AppBar'),
- ),
- ]..addAll(slivers),
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ controller: controller,
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: 100.0,
+ title: const Text('AppBar'),
+ ),
+ ]..addAll(slivers),
+ ),
),
),
));
- // 'Item 0' is covered by app bar.
- expect(semantics, isNot(includesNodeWith(label: 'Item 0')));
-
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- rect: TestSemantics.fullScreen,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 7,
- actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
- rect: TestSemantics.fullScreen,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)),
- label: 'Item 3',
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'AppBar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
new TestSemantics(
- id: 3,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)),
- label: 'Item 2',
- ),
- new TestSemantics(
- id: 4,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- // Item 1 starts 20.0dp below edge, so there would be room for Item 0.
- transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)),
- label: 'Item 1',
- ),
- // Item 0 is missing because its covered by the app bar.
- ],
- ),
- new TestSemantics(
- id: 5,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
- children: <TestSemantics>[
- new TestSemantics(
- id: 6,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- label: 'AppBar',
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
],
- )
+ ),
],
),
ignoreTransform: true,
+ ignoreRect: true,
+ ignoreId: true,
));
semantics.dispose();
@@ -517,88 +622,98 @@
);
});
final ScrollController controller = new ScrollController(initialScrollOffset: 280.0);
- await tester.pumpWidget(new Directionality(
+ await tester.pumpWidget(new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new CustomScrollView(
- reverse: true, // This is the important setting for this test.
- slivers: <Widget>[
- const SliverAppBar(
- pinned: true,
- expandedHeight: 100.0,
- title: const Text('AppBar'),
- ),
- new SliverList(
- delegate: new SliverChildListDelegate(listChildren),
- ),
- ],
- controller: controller,
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ reverse: true, // This is the important setting for this test.
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: 100.0,
+ title: const Text('AppBar'),
+ ),
+ new SliverList(
+ delegate: new SliverChildListDelegate(listChildren),
+ ),
+ ],
+ controller: controller,
+ ),
),
),
));
- // 'Item 0' is covered by app bar.
- expect(semantics, isNot(includesNodeWith(label: 'Item 0')));
-
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- rect: TestSemantics.fullScreen,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 7,
- actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
- rect: TestSemantics.fullScreen,
- children: <TestSemantics>[
- // Item 0 is missing because its covered by the app bar.
- new TestSemantics(
- id: 2,
- // Item 1 ends at 580dp, so there would be 20dp space for Item 0.
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- label: 'Item 1',
- ),
- new TestSemantics(
- id: 3,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- label: 'Item 2',
- ),
- new TestSemantics(
- id: 4,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- label: 'Item 3',
- ),
- ],
- ),
- new TestSemantics(
- id: 5,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
children: <TestSemantics>[
new TestSemantics(
- id: 6,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- label: 'AppBar',
- textDirection: TextDirection.ltr,
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'AppBar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
],
- )
+ ),
],
),
ignoreTransform: true,
+ ignoreId: true,
+ ignoreRect: true,
));
semantics.dispose();
});
- testWidgets('Slivers fully covered by another overlapping sliver are excluded (reverse)', (WidgetTester tester) async {
+ testWidgets('Slivers fully covered by another overlapping sliver are hidden (reverse)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final ScrollController controller = new ScrollController(initialScrollOffset: 280.0);
@@ -610,89 +725,93 @@
),
);
});
- await tester.pumpWidget(new Directionality(
+ await tester.pumpWidget(new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new CustomScrollView(
- reverse: true, // This is the important setting for this test.
- controller: controller,
- slivers: <Widget>[
- const SliverAppBar(
- pinned: true,
- expandedHeight: 100.0,
- title: const Text('AppBar'),
- ),
- ]..addAll(slivers),
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new CustomScrollView(
+ reverse: true, // This is the important setting for this test.
+ controller: controller,
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: 100.0,
+ title: const Text('AppBar'),
+ ),
+ ]..addAll(slivers),
+ ),
),
),
));
- // 'Item 0' is covered by app bar.
- expect(semantics, isNot(includesNodeWith(label: 'Item 0')));
-
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
- new TestSemantics.rootChild(
- id: 1,
- rect: TestSemantics.fullScreen,
- tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
children: <TestSemantics>[
new TestSemantics(
- id: 7,
- actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
- rect: TestSemantics.fullScreen,
children: <TestSemantics>[
new TestSemantics(
- id: 2,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, -20.0, 0.0)),
- label: 'Item 3',
+ actions: <SemanticsAction>[SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
new TestSemantics(
- id: 3,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- transform: new Matrix4.translation(new Vector3(0.0, 180.0, 0.0)),
- label: 'Item 2',
- ),
- new TestSemantics(
- id: 4,
- rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
- // Item 1 ends at 580dp, so there would be 20dp space for Item 0.
- transform: new Matrix4.translation(new Vector3(0.0, 380.0, 0.0)),
- label: 'Item 1',
- ),
- // Item 0 is missing because its covered by the app bar.
- ],
- ),
- new TestSemantics(
- id: 5,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
- flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
- tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
- children: <TestSemantics>[
- new TestSemantics(
- id: 6,
- rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
- transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
- label: 'AppBar',
- textDirection: TextDirection.ltr,
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'AppBar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
),
],
),
],
- )
+ ),
],
),
ignoreTransform: true,
+ ignoreId: true,
+ ignoreRect: true,
));
semantics.dispose();
});
- testWidgets('Slivers fully covered by another overlapping sliver are excluded (with center sliver)', (WidgetTester tester) async {
+ testWidgets('Slivers fully covered by another overlapping sliver are hidden (with center sliver)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final ScrollController controller = new ScrollController(initialScrollOffset: 280.0);
@@ -709,53 +828,177 @@
child: new Text('Backward Item $i', textDirection: TextDirection.ltr),
);
});
- await tester.pumpWidget(new Directionality(
+ await tester.pumpWidget(new Semantics(
textDirection: TextDirection.ltr,
- child: new MediaQuery(
- data: const MediaQueryData(),
- child: new Scrollable(
- controller: controller,
- viewportBuilder: (BuildContext context, ViewportOffset offset) {
- return new Viewport(
- offset: offset,
- center: forwardAppBarKey,
- slivers: <Widget>[
- new SliverList(
- delegate: new SliverChildListDelegate(backwardChildren),
- ),
- const SliverAppBar(
- pinned: true,
- expandedHeight: 100.0,
- flexibleSpace: const FlexibleSpaceBar(
- title: const Text('Backward app bar', textDirection: TextDirection.ltr),
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new MediaQuery(
+ data: const MediaQueryData(),
+ child: new Scrollable(
+ controller: controller,
+ viewportBuilder: (BuildContext context, ViewportOffset offset) {
+ return new Viewport(
+ offset: offset,
+ center: forwardAppBarKey,
+ slivers: <Widget>[
+ new SliverList(
+ delegate: new SliverChildListDelegate(backwardChildren),
),
- ),
- new SliverAppBar(
- pinned: true,
- key: forwardAppBarKey,
- expandedHeight: 100.0,
- flexibleSpace: const FlexibleSpaceBar(
- title: const Text('Forward app bar', textDirection: TextDirection.ltr),
+ const SliverAppBar(
+ pinned: true,
+ expandedHeight: 100.0,
+ flexibleSpace: const FlexibleSpaceBar(
+ title: const Text('Backward app bar', textDirection: TextDirection.ltr),
+ ),
),
- ),
- new SliverList(
- delegate: new SliverChildListDelegate(forwardChildren),
- ),
- ],
- );
- },
+ new SliverAppBar(
+ pinned: true,
+ key: forwardAppBarKey,
+ expandedHeight: 100.0,
+ flexibleSpace: const FlexibleSpaceBar(
+ title: const Text('Forward app bar', textDirection: TextDirection.ltr),
+ ),
+ ),
+ new SliverList(
+ delegate: new SliverChildListDelegate(forwardChildren),
+ ),
+ ],
+ );
+ },
+ ),
),
),
));
// 'Forward Item 0' is covered by app bar.
- expect(semantics, isNot(includesNodeWith(label: 'Forward Item 0')));
- expect(semantics, includesNodeWith(label: 'Forward Item 1'));
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'Forward app bar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Forward Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Forward Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Forward Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Forward Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Forward Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Forward Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ignoreTransform: true,
+ ignoreRect: true,
+ ignoreId: true,
+ ));
controller.jumpTo(-880.0);
await tester.pumpAndSettle();
- expect(semantics, isNot(includesNodeWith(label: 'Backward Item 0')));
- expect(semantics, includesNodeWith(label: 'Backward Item 1'));
+
+ // 'Backward Item 0' is covered by app bar.
+ expect(semantics, hasSemantics(
+ new TestSemantics.root(
+ children: <TestSemantics>[
+ new TestSemantics(
+ textDirection: TextDirection.ltr,
+ children: <TestSemantics>[
+ new TestSemantics(
+ children: <TestSemantics>[
+ new TestSemantics(
+ actions: <SemanticsAction>[
+ SemanticsAction.scrollUp,
+ SemanticsAction.scrollDown,
+ ],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Backward Item 5',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Backward Item 4',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Backward Item 3',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Backward Item 2',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ label: 'Backward Item 1',
+ textDirection: TextDirection.ltr,
+ ),
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.isHidden],
+ label: 'Backward Item 0',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ new TestSemantics(
+ tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
+ children: <TestSemantics>[
+ new TestSemantics(
+ flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
+ label: 'Backward app bar',
+ textDirection: TextDirection.ltr,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ), ignoreTransform: true, ignoreRect: true, ignoreId: true,
+ ));
semantics.dispose();
});
diff --git a/packages/flutter/test/widgets/slivers_appbar_floating_test.dart b/packages/flutter/test/widgets/slivers_appbar_floating_test.dart
index a35fb8b..651d273 100644
--- a/packages/flutter/test/widgets/slivers_appbar_floating_test.dart
+++ b/packages/flutter/test/widgets/slivers_appbar_floating_test.dart
@@ -70,8 +70,8 @@
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
- verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
- verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key2, const Offset(0.0, 1000.0), false);
+ verifyPaintPosition(key3, const Offset(0.0, 1200.0), false);
position.animateTo(bigHeight - 600.0 + delegate.maxExtent, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 1000));
@@ -142,8 +142,8 @@
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
- verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
- verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key2, const Offset(0.0, 1000.0), false);
+ verifyPaintPosition(key3, const Offset(0.0, 1200.0), false);
position.animateTo(bigHeight + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 1000));
@@ -177,8 +177,8 @@
final ScrollPositionWithSingleContext position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
- verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
- verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key2, const Offset(0.0, 1000.0), false);
+ verifyPaintPosition(key3, const Offset(0.0, 1200.0), false);
position.animateTo(bigHeight + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 1000));
diff --git a/packages/flutter/test/widgets/slivers_appbar_pinned_test.dart b/packages/flutter/test/widgets/slivers_appbar_pinned_test.dart
index 1eba52c..24ff76f 100644
--- a/packages/flutter/test/widgets/slivers_appbar_pinned_test.dart
+++ b/packages/flutter/test/widgets/slivers_appbar_pinned_test.dart
@@ -90,9 +90,11 @@
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
' │ crossAxisDirection: AxisDirection.right,\n'
- ' │ viewportMainAxisExtent: 600.0)\n'
+ ' │ viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
+ ' │ cacheOrigin: 0.0 )\n'
' │ geometry: SliverGeometry(scrollExtent: 200.0, paintExtent: 200.0,\n'
- ' │ maxPaintExtent: 200.0, hasVisualOverflow: true)\n'
+ ' │ maxPaintExtent: 200.0, hasVisualOverflow: true, cacheExtent:\n'
+ ' │ 200.0)\n'
' │ maxExtent: EXCEPTION (FlutterError)\n'
' │ child position: 0.0\n'
' │\n'
@@ -140,23 +142,23 @@
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
verifyPaintPosition(key2, const Offset(0.0, 550.0), true);
- verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
- verifyPaintPosition(key4, const Offset(0.0, 600.0), false);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key3, const Offset(0.0, 750.0), false);
+ verifyPaintPosition(key4, const Offset(0.0, 950.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 1500.0), false);
position.animateTo(550.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 100));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
verifyPaintPosition(key3, const Offset(0.0, 200.0), true);
verifyPaintPosition(key4, const Offset(0.0, 400.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 950.0), false);
position.animateTo(600.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 200));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
verifyPaintPosition(key3, const Offset(0.0, 150.0), true);
verifyPaintPosition(key4, const Offset(0.0, 350.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 900.0), false);
position.animateTo(650.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 300));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
@@ -164,7 +166,7 @@
verifyPaintPosition(key3, const Offset(0.0, 100.0), true);
verifyActualBoxPosition(tester, find.byType(Container), 1, new Rect.fromLTWH(0.0, 100.0, 800.0, 200.0));
verifyPaintPosition(key4, const Offset(0.0, 300.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 850.0), false);
position.animateTo(700.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 400));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
@@ -172,7 +174,7 @@
verifyPaintPosition(key3, const Offset(0.0, 100.0), true);
verifyActualBoxPosition(tester, find.byType(Container), 1, new Rect.fromLTWH(0.0, 100.0, 800.0, 200.0));
verifyPaintPosition(key4, const Offset(0.0, 250.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 800.0), false);
position.animateTo(750.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 500));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
@@ -180,21 +182,21 @@
verifyPaintPosition(key3, const Offset(0.0, 100.0), true);
verifyActualBoxPosition(tester, find.byType(Container), 1, new Rect.fromLTWH(0.0, 100.0, 800.0, 200.0));
verifyPaintPosition(key4, const Offset(0.0, 200.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 750.0), false);
position.animateTo(800.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 60));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
verifyPaintPosition(key3, const Offset(0.0, 100.0), true);
verifyPaintPosition(key4, const Offset(0.0, 150.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 700.0), false);
position.animateTo(850.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 70));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
verifyPaintPosition(key3, const Offset(0.0, 100.0), true);
verifyPaintPosition(key4, const Offset(0.0, 100.0), true);
- verifyPaintPosition(key5, const Offset(0.0, 600.0), false);
+ verifyPaintPosition(key5, const Offset(0.0, 650.0), false);
position.animateTo(900.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pumpAndSettle(const Duration(milliseconds: 80));
verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
diff --git a/packages/flutter/test/widgets/slivers_block_global_key_test.dart b/packages/flutter/test/widgets/slivers_block_global_key_test.dart
index 696d984..afcbb99 100644
--- a/packages/flutter/test/widgets/slivers_block_global_key_test.dart
+++ b/packages/flutter/test/widgets/slivers_block_global_key_test.dart
@@ -22,12 +22,15 @@
Widget build(BuildContext context) => new Text('${widget.value}:$generation ', textDirection: TextDirection.ltr);
}
+// Creates a SliverList with `keys.length` children and each child having a key from `keys` and a text of `key:generation`.
+// The generation is increased with every call to this method.
Future<Null> test(WidgetTester tester, double offset, List<int> keys) {
globalGeneration += 1;
return tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Viewport(
+ cacheExtent: 0.0,
offset: new ViewportOffset.fixed(offset),
slivers: <Widget>[
new SliverList(
@@ -41,6 +44,8 @@
);
}
+// `answerKey`: Expected offsets of visible SliverList children in global coordinate system.
+// `text`: A space-separated list of expected `key:generation` pairs for the visible SliverList children.
void verify(WidgetTester tester, List<Offset> answerKey, String text) {
final List<Offset> testAnswers = tester.renderObjectList<RenderBox>(find.byType(SizedBox)).map<Offset>(
(RenderBox target) => target.localToGlobal(const Offset(0.0, 0.0))
@@ -48,8 +53,8 @@
expect(testAnswers, equals(answerKey));
final String foundText =
tester.widgetList<Text>(find.byType(Text))
- .map<String>((Text widget) => widget.data)
- .reduce((String value, String element) => value + element);
+ .map<String>((Text widget) => widget.data)
+ .reduce((String value, String element) => value + element);
expect(foundText, equals(text));
}
diff --git a/packages/flutter/test/widgets/slivers_evil_test.dart b/packages/flutter/test/widgets/slivers_evil_test.dart
index 214031e..3443e0e 100644
--- a/packages/flutter/test/widgets/slivers_evil_test.dart
+++ b/packages/flutter/test/widgets/slivers_evil_test.dart
@@ -198,6 +198,7 @@
testWidgets('Removing offscreen items above and rescrolling does not crash', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(
home: new CustomScrollView(
+ cacheExtent: 0.0,
slivers: <Widget>[
new SliverFixedExtentList(
itemExtent: 100.0,
@@ -225,6 +226,7 @@
// Stop returning the first 3 items.
await tester.pumpWidget(new MaterialApp(
home: new CustomScrollView(
+ cacheExtent: 0.0,
slivers: <Widget>[
new SliverFixedExtentList(
itemExtent: 100.0,
diff --git a/packages/flutter/test/widgets/slivers_padding_test.dart b/packages/flutter/test/widgets/slivers_padding_test.dart
index e566999..d016e10 100644
--- a/packages/flutter/test/widgets/slivers_padding_test.dart
+++ b/packages/flutter/test/widgets/slivers_padding_test.dart
@@ -27,7 +27,7 @@
}
void verify(WidgetTester tester, List<Rect> answerKey) {
- final List<Rect> testAnswers = tester.renderObjectList<RenderBox>(find.byType(SizedBox)).map<Rect>(
+ final List<Rect> testAnswers = tester.renderObjectList<RenderBox>(find.byType(SizedBox, skipOffstage: false)).map<Rect>(
(RenderBox target) {
final Offset topLeft = target.localToGlobal(Offset.zero);
final Offset bottomRight = target.localToGlobal(target.size.bottomRight(Offset.zero));
@@ -45,14 +45,14 @@
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 420.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
]);
await test(tester, 200.0, padding, AxisDirection.down, TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 220.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
]);
await test(tester, 390.0, padding, AxisDirection.down, TextDirection.ltr);
@@ -84,14 +84,14 @@
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 420.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
]);
await test(tester, 200.0, padding, AxisDirection.down, TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 220.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
]);
await test(tester, 390.0, padding, AxisDirection.down, TextDirection.ltr);
@@ -123,14 +123,14 @@
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, 420.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
]);
await test(tester, 200.0, padding, AxisDirection.down, TextDirection.rtl);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, 220.0, 760.0, 400.0),
- new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
+ new Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
]);
await test(tester, 390.0, padding, AxisDirection.down, TextDirection.rtl);
@@ -363,6 +363,7 @@
return new Directionality(
textDirection: TextDirection.ltr,
child: new CustomScrollView(
+ cacheExtent: 0.0,
slivers: <Widget>[
new SliverPadding(
padding: EdgeInsets.zero,
diff --git a/packages/flutter/test/widgets/slivers_test.dart b/packages/flutter/test/widgets/slivers_test.dart
index c7ce26b..7fafa8c 100644
--- a/packages/flutter/test/widgets/slivers_test.dart
+++ b/packages/flutter/test/widgets/slivers_test.dart
@@ -26,10 +26,10 @@
}
void verify(WidgetTester tester, List<Offset> idealPositions, List<bool> idealVisibles) {
- final List<Offset> actualPositions = tester.renderObjectList<RenderBox>(find.byType(SizedBox)).map<Offset>(
+ final List<Offset> actualPositions = tester.renderObjectList<RenderBox>(find.byType(SizedBox, skipOffstage: false)).map<Offset>(
(RenderBox target) => target.localToGlobal(const Offset(0.0, 0.0))
).toList();
- final List<bool> actualVisibles = tester.renderObjectList<RenderSliverToBoxAdapter>(find.byType(SliverToBoxAdapter)).map<bool>(
+ final List<bool> actualVisibles = tester.renderObjectList<RenderSliverToBoxAdapter>(find.byType(SliverToBoxAdapter, skipOffstage: false)).map<bool>(
(RenderSliverToBoxAdapter target) => target.geometry.visible
).toList();
expect(actualPositions, equals(idealPositions));
@@ -43,9 +43,9 @@
verify(tester, <Offset>[
const Offset(0.0, 0.0),
const Offset(0.0, 400.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 800.0),
+ const Offset(0.0, 1200.0),
+ const Offset(0.0, 1600.0),
], <bool>[true, true, false, false, false]);
await test(tester, 200.0);
@@ -53,8 +53,8 @@
const Offset(0.0, -200.0),
const Offset(0.0, 200.0),
const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 1000.0),
+ const Offset(0.0, 1400.0),
], <bool>[true, true, false, false, false]);
await test(tester, 600.0);
@@ -63,7 +63,7 @@
const Offset(0.0, -200.0),
const Offset(0.0, 200.0),
const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 1000.0),
], <bool>[false, true, true, false, false]);
await test(tester, 900.0);
@@ -72,7 +72,7 @@
const Offset(0.0, -500.0),
const Offset(0.0, -100.0),
const Offset(0.0, 300.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 700.0),
], <bool>[false, false, true, true, false]);
});
@@ -82,18 +82,18 @@
verify(tester, <Offset>[
const Offset(0.0, 100.0),
const Offset(0.0, 500.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 900.0),
+ const Offset(0.0, 1300.0),
+ const Offset(0.0, 1700.0),
], <bool>[true, true, false, false, false]);
await test(tester, 200.0, anchor: 100.0);
verify(tester, <Offset>[
const Offset(0.0, -100.0),
const Offset(0.0, 300.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 700.0),
+ const Offset(0.0, 1100.0),
+ const Offset(0.0, 1500.0),
], <bool>[true, true, false, false, false]);
await test(tester, 600.0, anchor: 100.0);
@@ -101,8 +101,8 @@
const Offset(0.0, -500.0),
const Offset(0.0, -100.0),
const Offset(0.0, 300.0),
- const Offset(0.0, 600.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 700.0),
+ const Offset(0.0, 1100.0),
], <bool>[false, true, true, false, false]);
await test(tester, 900.0, anchor: 100.0);
@@ -111,7 +111,7 @@
const Offset(0.0, -400.0),
const Offset(0.0, 0.0),
const Offset(0.0, 400.0),
- const Offset(0.0, 600.0),
+ const Offset(0.0, 800.0),
], <bool>[false, false, true, true, false]);
});
diff --git a/packages/flutter_localizations/test/basics_test.dart b/packages/flutter_localizations/test/basics_test.dart
index 18c41a0..20a4518 100644
--- a/packages/flutter_localizations/test/basics_test.dart
+++ b/packages/flutter_localizations/test/basics_test.dart
@@ -21,9 +21,9 @@
),
));
- final LocalizationTrackerState outerTracker = tester.state(find.byKey(const ValueKey<String>('outer')));
+ final LocalizationTrackerState outerTracker = tester.state(find.byKey(const ValueKey<String>('outer'), skipOffstage: false));
expect(outerTracker.captionFontSize, 12.0);
- final LocalizationTrackerState innerTracker = tester.state(find.byKey(const ValueKey<String>('inner')));
+ final LocalizationTrackerState innerTracker = tester.state(find.byKey(const ValueKey<String>('inner'), skipOffstage: false));
expect(innerTracker.captionFontSize, 13.0);
});
diff --git a/packages/flutter_test/lib/src/finders.dart b/packages/flutter_test/lib/src/finders.dart
index 615c905..ed65a00 100644
--- a/packages/flutter_test/lib/src/finders.dart
+++ b/packages/flutter_test/lib/src/finders.dart
@@ -55,8 +55,8 @@
/// nodes that are [Offstage] or that are from inactive [Route]s.
Finder widgetWithText(Type widgetType, String text, { bool skipOffstage: true }) {
return find.ancestor(
- of: find.text(text),
- matching: find.byType(widgetType),
+ of: find.text(text, skipOffstage: skipOffstage),
+ matching: find.byType(widgetType, skipOffstage: skipOffstage),
);
}