Fix SliverMainAxisGroup layout in reverse (#145572)

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

The original tests for SliverMainAxisGroup did not actually check where the child was painted (#126596).

Followed the same pattern in RenderSliverMultiBoxAdaptor:

11c034f037/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart (L666)
This commit is contained in:
Kate Lovett 2024-04-01 14:06:07 -05:00 committed by GitHub
parent bd909542a3
commit 6d7922f3f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 9 deletions

View File

@ -274,8 +274,8 @@ class RenderSliverMainAxisGroup extends RenderSliver with ContainerRenderObjectM
assert(() { assert(() {
if (child != null && maxPaintExtent.isInfinite) { if (child != null && maxPaintExtent.isInfinite) {
throw FlutterError( throw FlutterError(
'Unreachable sliver found, you may have a sliver behind ' 'Unreachable sliver found, you may have a sliver following '
'a sliver with infinite extent. ' 'a sliver with an infinite extent. '
); );
} }
return true; return true;
@ -327,12 +327,50 @@ class RenderSliverMainAxisGroup extends RenderSliver with ContainerRenderObjectM
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
RenderSliver? child = lastChild; if (firstChild == null) {
return;
}
// offset is to the top-left corner, regardless of our axis direction.
// originOffset gives us the delta from the real origin to the origin in the axis direction.
final Offset mainAxisUnit, crossAxisUnit, originOffset;
final bool addExtent;
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
mainAxisUnit = const Offset(0.0, -1.0);
crossAxisUnit = const Offset(1.0, 0.0);
originOffset = offset + Offset(0.0, geometry!.paintExtent);
addExtent = true;
case AxisDirection.right:
mainAxisUnit = const Offset(1.0, 0.0);
crossAxisUnit = const Offset(0.0, 1.0);
originOffset = offset;
addExtent = false;
case AxisDirection.down:
mainAxisUnit = const Offset(0.0, 1.0);
crossAxisUnit = const Offset(1.0, 0.0);
originOffset = offset;
addExtent = false;
case AxisDirection.left:
mainAxisUnit = const Offset(-1.0, 0.0);
crossAxisUnit = const Offset(0.0, 1.0);
originOffset = offset + Offset(geometry!.paintExtent, 0.0);
addExtent = true;
}
RenderSliver? child = lastChild;
while (child != null) { while (child != null) {
final double mainAxisDelta = childMainAxisPosition(child);
final double crossAxisDelta = childCrossAxisPosition(child);
Offset childOffset = Offset(
originOffset.dx + mainAxisUnit.dx * mainAxisDelta + crossAxisUnit.dx * crossAxisDelta,
originOffset.dy + mainAxisUnit.dy * mainAxisDelta + crossAxisUnit.dy * crossAxisDelta,
);
if (addExtent) {
childOffset += mainAxisUnit * child.geometry!.paintExtent;
}
if (child.geometry!.visible) { if (child.geometry!.visible) {
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData; context.paintChild(child, childOffset);
context.paintChild(child, offset + childParentData.paintOffset);
} }
child = childBefore(child); child = childBefore(child);
} }

View File

@ -32,9 +32,16 @@ void main() {
expect(controller.offset, 0); expect(controller.offset, 0);
expect(find.text('Group 0 Tile 0'), findsOneWidget); expect(find.text('Group 0 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 0')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 300.0),
);
expect(find.text('Group 0 Tile 1'), findsOneWidget); expect(find.text('Group 0 Tile 1'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 1')),
const Rect.fromLTRB(0.0, 300.0, 300.0, 600.0),
);
expect(find.text('Group 0 Tile 2'), findsNothing); expect(find.text('Group 0 Tile 2'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset = 19 * 300.0; const double scrollOffset = 19 * 300.0;
@ -44,7 +51,15 @@ void main() {
expect(controller.offset, scrollOffset); expect(controller.offset, scrollOffset);
expect(find.text('Group 0 Tile 18'), findsNothing); expect(find.text('Group 0 Tile 18'), findsNothing);
expect(find.text('Group 0 Tile 19'), findsOneWidget); expect(find.text('Group 0 Tile 19'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 19')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 300.0),
);
expect(find.text('Group 1 Tile 0'), findsOneWidget); expect(find.text('Group 1 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 1 Tile 0')),
const Rect.fromLTRB(0.0, 300.0, 300.0, 500.0),
);
final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList(); final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList();
final RenderSliverList first = renderSlivers[0]; final RenderSliverList first = renderSlivers[0];
@ -85,9 +100,16 @@ void main() {
expect(controller.offset, 0); expect(controller.offset, 0);
expect(find.text('Group 0 Tile 0'), findsOneWidget); expect(find.text('Group 0 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 0')),
const Rect.fromLTRB(0.0, 300.0, 300.0, 600.0),
);
expect(find.text('Group 0 Tile 1'), findsOneWidget); expect(find.text('Group 0 Tile 1'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 1')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 300.0),
);
expect(find.text('Group 0 Tile 2'), findsNothing); expect(find.text('Group 0 Tile 2'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset = 19 * 300.0; const double scrollOffset = 19 * 300.0;
@ -97,7 +119,15 @@ void main() {
expect(controller.offset, scrollOffset); expect(controller.offset, scrollOffset);
expect(find.text('Group 0 Tile 18'), findsNothing); expect(find.text('Group 0 Tile 18'), findsNothing);
expect(find.text('Group 0 Tile 19'), findsOneWidget); expect(find.text('Group 0 Tile 19'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 19')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 300.0),
);
expect(find.text('Group 1 Tile 0'), findsOneWidget); expect(find.text('Group 1 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 1 Tile 0')),
const Rect.fromLTRB(0.0, 400.0, 300.0, 600.0),
);
final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList(); final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList();
final RenderSliverList first = renderSlivers[0]; final RenderSliverList first = renderSlivers[0];
@ -138,8 +168,11 @@ void main() {
expect(controller.offset, 0); expect(controller.offset, 0);
expect(find.text('Group 0 Tile 0'), findsOneWidget); expect(find.text('Group 0 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 0')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 600.0),
);
expect(find.text('Group 0 Tile 1'), findsNothing); expect(find.text('Group 0 Tile 1'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset = 19 * 300.0; const double scrollOffset = 19 * 300.0;
@ -149,6 +182,10 @@ void main() {
expect(controller.offset, scrollOffset); expect(controller.offset, scrollOffset);
expect(find.text('Group 0 Tile 18'), findsNothing); expect(find.text('Group 0 Tile 18'), findsNothing);
expect(find.text('Group 0 Tile 19'), findsOneWidget); expect(find.text('Group 0 Tile 19'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 19')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 600.0),
);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset2 = 20 * 300.0; const double scrollOffset2 = 20 * 300.0;
@ -156,6 +193,10 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Group 0 Tile 19'), findsNothing); expect(find.text('Group 0 Tile 19'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsOneWidget); expect(find.text('Group 1 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 1 Tile 0')),
const Rect.fromLTRB(0.0, 0.0, 200.0, 600.0),
);
final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList(); final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList();
final RenderSliverList first = renderSlivers[0]; final RenderSliverList first = renderSlivers[0];
@ -197,8 +238,11 @@ void main() {
expect(controller.offset, 0); expect(controller.offset, 0);
expect(find.text('Group 0 Tile 0'), findsOneWidget); expect(find.text('Group 0 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 0')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 600.0),
);
expect(find.text('Group 0 Tile 1'), findsNothing); expect(find.text('Group 0 Tile 1'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset = 19 * 300.0; const double scrollOffset = 19 * 300.0;
@ -208,6 +252,10 @@ void main() {
expect(controller.offset, scrollOffset); expect(controller.offset, scrollOffset);
expect(find.text('Group 0 Tile 18'), findsNothing); expect(find.text('Group 0 Tile 18'), findsNothing);
expect(find.text('Group 0 Tile 19'), findsOneWidget); expect(find.text('Group 0 Tile 19'), findsOneWidget);
expect(
tester.getRect(find.text('Group 0 Tile 19')),
const Rect.fromLTRB(0.0, 0.0, 300.0, 600.0),
);
expect(find.text('Group 1 Tile 0'), findsNothing); expect(find.text('Group 1 Tile 0'), findsNothing);
const double scrollOffset2 = 20 * 300.0; const double scrollOffset2 = 20 * 300.0;
@ -215,6 +263,10 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Group 0 Tile 19'), findsNothing); expect(find.text('Group 0 Tile 19'), findsNothing);
expect(find.text('Group 1 Tile 0'), findsOneWidget); expect(find.text('Group 1 Tile 0'), findsOneWidget);
expect(
tester.getRect(find.text('Group 1 Tile 0')),
const Rect.fromLTRB(100.0, 0.0, 300.0, 600.0),
);
final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList(); final List<RenderSliverList> renderSlivers = tester.renderObjectList<RenderSliverList>(find.byType(SliverList)).toList();
final RenderSliverList first = renderSlivers[0]; final RenderSliverList first = renderSlivers[0];