mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
Handle ListView item size changes that cause underflow (#9586)
This commit is contained in:
parent
b7ec82014d
commit
3c3b003f8b
|
@ -97,6 +97,7 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
|
|||
earliestScrollOffset = childScrollOffset(earliestUsefulChild)) {
|
||||
// We have to add children before the earliestUsefulChild.
|
||||
earliestUsefulChild = insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true);
|
||||
|
||||
if (earliestUsefulChild == null) {
|
||||
// We ran out of children before reaching the scroll offset.
|
||||
// We must inform our parent that this sliver cannot fulfill
|
||||
|
@ -108,8 +109,33 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
|
|||
childParentData.layoutOffset = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
final double firstChildScrollOffset = earliestScrollOffset - paintExtentOf(firstChild);
|
||||
if (firstChildScrollOffset < 0.0) {
|
||||
// The first child doesn't fit within the viewport (underflow) and
|
||||
// there may be additional children above it. Find the real first child
|
||||
// and then correct the scroll position so that there's room for all and
|
||||
// so that the trailing edge of the original firstChild appears where it
|
||||
// was before the scroll offset correction.
|
||||
// TODO(hansmuller): do this work incrementally, instead of all at once,
|
||||
// i.e. find a way to avoid visiting ALL of the children whose offset
|
||||
// is < 0 before returning for the scroll correction.
|
||||
double correction = 0.0;
|
||||
while (earliestUsefulChild != null) {
|
||||
assert(firstChild == earliestUsefulChild);
|
||||
correction += paintExtentOf(firstChild);
|
||||
earliestUsefulChild = insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true);
|
||||
}
|
||||
geometry = new SliverGeometry(
|
||||
scrollOffsetCorrection: correction - earliestScrollOffset,
|
||||
);
|
||||
final SliverMultiBoxAdaptorParentData childParentData = firstChild.parentData;
|
||||
childParentData.layoutOffset = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
final SliverMultiBoxAdaptorParentData childParentData = earliestUsefulChild.parentData;
|
||||
childParentData.layoutOffset = earliestScrollOffset - paintExtentOf(firstChild);
|
||||
childParentData.layoutOffset = firstChildScrollOffset;
|
||||
assert(earliestUsefulChild == firstChild);
|
||||
leadingChildWithLayout = earliestUsefulChild;
|
||||
trailingChildWithLayout ??= earliestUsefulChild;
|
||||
|
|
|
@ -53,4 +53,82 @@ void main() {
|
|||
expect(tester.getTopLeft(find.text('2')).dy, equals(200.0));
|
||||
});
|
||||
|
||||
testWidgets('ListView can handle inserts at 0', (WidgetTester tester) async {
|
||||
final ScrollController controller = new ScrollController();
|
||||
await tester.pumpWidget(new ListView(
|
||||
controller: controller,
|
||||
children: <Widget>[
|
||||
new Container(height: 400.0, child: const Text('0')),
|
||||
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')),
|
||||
],
|
||||
));
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
expect(find.text('2'), findsNothing);
|
||||
expect(find.text('3'), findsNothing);
|
||||
|
||||
final Finder findItemA = find.descendant(of: find.byType(Container), matching: find.text('A'));
|
||||
final Finder findItemB = find.descendant(of: find.byType(Container), matching: find.text('B'));
|
||||
await tester.pumpWidget(new ListView(
|
||||
controller: controller,
|
||||
children: <Widget>[
|
||||
new Container(height: 10.0, child: const Text('A')),
|
||||
new Container(height: 10.0, child: const Text('B')),
|
||||
new Container(height: 400.0, child: const Text('0')),
|
||||
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')),
|
||||
],
|
||||
));
|
||||
expect(find.text('A'), findsOneWidget);
|
||||
expect(find.text('B'), findsOneWidget);
|
||||
expect(tester.getTopLeft(findItemA).dy, 0.0);
|
||||
expect(tester.getBottomRight(findItemA).dy, 10.0);
|
||||
expect(tester.getTopLeft(findItemB).dy, 10.0);
|
||||
expect(tester.getBottomRight(findItemB).dy, 20.0);
|
||||
|
||||
|
||||
controller.jumpTo(1200.0);
|
||||
await tester.pump();
|
||||
expect(find.text('A'), findsNothing);
|
||||
expect(find.text('B'), findsNothing);
|
||||
|
||||
await tester.pumpWidget(new ListView(
|
||||
controller: controller,
|
||||
children: <Widget>[
|
||||
new Container(height: 200.0, child: const Text('A')),
|
||||
new Container(height: 200.0, child: const Text('B')),
|
||||
new Container(height: 400.0, child: const Text('0')),
|
||||
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')),
|
||||
],
|
||||
));
|
||||
expect(find.text('A'), findsNothing);
|
||||
expect(find.text('B'), findsNothing);
|
||||
|
||||
// Scrolling to 0 causes items A and B to underflow (extend below
|
||||
// scrollOffset 0) because their heights have grown from 10 - 200.
|
||||
// RenderSliver list corrects the scroll offset in this case. Only item
|
||||
// B will become visible and item B's bottom edge will still appear
|
||||
// where it was when its height was 10.0.
|
||||
controller.jumpTo(0.0);
|
||||
await tester.pump();
|
||||
expect(find.text('B'), findsOneWidget);
|
||||
expect(controller.offset, greaterThan(0.0)); // RenderSliverList corrected the offset.
|
||||
expect(tester.getTopLeft(findItemB).dy, -180.0);
|
||||
expect(tester.getBottomRight(findItemB).dy, 20.0);
|
||||
|
||||
|
||||
controller.jumpTo(0.0);
|
||||
await tester.pump();
|
||||
expect(find.text('A'), findsOneWidget);
|
||||
expect(find.text('B'), findsOneWidget);
|
||||
expect(tester.getTopLeft(findItemA).dy, 0.0);
|
||||
expect(tester.getBottomRight(findItemA).dy, 200.0);
|
||||
expect(tester.getTopLeft(findItemB).dy, 200.0);
|
||||
expect(tester.getBottomRight(findItemB).dy, 400.0);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue