Skip to content

Commit 37f9c54

Browse files
Use RenderSliverPadding to inset SliverFillViewport (#45432)
1 parent 1374a41 commit 37f9c54

File tree

7 files changed

+378
-176
lines changed

7 files changed

+378
-176
lines changed

packages/flutter/lib/src/rendering/sliver_fill.dart

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -57,41 +57,6 @@ class RenderSliverFillViewport extends RenderSliverFixedExtentBoxAdaptor {
5757
_viewportFraction = value;
5858
markNeedsLayout();
5959
}
60-
61-
double get _padding => (1.0 - viewportFraction) * constraints.viewportMainAxisExtent * 0.5;
62-
63-
@override
64-
double indexToLayoutOffset(double itemExtent, int index) {
65-
return _padding + super.indexToLayoutOffset(itemExtent, index);
66-
}
67-
68-
@override
69-
int getMinChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
70-
return super.getMinChildIndexForScrollOffset(math.max(scrollOffset - _padding, 0.0), itemExtent);
71-
}
72-
73-
@override
74-
int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
75-
return super.getMaxChildIndexForScrollOffset(math.max(scrollOffset - _padding, 0.0), itemExtent);
76-
}
77-
78-
@override
79-
double estimateMaxScrollOffset(
80-
SliverConstraints constraints, {
81-
int firstIndex,
82-
int lastIndex,
83-
double leadingScrollOffset,
84-
double trailingScrollOffset,
85-
}) {
86-
final double padding = _padding;
87-
return childManager.estimateMaxScrollOffset(
88-
constraints,
89-
firstIndex: firstIndex,
90-
lastIndex: lastIndex,
91-
leadingScrollOffset: leadingScrollOffset - padding,
92-
trailingScrollOffset: trailingScrollOffset - padding,
93-
) + padding + padding;
94-
}
9560
}
9661

9762
/// A sliver that contains a single box child that fills the remaining space in

packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
195195
if (firstChild == null) {
196196
if (!addInitialChild(index: firstIndex, layoutOffset: indexToLayoutOffset(itemExtent, firstIndex))) {
197197
// There are either no children, or we are past the end of all our children.
198-
// If it is the later, we will need to find the first available child.
198+
// If it is the latter, we will need to find the first available child.
199199
double max;
200200
if (childManager.childCount != null) {
201201
max = computeMaxScrollOffset(constraints, itemExtent);

packages/flutter/lib/src/rendering/sliver_padding.dart

Lines changed: 110 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,73 +12,27 @@ import 'debug.dart';
1212
import 'object.dart';
1313
import 'sliver.dart';
1414

15-
/// Inset a [RenderSliver], applying padding on each side.
15+
/// Insets a [RenderSliver] by applying [resolvedPadding] on each side.
1616
///
17-
/// A [RenderSliverPadding] object wraps the [SliverGeometry.layoutExtent] of
18-
/// its child. Any incoming [SliverConstraints.overlap] is ignored and not
17+
/// A [RenderSliverEdgeInsetsPadding] subclass wraps the [SliverGeometry.layoutExtent]
18+
/// of its child. Any incoming [SliverConstraints.overlap] is ignored and not
1919
/// passed on to the child.
2020
///
21+
/// {@template flutter.rendering.sliverPadding.limitation}
2122
/// Applying padding to anything but the most mundane sliver is likely to have
22-
/// undesired effects. For example, wrapping a
23-
/// [RenderSliverPinnedPersistentHeader] will cause the app bar to overlap
24-
/// earlier slivers (contrary to the normal behavior of pinned app bars), and
25-
/// while the app bar is pinned, the padding will scroll away.
26-
class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> {
27-
/// Creates a render object that insets its child in a viewport.
28-
///
29-
/// The [padding] argument must not be null and must have non-negative insets.
30-
RenderSliverPadding({
31-
@required EdgeInsetsGeometry padding,
32-
TextDirection textDirection,
33-
RenderSliver child,
34-
}) : assert(padding != null),
35-
assert(padding.isNonNegative),
36-
_padding = padding,
37-
_textDirection = textDirection {
38-
this.child = child;
39-
}
40-
41-
EdgeInsets _resolvedPadding;
42-
43-
void _resolve() {
44-
if (_resolvedPadding != null)
45-
return;
46-
_resolvedPadding = padding.resolve(textDirection);
47-
assert(_resolvedPadding.isNonNegative);
48-
}
49-
50-
void _markNeedResolution() {
51-
_resolvedPadding = null;
52-
markNeedsLayout();
53-
}
54-
23+
/// undesired effects. For example, wrapping a [RenderSliverPinnedPersistentHeader]
24+
/// will cause the app bar to overlap earlier slivers (contrary to the normal
25+
/// behavior of pinned app bars), and while the app bar is pinned, the padding
26+
/// will scroll away.
27+
/// {@endtemplate}
28+
abstract class RenderSliverEdgeInsetsPadding extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> {
5529
/// The amount to pad the child in each dimension.
5630
///
57-
/// If this is set to an [EdgeInsetsDirectional] object, then [textDirection]
58-
/// must not be null.
59-
EdgeInsetsGeometry get padding => _padding;
60-
EdgeInsetsGeometry _padding;
61-
set padding(EdgeInsetsGeometry value) {
62-
assert(value != null);
63-
assert(padding.isNonNegative);
64-
if (_padding == value)
65-
return;
66-
_padding = value;
67-
_markNeedResolution();
68-
}
69-
70-
/// The text direction with which to resolve [padding].
31+
/// The offsets are specified in terms of visual edges, left, top, right, and
32+
/// bottom. These values are not affected by the [TextDirection].
7133
///
72-
/// This may be changed to null, but only after the [padding] has been changed
73-
/// to a value that does not depend on the direction.
74-
TextDirection get textDirection => _textDirection;
75-
TextDirection _textDirection;
76-
set textDirection(TextDirection value) {
77-
if (_textDirection == value)
78-
return;
79-
_textDirection = value;
80-
_markNeedResolution();
81-
}
34+
/// Must not be null or contain negative values when [performLayout] is called.
35+
EdgeInsets get resolvedPadding;
8236

8337
/// The padding in the scroll direction on the side nearest the 0.0 scroll direction.
8438
///
@@ -88,16 +42,16 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
8842
assert(constraints != null);
8943
assert(constraints.axisDirection != null);
9044
assert(constraints.growthDirection != null);
91-
assert(_resolvedPadding != null);
45+
assert(resolvedPadding != null);
9246
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
9347
case AxisDirection.up:
94-
return _resolvedPadding.bottom;
48+
return resolvedPadding.bottom;
9549
case AxisDirection.right:
96-
return _resolvedPadding.left;
50+
return resolvedPadding.left;
9751
case AxisDirection.down:
98-
return _resolvedPadding.top;
52+
return resolvedPadding.top;
9953
case AxisDirection.left:
100-
return _resolvedPadding.right;
54+
return resolvedPadding.right;
10155
}
10256
return null;
10357
}
@@ -110,16 +64,16 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
11064
assert(constraints != null);
11165
assert(constraints.axisDirection != null);
11266
assert(constraints.growthDirection != null);
113-
assert(_resolvedPadding != null);
67+
assert(resolvedPadding != null);
11468
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
11569
case AxisDirection.up:
116-
return _resolvedPadding.top;
70+
return resolvedPadding.top;
11771
case AxisDirection.right:
118-
return _resolvedPadding.right;
72+
return resolvedPadding.right;
11973
case AxisDirection.down:
120-
return _resolvedPadding.bottom;
74+
return resolvedPadding.bottom;
12175
case AxisDirection.left:
122-
return _resolvedPadding.left;
76+
return resolvedPadding.left;
12377
}
12478
return null;
12579
}
@@ -133,8 +87,8 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
13387
double get mainAxisPadding {
13488
assert(constraints != null);
13589
assert(constraints.axis != null);
136-
assert(_resolvedPadding != null);
137-
return _resolvedPadding.along(constraints.axis);
90+
assert(resolvedPadding != null);
91+
return resolvedPadding.along(constraints.axis);
13892
}
13993

14094
/// The total padding in the cross-axis direction. (In other words, for a
@@ -146,12 +100,12 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
146100
double get crossAxisPadding {
147101
assert(constraints != null);
148102
assert(constraints.axis != null);
149-
assert(_resolvedPadding != null);
103+
assert(resolvedPadding != null);
150104
switch (constraints.axis) {
151105
case Axis.horizontal:
152-
return _resolvedPadding.vertical;
106+
return resolvedPadding.vertical;
153107
case Axis.vertical:
154-
return _resolvedPadding.horizontal;
108+
return resolvedPadding.horizontal;
155109
}
156110
return null;
157111
}
@@ -164,8 +118,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
164118

165119
@override
166120
void performLayout() {
167-
_resolve();
168-
assert(_resolvedPadding != null);
121+
assert(resolvedPadding != null);
169122
final double beforePadding = this.beforePadding;
170123
final double afterPadding = this.afterPadding;
171124
final double mainAxisPadding = this.mainAxisPadding;
@@ -240,16 +193,16 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
240193
assert(constraints.growthDirection != null);
241194
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
242195
case AxisDirection.up:
243-
childParentData.paintOffset = Offset(_resolvedPadding.left, calculatePaintOffset(constraints, from: _resolvedPadding.bottom + childLayoutGeometry.scrollExtent, to: _resolvedPadding.bottom + childLayoutGeometry.scrollExtent + _resolvedPadding.top));
196+
childParentData.paintOffset = Offset(resolvedPadding.left, calculatePaintOffset(constraints, from: resolvedPadding.bottom + childLayoutGeometry.scrollExtent, to: resolvedPadding.bottom + childLayoutGeometry.scrollExtent + resolvedPadding.top));
244197
break;
245198
case AxisDirection.right:
246-
childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: 0.0, to: _resolvedPadding.left), _resolvedPadding.top);
199+
childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: 0.0, to: resolvedPadding.left), resolvedPadding.top);
247200
break;
248201
case AxisDirection.down:
249-
childParentData.paintOffset = Offset(_resolvedPadding.left, calculatePaintOffset(constraints, from: 0.0, to: _resolvedPadding.top));
202+
childParentData.paintOffset = Offset(resolvedPadding.left, calculatePaintOffset(constraints, from: 0.0, to: resolvedPadding.top));
250203
break;
251204
case AxisDirection.left:
252-
childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: _resolvedPadding.right + childLayoutGeometry.scrollExtent, to: _resolvedPadding.right + childLayoutGeometry.scrollExtent + _resolvedPadding.left), _resolvedPadding.top);
205+
childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: resolvedPadding.right + childLayoutGeometry.scrollExtent, to: resolvedPadding.right + childLayoutGeometry.scrollExtent + resolvedPadding.left), resolvedPadding.top);
253206
break;
254207
}
255208
assert(childParentData.paintOffset != null);
@@ -289,14 +242,14 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
289242
assert(constraints != null);
290243
assert(constraints.axisDirection != null);
291244
assert(constraints.growthDirection != null);
292-
assert(_resolvedPadding != null);
245+
assert(resolvedPadding != null);
293246
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
294247
case AxisDirection.up:
295248
case AxisDirection.down:
296-
return _resolvedPadding.left;
249+
return resolvedPadding.left;
297250
case AxisDirection.left:
298251
case AxisDirection.right:
299-
return _resolvedPadding.top;
252+
return resolvedPadding.top;
300253
}
301254
return null;
302255
}
@@ -346,6 +299,79 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
346299
return true;
347300
}());
348301
}
302+
}
303+
304+
/// Insets a [RenderSliver], applying padding on each side.
305+
///
306+
/// A [RenderSliverPadding] object wraps the [SliverGeometry.layoutExtent] of
307+
/// its child. Any incoming [SliverConstraints.overlap] is ignored and not
308+
/// passed on to the child.
309+
///
310+
/// {@macro flutter.rendering.sliverPadding.limitation}
311+
class RenderSliverPadding extends RenderSliverEdgeInsetsPadding {
312+
/// Creates a render object that insets its child in a viewport.
313+
///
314+
/// The [padding] argument must not be null and must have non-negative insets.
315+
RenderSliverPadding({
316+
@required EdgeInsetsGeometry padding,
317+
TextDirection textDirection,
318+
RenderSliver child,
319+
}) : assert(padding != null),
320+
assert(padding.isNonNegative),
321+
_padding = padding,
322+
_textDirection = textDirection {
323+
this.child = child;
324+
}
325+
326+
@override
327+
EdgeInsets get resolvedPadding => _resolvedPadding;
328+
EdgeInsets _resolvedPadding;
329+
330+
void _resolve() {
331+
if (resolvedPadding != null)
332+
return;
333+
_resolvedPadding = padding.resolve(textDirection);
334+
assert(resolvedPadding.isNonNegative);
335+
}
336+
337+
void _markNeedsResolution() {
338+
_resolvedPadding = null;
339+
markNeedsLayout();
340+
}
341+
342+
/// The amount to pad the child in each dimension.
343+
///
344+
/// If this is set to an [EdgeInsetsDirectional] object, then [textDirection]
345+
/// must not be null.
346+
EdgeInsetsGeometry get padding => _padding;
347+
EdgeInsetsGeometry _padding;
348+
set padding(EdgeInsetsGeometry value) {
349+
assert(value != null);
350+
assert(padding.isNonNegative);
351+
if (_padding == value)
352+
return;
353+
_padding = value;
354+
_markNeedsResolution();
355+
}
356+
357+
/// The text direction with which to resolve [padding].
358+
///
359+
/// This may be changed to null, but only after the [padding] has been changed
360+
/// to a value that does not depend on the direction.
361+
TextDirection get textDirection => _textDirection;
362+
TextDirection _textDirection;
363+
set textDirection(TextDirection value) {
364+
if (_textDirection == value)
365+
return;
366+
_textDirection = value;
367+
_markNeedsResolution();
368+
}
369+
370+
@override
371+
void performLayout() {
372+
_resolve();
373+
super.performLayout();
374+
}
349375

350376
@override
351377
void debugFillProperties(DiagnosticPropertiesBuilder properties) {

packages/flutter/lib/src/widgets/page_view.dart

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,16 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
345345
forcePixels(getPixelsFromPage(oldPage));
346346
}
347347

348+
// The amount of offset that will be added to [minScrollExtent] and subtracted
349+
// from [maxScrollExtent], such that every page will properly snap to the center
350+
// of the viewport when viewportFraction is greater than 1.
351+
//
352+
// The value is 0 if viewportFraction is less than or equal to 1, larger than 0
353+
// otherwise.
354+
double get _initialPageOffset => math.max(0, viewportDimension * (viewportFraction - 1) / 2);
355+
348356
double getPageFromPixels(double pixels, double viewportDimension) {
349-
final double actual = math.max(0.0, pixels) / math.max(1.0, viewportDimension * viewportFraction);
357+
final double actual = math.max(0.0, pixels - _initialPageOffset) / math.max(1.0, viewportDimension * viewportFraction);
350358
final double round = actual.roundToDouble();
351359
if ((actual - round).abs() < precisionErrorTolerance) {
352360
return round;
@@ -355,7 +363,7 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
355363
}
356364

357365
double getPixelsFromPage(double page) {
358-
return page * viewportDimension * viewportFraction;
366+
return page * viewportDimension * viewportFraction + _initialPageOffset;
359367
}
360368

361369
@override
@@ -396,6 +404,15 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
396404
return result;
397405
}
398406

407+
@override
408+
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
409+
final double newMinScrollExtent = minScrollExtent + _initialPageOffset;
410+
return super.applyContentDimensions(
411+
newMinScrollExtent,
412+
math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset),
413+
);
414+
}
415+
399416
@override
400417
PageMetrics copyWith({
401418
double minScrollExtent,

0 commit comments

Comments
 (0)