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'; ...@@ -19,11 +19,9 @@ import 'sliver.dart';
/// passed on to the child. /// passed on to the child.
/// ///
/// {@template flutter.rendering.sliverPadding.limitation} /// {@template flutter.rendering.sliverPadding.limitation}
/// Applying padding to anything but the most mundane sliver is likely to have /// Applying padding in the main extent of the viewport to slivers that have scroll effects is likely to have
/// undesired effects. For example, wrapping a [RenderSliverPinnedPersistentHeader] /// undesired effects. For example, For example, wrapping a [SliverPersistentHeader] with
/// will cause the app bar to overlap earlier slivers (contrary to the normal /// `pinned:true` will cause only the appbar to stay pinned while the padding will scroll away.
/// behavior of pinned app bars), and while the app bar is pinned, the padding
/// will scroll away.
/// {@endtemplate} /// {@endtemplate}
abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> { abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> {
/// The amount to pad the child in each dimension. /// The amount to pad the child in each dimension.
...@@ -129,11 +127,20 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj ...@@ -129,11 +127,20 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
); );
return; 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( child!.layout(
constraints.copyWith( constraints.copyWith(
scrollOffset: math.max(0.0, constraints.scrollOffset - beforePadding), scrollOffset: math.max(0.0, constraints.scrollOffset - beforePadding),
cacheOrigin: math.min(0.0, constraints.cacheOrigin + beforePadding), cacheOrigin: math.min(0.0, constraints.cacheOrigin + beforePadding),
overlap: 0.0, overlap: overlap,
remainingPaintExtent: constraints.remainingPaintExtent - calculatePaintOffset(constraints, from: 0.0, to: beforePadding), remainingPaintExtent: constraints.remainingPaintExtent - calculatePaintOffset(constraints, from: 0.0, to: beforePadding),
remainingCacheExtent: constraints.remainingCacheExtent - calculateCacheOffset(constraints, from: 0.0, to: beforePadding), remainingCacheExtent: constraints.remainingCacheExtent - calculateCacheOffset(constraints, from: 0.0, to: beforePadding),
crossAxisExtent: math.max(0.0, constraints.crossAxisExtent - crossAxisPadding), crossAxisExtent: math.max(0.0, constraints.crossAxisExtent - crossAxisPadding),
...@@ -148,11 +155,6 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj ...@@ -148,11 +155,6 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
); );
return; return;
} }
final double beforePaddingPaintExtent = calculatePaintOffset(
constraints,
from: 0.0,
to: beforePadding,
);
final double afterPaddingPaintExtent = calculatePaintOffset( final double afterPaddingPaintExtent = calculatePaintOffset(
constraints, constraints,
from: beforePadding + childLayoutGeometry.scrollExtent, from: beforePadding + childLayoutGeometry.scrollExtent,
...@@ -175,6 +177,7 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj ...@@ -175,6 +177,7 @@ abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObj
constraints.remainingPaintExtent, constraints.remainingPaintExtent,
); );
geometry = SliverGeometry( geometry = SliverGeometry(
paintOrigin: childLayoutGeometry.paintOrigin,
scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent, scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent,
paintExtent: paintExtent, paintExtent: paintExtent,
layoutExtent: math.min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), layoutExtent: math.min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent, paintExtent),
......
...@@ -3004,11 +3004,7 @@ class SliverToBoxAdapter extends SingleChildRenderObjectWidget { ...@@ -3004,11 +3004,7 @@ class SliverToBoxAdapter extends SingleChildRenderObjectWidget {
/// is a basic sliver that insets another sliver by applying padding on each /// is a basic sliver that insets another sliver by applying padding on each
/// side. /// side.
/// ///
/// Applying padding to anything but the most mundane sliver is likely to have /// {@macro flutter.rendering.sliverPadding.limitation}
/// 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.
/// ///
/// See also: /// See also:
/// ///
......
...@@ -9,6 +9,18 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -9,6 +9,18 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.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) { Future<void> test(WidgetTester tester, double offset, EdgeInsetsGeometry padding, AxisDirection axisDirection, TextDirection textDirection) {
return tester.pumpWidget( return tester.pumpWidget(
Directionality( Directionality(
...@@ -459,4 +471,79 @@ void main() { ...@@ -459,4 +471,79 @@ void main() {
equals(570), 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