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
This commit is contained in:
parent
e836a84e30
commit
9c8763368f
@ -120,13 +120,14 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
|
|||||||
);
|
);
|
||||||
|
|
||||||
final int firstIndex = getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
|
final int firstIndex = getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
|
||||||
final int targetLastIndex = getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent);
|
final int targetLastIndex = targetEndScrollOffset.isFinite ?
|
||||||
|
getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent) : null;
|
||||||
|
|
||||||
if (firstChild != null) {
|
if (firstChild != null) {
|
||||||
final int oldFirstIndex = indexOf(firstChild);
|
final int oldFirstIndex = indexOf(firstChild);
|
||||||
final int oldLastIndex = indexOf(lastChild);
|
final int oldLastIndex = indexOf(lastChild);
|
||||||
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
|
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)
|
if (leadingGarbage + trailingGarbage > 0)
|
||||||
collectGarbage(leadingGarbage, trailingGarbage);
|
collectGarbage(leadingGarbage, trailingGarbage);
|
||||||
}
|
}
|
||||||
@ -156,7 +157,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
|
|||||||
trailingChildWithLayout = firstChild;
|
trailingChildWithLayout = firstChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (indexOf(trailingChildWithLayout) < targetLastIndex) {
|
while (targetLastIndex == null || indexOf(trailingChildWithLayout) < targetLastIndex) {
|
||||||
RenderBox child = childAfter(trailingChildWithLayout);
|
RenderBox child = childAfter(trailingChildWithLayout);
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
|
child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
|
||||||
@ -180,7 +181,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
|
|||||||
assert(firstIndex == 0 || childScrollOffset(firstChild) <= scrollOffset);
|
assert(firstIndex == 0 || childScrollOffset(firstChild) <= scrollOffset);
|
||||||
assert(debugAssertChildListIsNonEmptyAndContiguous());
|
assert(debugAssertChildListIsNonEmptyAndContiguous());
|
||||||
assert(indexOf(firstChild) == firstIndex);
|
assert(indexOf(firstChild) == firstIndex);
|
||||||
assert(lastIndex <= targetLastIndex);
|
assert(targetLastIndex == null || lastIndex <= targetLastIndex);
|
||||||
|
|
||||||
final double estimatedMaxScrollOffset = estimateMaxScrollOffset(
|
final double estimatedMaxScrollOffset = estimateMaxScrollOffset(
|
||||||
constraints,
|
constraints,
|
||||||
@ -201,7 +202,8 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
|
|||||||
paintExtent: paintExtent,
|
paintExtent: paintExtent,
|
||||||
maxPaintExtent: estimatedMaxScrollOffset,
|
maxPaintExtent: estimatedMaxScrollOffset,
|
||||||
// Conservative to avoid flickering away the clip during scroll.
|
// 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());
|
assert(childManager.debugAssertChildListLocked());
|
||||||
|
@ -172,14 +172,17 @@ class SliverGridRegularTileLayout extends SliverGridLayout {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
||||||
return crossAxisCount * (scrollOffset ~/ mainAxisStride);
|
return mainAxisStride > 0.0 ? crossAxisCount * (scrollOffset ~/ mainAxisStride) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
||||||
|
if (mainAxisStride > 0.0) {
|
||||||
final int mainAxisCount = (scrollOffset / mainAxisStride).ceil();
|
final int mainAxisCount = (scrollOffset / mainAxisStride).ceil();
|
||||||
return math.max(0, crossAxisCount * mainAxisCount - 1);
|
return math.max(0, crossAxisCount * mainAxisCount - 1);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SliverGridGeometry getGeometryForChildIndex(int index) {
|
SliverGridGeometry getGeometryForChildIndex(int index) {
|
||||||
@ -486,13 +489,14 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
|
|||||||
final SliverGridLayout layout = _gridDelegate.getLayout(constraints);
|
final SliverGridLayout layout = _gridDelegate.getLayout(constraints);
|
||||||
|
|
||||||
final int firstIndex = layout.getMinChildIndexForScrollOffset(scrollOffset);
|
final int firstIndex = layout.getMinChildIndexForScrollOffset(scrollOffset);
|
||||||
final int targetLastIndex = layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset);
|
final int targetLastIndex = targetEndScrollOffset.isFinite ?
|
||||||
|
layout.getMaxChildIndexForScrollOffset(targetEndScrollOffset) : null;
|
||||||
|
|
||||||
if (firstChild != null) {
|
if (firstChild != null) {
|
||||||
final int oldFirstIndex = indexOf(firstChild);
|
final int oldFirstIndex = indexOf(firstChild);
|
||||||
final int oldLastIndex = indexOf(lastChild);
|
final int oldLastIndex = indexOf(lastChild);
|
||||||
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
|
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)
|
if (leadingGarbage + trailingGarbage > 0)
|
||||||
collectGarbage(leadingGarbage, trailingGarbage);
|
collectGarbage(leadingGarbage, trailingGarbage);
|
||||||
}
|
}
|
||||||
@ -532,7 +536,7 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
|
|||||||
trailingChildWithLayout = firstChild;
|
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 SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
|
||||||
final BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints);
|
final BoxConstraints childConstraints = gridGeometry.getBoxConstraints(constraints);
|
||||||
RenderBox child = childAfter(trailingChildWithLayout);
|
RenderBox child = childAfter(trailingChildWithLayout);
|
||||||
@ -559,7 +563,7 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
|
|||||||
assert(childScrollOffset(firstChild) <= scrollOffset);
|
assert(childScrollOffset(firstChild) <= scrollOffset);
|
||||||
assert(debugAssertChildListIsNonEmptyAndContiguous());
|
assert(debugAssertChildListIsNonEmptyAndContiguous());
|
||||||
assert(indexOf(firstChild) == firstIndex);
|
assert(indexOf(firstChild) == firstIndex);
|
||||||
assert(lastIndex <= targetLastIndex);
|
assert(targetLastIndex == null || lastIndex <= targetLastIndex);
|
||||||
|
|
||||||
final double estimatedTotalExtent = childManager.estimateMaxScrollOffset(
|
final double estimatedTotalExtent = childManager.estimateMaxScrollOffset(
|
||||||
constraints,
|
constraints,
|
||||||
|
@ -328,6 +328,47 @@ void main() {
|
|||||||
expect(find.byType(GridView), isNot(paints..rect(color: green)..rect(color: green)..rect(color: green)));
|
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?
|
// 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)
|
// (try it with the flutter_gallery Grid demo)
|
||||||
}
|
}
|
||||||
|
@ -144,4 +144,23 @@ void main() {
|
|||||||
expect(find.text('4'), findsOneWidget);
|
expect(find.text('4'), findsOneWidget);
|
||||||
expect(find.text('5'), findsNothing);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user