Fix SliverMainAxisGroup geometry cacheExtent (#142482)

Fixes https://github.com/flutter/flutter/issues/142183

This fixes a bug in the SliverGeometry of SliverMainAxisGroup. The cacheExtent represents how many pixels the sliver has consumed in the SliverConstraints.remainingCacheExtent. Since it was not set, slivers that came after a SliverMainAxisGroup that filled the whole screen did not properly lay out their own children, in some cases making lazy sliver more eager than they should be.
This commit is contained in:
Kate Lovett 2024-01-30 10:04:13 -06:00 committed by GitHub
parent 5bb4fa35f3
commit e8cb029583
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 1 deletions

View file

@ -284,9 +284,21 @@ class RenderSliverMainAxisGroup extends RenderSliver with ContainerRenderObjectM
offset += child.geometry!.scrollExtent;
child = childAfter(child);
}
final double paintExtent = calculatePaintOffset(
constraints,
from: math.min(constraints.scrollOffset, 0),
to: totalScrollExtent,
);
final double cacheExtent = calculateCacheOffset(
constraints,
from: math.min(constraints.scrollOffset, 0),
to: totalScrollExtent,
);
geometry = SliverGeometry(
scrollExtent: totalScrollExtent,
paintExtent: calculatePaintOffset(constraints, from: 0, to: totalScrollExtent),
paintExtent: paintExtent,
cacheExtent: cacheExtent,
maxPaintExtent: maxPaintExtent,
hasVisualOverflow: totalScrollExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
);

View file

@ -692,6 +692,42 @@ void main() {
expect(controller.offset, 1000);
expect(counter, equals(2));
});
testWidgets('SliverMainAxisGroup does not cause extra builds for lazy sliver children', (WidgetTester tester) async {
// By setting the correct SliverGeometry in the first SliverMainAxisGroup,
// the following SliverMainAxisGroups will not perform extra work.
final Map<int, int> buildsPerGroup = <int, int>{
0 : 0,
1 : 0,
2 : 0,
};
await tester.pumpWidget(MaterialApp(
home: CustomScrollView(
slivers: <Widget>[
for (int groupIndex = 0; groupIndex < 3; groupIndex++)
SliverMainAxisGroup(
slivers: <Widget>[
SliverList.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
buildsPerGroup[groupIndex] = buildsPerGroup[groupIndex]! + 1;
return const SizedBox.square(dimension: 50);
},
),
],
),
]
),
));
await tester.pumpAndSettle();
expect(buildsPerGroup[0], 17); // First sliver filled the screen and cache extent
expect(buildsPerGroup[1], 1); // Second only lays out one child
expect(buildsPerGroup[2], 1); // Third only lays out one child
final RenderSliverMainAxisGroup renderGroup = tester.renderObject(
find.byType(SliverMainAxisGroup).first,
) as RenderSliverMainAxisGroup;
expect(renderGroup.geometry!.cacheExtent, 850.0);
});
}
Widget _buildSliverList({