Skip to content

Commit 0403ed4

Browse files
authored
Add viewportFraction feature to PageView (flutter#8539)
This feature lets you see a portion of the next and previous page in a PageView. Fixes flutter#8408
1 parent bb1dea7 commit 0403ed4

File tree

6 files changed

+380
-64
lines changed

6 files changed

+380
-64
lines changed

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

+89-13
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,41 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
1818
/// The main-axis extent of each item.
1919
double get itemExtent;
2020

21+
@protected
22+
double indexToScrollOffset(double itemExtent, int index) => itemExtent * index;
23+
24+
@protected
25+
int getMinChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
26+
return itemExtent > 0.0 ? math.max(0, scrollOffset ~/ itemExtent) : 0;
27+
}
28+
29+
@protected
30+
int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
31+
return itemExtent > 0.0 ? math.max(0, (scrollOffset / itemExtent).ceil() - 1) : 0;
32+
}
33+
34+
@protected
35+
double estimateMaxScrollOffset(SliverConstraints constraints, {
36+
int firstIndex,
37+
int lastIndex,
38+
double leadingScrollOffset,
39+
double trailingScrollOffset,
40+
}) {
41+
return childManager.estimateMaxScrollOffset(
42+
constraints,
43+
firstIndex: firstIndex,
44+
lastIndex: lastIndex,
45+
leadingScrollOffset: leadingScrollOffset,
46+
trailingScrollOffset: trailingScrollOffset,
47+
);
48+
}
49+
2150
@override
2251
void performLayout() {
2352
assert(childManager.debugAssertChildListLocked());
2453
childManager.setDidUnderflow(false);
2554

2655
final double itemExtent = this.itemExtent;
27-
double indexToScrollOffset(int index) => itemExtent * index;
2856

2957
final double scrollOffset = constraints.scrollOffset;
3058
assert(scrollOffset >= 0.0);
@@ -37,8 +65,8 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
3765
maxExtent: itemExtent,
3866
);
3967

40-
final int firstIndex = itemExtent > 0.0 ? math.max(0, scrollOffset ~/ itemExtent) : 0;
41-
final int targetLastIndex = itemExtent > 0.0 ? math.max(0, (targetEndScrollOffset / itemExtent).ceil() - 1) : 0;
68+
final int firstIndex = getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
69+
final int targetLastIndex = getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent);
4270

4371
if (firstChild != null) {
4472
final int oldFirstIndex = indexOf(firstChild);
@@ -50,7 +78,7 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
5078
}
5179

5280
if (firstChild == null) {
53-
if (!addInitialChild(index: firstIndex, scrollOffset: indexToScrollOffset(firstIndex))) {
81+
if (!addInitialChild(index: firstIndex, scrollOffset: indexToScrollOffset(itemExtent, firstIndex))) {
5482
// There are no children.
5583
geometry = SliverGeometry.zero;
5684
return;
@@ -62,15 +90,15 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
6290
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
6391
final RenderBox child = insertAndLayoutLeadingChild(childConstraints);
6492
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
65-
childParentData.layoutOffset = indexToScrollOffset(index);
93+
childParentData.layoutOffset = indexToScrollOffset(itemExtent, index);
6694
assert(childParentData.index == index);
6795
trailingChildWithLayout ??= child;
6896
}
6997

7098
if (trailingChildWithLayout == null) {
7199
firstChild.layout(childConstraints);
72100
final SliverMultiBoxAdaptorParentData childParentData = firstChild.parentData;
73-
childParentData.layoutOffset = indexToScrollOffset(firstIndex);
101+
childParentData.layoutOffset = indexToScrollOffset(itemExtent, firstIndex);
74102
trailingChildWithLayout = firstChild;
75103
}
76104

@@ -88,19 +116,19 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
88116
trailingChildWithLayout = child;
89117
assert(child != null);
90118
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
91-
childParentData.layoutOffset = indexToScrollOffset(childParentData.index);
119+
childParentData.layoutOffset = indexToScrollOffset(itemExtent, childParentData.index);
92120
}
93121

94122
final int lastIndex = indexOf(lastChild);
95-
final double leadingScrollOffset = indexToScrollOffset(firstIndex);
96-
final double trailingScrollOffset = indexToScrollOffset(lastIndex + 1);
123+
final double leadingScrollOffset = indexToScrollOffset(itemExtent, firstIndex);
124+
final double trailingScrollOffset = indexToScrollOffset(itemExtent, lastIndex + 1);
97125

98-
assert(childScrollOffset(firstChild) <= scrollOffset);
126+
assert(firstIndex == 0 || childScrollOffset(firstChild) <= scrollOffset);
99127
assert(debugAssertChildListIsNonEmptyAndContiguous());
100128
assert(indexOf(firstChild) == firstIndex);
101129
assert(lastIndex <= targetLastIndex);
102130

103-
final double estimatedMaxScrollOffset = childManager.estimateMaxScrollOffset(
131+
final double estimatedMaxScrollOffset = estimateMaxScrollOffset(
104132
constraints,
105133
firstIndex: firstIndex,
106134
lastIndex: lastIndex,
@@ -147,8 +175,56 @@ class RenderSliverFixedExtentList extends RenderSliverFixedExtentBoxAdaptor {
147175
class RenderSliverFill extends RenderSliverFixedExtentBoxAdaptor {
148176
RenderSliverFill({
149177
@required RenderSliverBoxChildManager childManager,
150-
}) : super(childManager: childManager);
178+
double viewportFraction: 1.0,
179+
}) : _viewportFraction = viewportFraction, super(childManager: childManager) {
180+
assert(viewportFraction != null);
181+
assert(viewportFraction > 0.0);
182+
}
183+
184+
@override
185+
double get itemExtent => constraints.viewportMainAxisExtent * viewportFraction;
186+
187+
double get viewportFraction => _viewportFraction;
188+
double _viewportFraction;
189+
set viewportFraction (double newValue) {
190+
assert(newValue != null);
191+
if (_viewportFraction == newValue)
192+
return;
193+
_viewportFraction = newValue;
194+
markNeedsLayout();
195+
}
196+
197+
double get _padding => (1.0 - viewportFraction) * constraints.viewportMainAxisExtent * 0.5;
198+
199+
@override
200+
double indexToScrollOffset(double itemExtent, int index) {
201+
return _padding + super.indexToScrollOffset(itemExtent, index);
202+
}
203+
204+
@override
205+
int getMinChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
206+
return super.getMinChildIndexForScrollOffset(math.max(scrollOffset - _padding, 0.0), itemExtent);
207+
}
208+
209+
@override
210+
int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) {
211+
return super.getMaxChildIndexForScrollOffset(math.max(scrollOffset - _padding, 0.0), itemExtent);
212+
}
151213

152214
@override
153-
double get itemExtent => constraints.viewportMainAxisExtent;
215+
double estimateMaxScrollOffset(SliverConstraints constraints, {
216+
int firstIndex,
217+
int lastIndex,
218+
double leadingScrollOffset,
219+
double trailingScrollOffset,
220+
}) {
221+
final double padding = _padding;
222+
return childManager.estimateMaxScrollOffset(
223+
constraints,
224+
firstIndex: firstIndex,
225+
lastIndex: lastIndex,
226+
leadingScrollOffset: leadingScrollOffset - padding,
227+
trailingScrollOffset: trailingScrollOffset - padding,
228+
) + padding + padding;
229+
}
154230
}

0 commit comments

Comments
 (0)