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
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 26c5dae..fd51294 100644
--- a/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
+++ b/packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
@@ -120,13 +120,14 @@
);
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 @@
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 @@
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 @@
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());
diff --git a/packages/flutter/lib/src/rendering/sliver_grid.dart b/packages/flutter/lib/src/rendering/sliver_grid.dart
index 484bbf0..dec6010 100644
--- a/packages/flutter/lib/src/rendering/sliver_grid.dart
+++ b/packages/flutter/lib/src/rendering/sliver_grid.dart
@@ -172,13 +172,16 @@
@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 @@
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 @@
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 @@
assert(childScrollOffset(firstChild) <= scrollOffset);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild) == firstIndex);
- assert(lastIndex <= targetLastIndex);
+ assert(targetLastIndex == null || lastIndex <= targetLastIndex);
final double estimatedTotalExtent = childManager.estimateMaxScrollOffset(
constraints,
diff --git a/packages/flutter/test/widgets/grid_view_test.dart b/packages/flutter/test/widgets/grid_view_test.dart
index e81dfae..10ccabf 100644
--- a/packages/flutter/test/widgets/grid_view_test.dart
+++ b/packages/flutter/test/widgets/grid_view_test.dart
@@ -328,6 +328,47 @@
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)
}
diff --git a/packages/flutter/test/widgets/list_view_test.dart b/packages/flutter/test/widgets/list_view_test.dart
index e4a6b59..74b498d 100644
--- a/packages/flutter/test/widgets/list_view_test.dart
+++ b/packages/flutter/test/widgets/list_view_test.dart
@@ -144,4 +144,23 @@
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);
+ });
}