Unverified Commit fa109c49 authored by Pieter van Loon's avatar Pieter van Loon Committed by GitHub

Sliver padding overlap fix (#65323)

parent cb3de6fb
......@@ -19,11 +19,9 @@ import 'sliver.dart';
/// passed on to the child.
///
/// {@template flutter.rendering.sliverPadding.limitation}
/// Applying padding to anything but the most mundane sliver is likely to have
/// undesired effects. For example, wrapping a [RenderSliverPinnedPersistentHeader]
/// will cause the app bar to overlap earlier slivers (contrary to the normal
/// behavior of pinned app bars), and while the app bar is pinned, the padding
/// will scroll away.
/// Applying padding in the main extent of the viewport to slivers that have scroll effects is likely to have
/// undesired effects. For example, For example, wrapping a [SliverPersistentHeader] with
/// `pinned:true` will cause only the appbar to stay pinned while the padding will scroll away.
/// {@endtemplate}
abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> {
/// The amount to pad the child in each dimension.
......@@ -129,11 +127,20 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
);
return;
}
final double beforePaddingPaintExtent = calculatePaintOffset(
constraints,
from: 0.0,
to: beforePadding,
);
double overlap = constraints.overlap;
if (overlap > 0) {
overlap = math.max(0.0, constraints.overlap - beforePaddingPaintExtent);
}
child!.layout(
constraints.copyWith(
scrollOffset: math.max(0.0, constraints.scrollOffset - beforePadding),
cacheOrigin: math.min(0.0, constraints.cacheOrigin + beforePadding),
overlap: 0.0,
overlap: overlap,
remainingPaintExtent: constraints.remainingPaintExtent - calculatePaintOffset(constraints, from: 0.0, to: beforePadding),
remainingCacheExtent: constraints.remainingCacheExtent - calculateCacheOffset(constraints, from: 0.0, to: beforePadding),
crossAxisExtent: math.max(0.0, constraints.crossAxisExtent - crossAxisPadding),
......@@ -148,11 +155,6 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
);
return;
}
final double beforePaddingPaintExtent = calculatePaintOffset(
constraints,
from: 0.0,
to: beforePadding,
);
final double afterPaddingPaintExtent = calculatePaintOffset(
constraints,
from: beforePadding + childLayoutGeometry.scrollExtent,
......@@ -175,6 +177,7 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
constraints.remainingPaintExtent,
);
geometry = SliverGeometry(
paintOrigin: childLayoutGeometry.paintOrigin,
scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent,
paintExtent: paintExtent,
layoutExtent: math.min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, paintExtent),
......
......@@ -3004,11 +3004,7 @@ class SliverToBoxAdapter extends SingleChildRenderObjectWidget {
/// is a basic sliver that insets another sliver by applying padding on each
/// side.
///
/// Applying padding to anything but the most mundane sliver is likely to have
/// undesired effects. For example, wrapping a [SliverPersistentHeader] with
/// `pinned:true` will cause the app bar to overlap earlier slivers (contrary to
/// the normal behavior of pinned app bars), and while the app bar is pinned,
/// the padding will scroll away.
/// {@macro flutter.rendering.sliverPadding.limitation}
///
/// See also:
///
......
......@@ -9,6 +9,18 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class _MockRenderSliver extends RenderSliver {
@override
void performLayout() {
geometry = const SliverGeometry(
paintOrigin: 10,
paintExtent: 10,
maxPaintExtent: 10,
);
}
}
Future<void> test(WidgetTester tester, double offset, EdgeInsetsGeometry padding, AxisDirection axisDirection, TextDirection textDirection) {
return tester.pumpWidget(
Directionality(
......@@ -459,4 +471,79 @@ void main() {
equals(570),
);
});
testWidgets('SliverPadding consumes only its padding from the overlap of its parent\'s constraints', (WidgetTester tester) async {
final _MockRenderSliver mock = _MockRenderSliver();
final RenderSliverPadding renderObject = RenderSliverPadding(
padding: const EdgeInsets.only(top: 20),
);
renderObject.child = mock;
renderObject.layout(const SliverConstraints(
viewportMainAxisExtent: 100.0,
overlap: 100.0,
cacheOrigin: 0.0,
scrollOffset: 0.0,
axisDirection: AxisDirection.down,
growthDirection: GrowthDirection.forward,
crossAxisExtent: 100.0,
crossAxisDirection: AxisDirection.right,
userScrollDirection: ScrollDirection.idle,
remainingPaintExtent: 100.0,
remainingCacheExtent: 100.0,
precedingScrollExtent: 0.0,
),
parentUsesSize: true,
);
expect(mock.constraints.overlap, 80.0);
});
testWidgets('SliverPadding passes the overlap to the child if it\'s negative', (WidgetTester tester) async {
final _MockRenderSliver mock = _MockRenderSliver();
final RenderSliverPadding renderObject = RenderSliverPadding(
padding: const EdgeInsets.only(top: 20),
);
renderObject.child = mock;
renderObject.layout(const SliverConstraints(
viewportMainAxisExtent: 100.0,
overlap: -100.0,
cacheOrigin: 0.0,
scrollOffset: 0.0,
axisDirection: AxisDirection.down,
growthDirection: GrowthDirection.forward,
crossAxisExtent: 100.0,
crossAxisDirection: AxisDirection.right,
userScrollDirection: ScrollDirection.idle,
remainingPaintExtent: 100.0,
remainingCacheExtent: 100.0,
precedingScrollExtent: 0.0,
),
parentUsesSize: true,
);
expect(mock.constraints.overlap, -100.0);
});
testWidgets('SliverPadding passes the paintOrigin of the child on', (WidgetTester tester) async {
final _MockRenderSliver mock = _MockRenderSliver();
final RenderSliverPadding renderObject = RenderSliverPadding(
padding: const EdgeInsets.only(top: 20),
);
renderObject.child = mock;
renderObject.layout(const SliverConstraints(
viewportMainAxisExtent: 100.0,
overlap: 100.0,
cacheOrigin: 0.0,
scrollOffset: 0.0,
axisDirection: AxisDirection.down,
growthDirection: GrowthDirection.forward,
crossAxisExtent: 100.0,
crossAxisDirection: AxisDirection.right,
userScrollDirection: ScrollDirection.idle,
remainingPaintExtent: 100.0,
remainingCacheExtent: 100.0,
precedingScrollExtent: 0.0,
),
parentUsesSize: true,
);
expect(renderObject.geometry.paintOrigin, 10.0);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment