diff --git a/packages/flutter/lib/src/rendering/sliver_group.dart b/packages/flutter/lib/src/rendering/sliver_group.dart index 190fd295083..f4c01c49ab3 100644 --- a/packages/flutter/lib/src/rendering/sliver_group.dart +++ b/packages/flutter/lib/src/rendering/sliver_group.dart @@ -296,6 +296,9 @@ class RenderSliverMainAxisGroup extends RenderSliver double paintOffset = constraints.overlap; double maxScrollObstructionExtent = 0; + double cacheOrigin = constraints.cacheOrigin; + double remainingCacheExtent = constraints.remainingCacheExtent; + final ( RenderSliver? leadingChild, RenderSliver? Function(RenderSliver child) advance, @@ -310,17 +313,22 @@ class RenderSliverMainAxisGroup extends RenderSliver from: 0.0, to: scrollOffset, ); + + final double childScrollOffset = math.max(0.0, constraints.scrollOffset - scrollOffset); + final double correctedCacheOrigin = math.max(cacheOrigin, -childScrollOffset); + final double cacheExtentCorrection = cacheOrigin - correctedCacheOrigin; + child.layout( constraints.copyWith( - scrollOffset: math.max(0.0, constraints.scrollOffset - scrollOffset), - cacheOrigin: math.min(0.0, constraints.cacheOrigin + scrollOffset), + scrollOffset: childScrollOffset, + cacheOrigin: correctedCacheOrigin, overlap: math.max(0.0, _fixPrecisionError(paintOffset - beforeOffsetPaintExtent)), remainingPaintExtent: _fixPrecisionError( constraints.remainingPaintExtent - beforeOffsetPaintExtent, ), - remainingCacheExtent: _fixPrecisionError( - constraints.remainingCacheExtent - - calculateCacheOffset(constraints, from: 0.0, to: scrollOffset), + remainingCacheExtent: math.max( + 0.0, + _fixPrecisionError(remainingCacheExtent + cacheExtentCorrection), ), precedingScrollExtent: scrollOffset + constraints.precedingScrollExtent, ), @@ -349,6 +357,12 @@ class RenderSliverMainAxisGroup extends RenderSliver maxPaintExtent += childLayoutGeometry.maxPaintExtent; maxScrollObstructionExtent += childLayoutGeometry.maxScrollObstructionExtent; paintOffset = math.max(childPaintOffset + childLayoutGeometry.paintExtent, paintOffset); + if (childLayoutGeometry.cacheExtent != 0.0) { + remainingCacheExtent = _fixPrecisionError( + remainingCacheExtent - childLayoutGeometry.cacheExtent - cacheExtentCorrection, + ); + cacheOrigin = math.min(correctedCacheOrigin + childLayoutGeometry.cacheExtent, 0.0); + } child = advance(child); assert(() { if (child != null && maxPaintExtent.isInfinite) { diff --git a/packages/flutter/test/widgets/sliver_main_axis_group_test.dart b/packages/flutter/test/widgets/sliver_main_axis_group_test.dart index 2a110a72384..58896a22f70 100644 --- a/packages/flutter/test/widgets/sliver_main_axis_group_test.dart +++ b/packages/flutter/test/widgets/sliver_main_axis_group_test.dart @@ -856,6 +856,38 @@ void main() { expect(renderGroup.geometry!.cacheExtent, 850.0); }); + testWidgets('SliverMainAxisGroup has consistent cacheOrigin', (WidgetTester tester) async { + const Widget item = SizedBox.square(dimension: 50); + + await tester.pumpWidget( + MaterialApp( + home: CustomScrollView( + slivers: [ + SliverMainAxisGroup( + slivers: [ + const PinnedHeaderSliver(child: SizedBox(height: 500)), + SliverList.builder( + itemCount: 100, + itemBuilder: (BuildContext context, int index) => item, + ), + const SliverToBoxAdapter(child: item), + ], + ), + ], + ), + ), + ); + + await tester.scrollUntilVisible(find.byType(SliverToBoxAdapter), 500); + await tester.pumpAndSettle(); + + final RenderSliver sliverList = + find.byType(SliverList).evaluate().single.findRenderObject()! as RenderSliver; + + expect(sliverList.constraints.cacheOrigin, -250.0); + expect(sliverList.constraints.remainingCacheExtent, 1100); + }); + testWidgets('SliverMainAxisGroup correctly handles ensureVisible', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget(