Commit 9c876336 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Fixed extent lists cannot handle infinite extent (#8885)

Previously they tried to compute an integer target end index, but
integers can't represent infinity. Now we use null to represent
infinity.

Also, fix some similar issues with grids.

Fixes #8398
parent e836a84e
......@@ -120,13 +120,14 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
);
final int firstIndex = getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
final int targetLastIndex = getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent);
final int targetLastIndex = targetEndScrollOffset.isFinite ?
getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent) : null;
if (firstChild != null) {
final int oldFirstIndex = indexOf(firstChild);
final int oldLastIndex = indexOf(lastChild);
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
final int trailingGarbage = (oldLastIndex - targetLastIndex).clamp(0, childCount);
final int trailingGarbage = targetLastIndex == null ? 0 : (oldLastIndex - targetLastIndex).clamp(0, childCount);
if (leadingGarbage + trailingGarbage > 0)
collectGarbage(leadingGarbage, trailingGarbage);
}
......@@ -156,7 +157,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
trailingChildWithLayout = firstChild;
}
while (indexOf(trailingChildWithLayout) < targetLastIndex) {
while (targetLastIndex == null || indexOf(trailingChildWithLayout) < targetLastIndex) {
RenderBox child = childAfter(trailingChildWithLayout);
if (child == null) {
child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
......@@ -180,7 +181,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
assert(firstIndex == 0 || childScrollOffset(firstChild) <= scrollOffset);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild) == firstIndex);
assert(lastIndex <= targetLastIndex);
assert(targetLastIndex == null || lastIndex <= targetLastIndex);
final double estimatedMaxScrollOffset = estimateMaxScrollOffset(
constraints,
......@@ -201,7 +202,8 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
paintExtent: paintExtent,
maxPaintExtent: estimatedMaxScrollOffset,
// Conservative to avoid flickering away the clip during scroll.
hasVisualOverflow: lastIndex >= targetLastIndex || constraints.scrollOffset > 0.0,
hasVisualOverflow: (targetLastIndex != null && lastIndex >= targetLastIndex)
|| constraints.scrollOffset > 0.0,
);
assert(childManager.debugAssertChildListLocked());
......
......@@ -172,13 +172,16 @@ class SliverGridRegularTileLayout extends SliverGridLayout {
@override
int getMinChildIndexForScrollOffset(double scrollOffset) {
return crossAxisCount * (scrollOffset ~/ mainAxisStride);
return mainAxisStride > 0.0 ? crossAxisCount * (scrollOffset ~/ mainAxisStride) : 0;
}
@override
int getMaxChildIndexForScrollOffset(double scrollOffset) {
final int mainAxisCount = (scrollOffset / mainAxisStride).ceil();
return math.max(0, crossAxisCount * mainAxisCount - 1);
if (mainAxisStride > 0.0) {
final int mainAxisCount = (scrollOffset / mainAxisStride).ceil();
return math.max(0, crossAxisCount * mainAxisCount - 1);
}
return 0;
}
@override
......@@ -486,13 +489,14 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
final SliverGridLayout layout = _gridDelegate.getLayout(constraints);
final int firstIndex = layout.getMinChildIndexForScrollOffset(scrollOffset);
final int targetLastIndex = layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset);
final int targetLastIndex = targetEndScrollOffset.isFinite ?
layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset) : null;
if (firstChild != null) {
final int oldFirstIndex = indexOf(firstChild);
final int oldLastIndex = indexOf(lastChild);
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
final int trailingGarbage = (oldLastIndex - targetLastIndex).clamp(0, childCount);
final int trailingGarbage = targetLastIndex == null ? 0 : (oldLastIndex - targetLastIndex).clamp(0, childCount);
if (leadingGarbage + trailingGarbage > 0)
collectGarbage(leadingGarbage, trailingGarbage);
}
......@@ -532,7 +536,7 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
trailingChildWithLayout = firstChild;
}
for (int index = indexOf(trailingChildWithLayout) + 1; index <= targetLastIndex; ++index) {
for (int index = indexOf(trailingChildWithLayout) + 1; targetLastIndex == null || index <= targetLastIndex; ++index) {
final SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
final BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints);
RenderBox child = childAfter(trailingChildWithLayout);
......@@ -559,7 +563,7 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
assert(childScrollOffset(firstChild) <= scrollOffset);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild) == firstIndex);
assert(lastIndex <= targetLastIndex);
assert(targetLastIndex == null || lastIndex <= targetLastIndex);
final double estimatedTotalExtent = childManager.estimateMaxScrollOffset(
constraints,
......
......@@ -328,6 +328,47 @@ void main() {
expect(find.byType(GridView), isNot(paints..rect(color: green)..rect(color: green)..rect(color: green)));
});
testWidgets('GridView in zero context', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new SizedBox(
width: 0.0,
height: 0.0,
child: new GridView.count(
crossAxisCount: 4,
children: new List<Widget>.generate(20, (int i) {
return new Container(
child: new Text('$i'),
);
}),
),
),
),
);
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
});
testWidgets('GridView in unbounded context', (WidgetTester tester) async {
await tester.pumpWidget(
new SingleChildScrollView(
child: new GridView.count(
crossAxisCount: 4,
shrinkWrap: true,
children: new List<Widget>.generate(20, (int i) {
return new Container(
child: new Text('$i'),
);
}),
),
),
);
expect(find.text('0'), findsOneWidget);
expect(find.text('19'), findsOneWidget);
});
// TODO(ianh): can you tap a grid cell that is slightly off the bottom of the screen?
// (try it with the flutter_gallery Grid demo)
}
......@@ -144,4 +144,23 @@ void main() {
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
});
testWidgets('ListView with itemExtent in unbounded context', (WidgetTester tester) async {
await tester.pumpWidget(
new SingleChildScrollView(
child: new ListView(
itemExtent: 100.0,
shrinkWrap: true,
children: new List<Widget>.generate(20, (int i) {
return new Container(
child: new Text('$i'),
);
}),
),
),
);
expect(find.text('0'), findsOneWidget);
expect(find.text('19'), findsOneWidget);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment