Merge pull request #1827 from abarth/scrollable_list_padding
Fix padding for ScrollableList
This commit is contained in:
@@ -142,13 +142,13 @@ class RenderList extends RenderVirtualViewport<ListParentData> {
|
||||
case Axis.vertical:
|
||||
itemWidth = math.max(0.0, size.width - (padding == null ? 0.0 : padding.horizontal));
|
||||
itemHeight = itemExtent ?? size.height;
|
||||
y = padding != null ? padding.top : 0.0;
|
||||
x = padding != null ? padding.left : 0.0;
|
||||
dy = itemHeight;
|
||||
break;
|
||||
case Axis.horizontal:
|
||||
itemWidth = itemExtent ?? size.width;
|
||||
itemHeight = math.max(0.0, size.height - (padding == null ? 0.0 : padding.vertical));
|
||||
x = padding != null ? padding.left : 0.0;
|
||||
y = padding != null ? padding.top : 0.0;
|
||||
dx = itemWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class _ScrollableListState extends ScrollableState<ScrollableList> {
|
||||
Widget buildContent(BuildContext context) {
|
||||
return new ListViewport(
|
||||
onExtentsChanged: _handleExtentsChanged,
|
||||
startOffset: scrollOffset,
|
||||
scrollOffset: scrollOffset,
|
||||
scrollDirection: config.scrollDirection,
|
||||
scrollAnchor: config.scrollAnchor,
|
||||
itemExtent: config.itemExtent,
|
||||
@@ -92,7 +92,7 @@ class _ScrollableListState extends ScrollableState<ScrollableList> {
|
||||
class _VirtualListViewport extends VirtualViewport {
|
||||
_VirtualListViewport(
|
||||
this.onExtentsChanged,
|
||||
this.startOffset,
|
||||
this.scrollOffset,
|
||||
this.scrollDirection,
|
||||
this.scrollAnchor,
|
||||
this.itemExtent,
|
||||
@@ -105,7 +105,7 @@ class _VirtualListViewport extends VirtualViewport {
|
||||
}
|
||||
|
||||
final ExtentsChangedCallback onExtentsChanged;
|
||||
final double startOffset;
|
||||
final double scrollOffset;
|
||||
final Axis scrollDirection;
|
||||
final ViewportAnchor scrollAnchor;
|
||||
final double itemExtent;
|
||||
@@ -113,6 +113,33 @@ class _VirtualListViewport extends VirtualViewport {
|
||||
final EdgeDims padding;
|
||||
final Painter overlayPainter;
|
||||
|
||||
double get _leadingPadding {
|
||||
switch (scrollDirection) {
|
||||
case Axis.vertical:
|
||||
switch (scrollAnchor) {
|
||||
case ViewportAnchor.start:
|
||||
return padding.top;
|
||||
case ViewportAnchor.end:
|
||||
return padding.bottom;
|
||||
}
|
||||
break;
|
||||
case Axis.horizontal:
|
||||
switch (scrollAnchor) {
|
||||
case ViewportAnchor.start:
|
||||
return padding.left;
|
||||
case ViewportAnchor.end:
|
||||
return padding.right;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double get startOffset {
|
||||
if (padding == null)
|
||||
return scrollOffset;
|
||||
return scrollOffset - _leadingPadding;
|
||||
}
|
||||
|
||||
RenderList createRenderObject() => new RenderList(itemExtent: itemExtent);
|
||||
|
||||
_VirtualListViewportElement createElement() => new _VirtualListViewportElement(this);
|
||||
@@ -156,32 +183,15 @@ class _VirtualListViewportElement extends VirtualViewportElement<_VirtualListVie
|
||||
|
||||
double containerExtent;
|
||||
double contentExtent;
|
||||
double leadingPadding;
|
||||
|
||||
switch (widget.scrollDirection) {
|
||||
case Axis.vertical:
|
||||
containerExtent = containerSize.height;
|
||||
contentExtent = length == null ? double.INFINITY : widget.itemExtent * length + padding.vertical;
|
||||
switch (widget.scrollAnchor) {
|
||||
case ViewportAnchor.start:
|
||||
leadingPadding = padding.top;
|
||||
break;
|
||||
case ViewportAnchor.end:
|
||||
leadingPadding = padding.bottom;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Axis.horizontal:
|
||||
containerExtent = renderObject.size.width;
|
||||
contentExtent = length == null ? double.INFINITY : widget.itemExtent * length + padding.horizontal;
|
||||
switch (widget.scrollAnchor) {
|
||||
case ViewportAnchor.start:
|
||||
leadingPadding = padding.left;
|
||||
break;
|
||||
case ViewportAnchor.end:
|
||||
leadingPadding = padding.right;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -191,8 +201,9 @@ class _VirtualListViewportElement extends VirtualViewportElement<_VirtualListVie
|
||||
_startOffsetBase = 0.0;
|
||||
_startOffsetLimit = double.INFINITY;
|
||||
} else {
|
||||
int startItem = math.max(0, (widget.startOffset - leadingPadding) ~/ itemExtent);
|
||||
int limitItem = math.max(0, ((widget.startOffset - leadingPadding + containerExtent) / itemExtent).ceil());
|
||||
final double startOffset = widget.startOffset;
|
||||
int startItem = math.max(0, startOffset ~/ itemExtent);
|
||||
int limitItem = math.max(0, ((startOffset + containerExtent) / itemExtent).ceil());
|
||||
|
||||
if (!widget.itemsWrap && length != null) {
|
||||
startItem = math.min(length, startItem);
|
||||
@@ -232,7 +243,7 @@ class _VirtualListViewportElement extends VirtualViewportElement<_VirtualListVie
|
||||
class ListViewport extends _VirtualListViewport with VirtualViewportIterableMixin {
|
||||
ListViewport({
|
||||
ExtentsChangedCallback onExtentsChanged,
|
||||
double startOffset: 0.0,
|
||||
double scrollOffset: 0.0,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
ViewportAnchor scrollAnchor: ViewportAnchor.start,
|
||||
double itemExtent,
|
||||
@@ -242,7 +253,7 @@ class ListViewport extends _VirtualListViewport with VirtualViewportIterableMixi
|
||||
this.children
|
||||
}) : super(
|
||||
onExtentsChanged,
|
||||
startOffset,
|
||||
scrollOffset,
|
||||
scrollDirection,
|
||||
scrollAnchor,
|
||||
itemExtent,
|
||||
@@ -327,7 +338,7 @@ class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> {
|
||||
Widget buildContent(BuildContext context) {
|
||||
return new LazyListViewport(
|
||||
onExtentsChanged: _handleExtentsChanged,
|
||||
startOffset: scrollOffset,
|
||||
scrollOffset: scrollOffset,
|
||||
scrollDirection: config.scrollDirection,
|
||||
scrollAnchor: config.scrollAnchor,
|
||||
itemExtent: config.itemExtent,
|
||||
@@ -342,7 +353,7 @@ class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> {
|
||||
class LazyListViewport extends _VirtualListViewport with VirtualViewportLazyMixin {
|
||||
LazyListViewport({
|
||||
ExtentsChangedCallback onExtentsChanged,
|
||||
double startOffset: 0.0,
|
||||
double scrollOffset: 0.0,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
ViewportAnchor scrollAnchor: ViewportAnchor.start,
|
||||
double itemExtent,
|
||||
@@ -352,7 +363,7 @@ class LazyListViewport extends _VirtualListViewport with VirtualViewportLazyMixi
|
||||
this.itemBuilder
|
||||
}) : super(
|
||||
onExtentsChanged,
|
||||
startOffset,
|
||||
scrollOffset,
|
||||
scrollDirection,
|
||||
scrollAnchor,
|
||||
itemExtent,
|
||||
|
||||
@@ -102,18 +102,19 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
|
||||
// If we don't already need layout, we need to request a layout if the
|
||||
// viewport has shifted to expose new children.
|
||||
if (!renderObject.needsLayout) {
|
||||
final double startOffset = widget.startOffset;
|
||||
bool shouldLayout = false;
|
||||
if (startOffsetBase != null) {
|
||||
if (widget.startOffset < startOffsetBase)
|
||||
if (startOffset < startOffsetBase)
|
||||
shouldLayout = true;
|
||||
else if (widget.startOffset == startOffsetBase && oldWidget?.startOffset != startOffsetBase)
|
||||
else if (startOffset == startOffsetBase && oldWidget?.startOffset != startOffsetBase)
|
||||
shouldLayout = true;
|
||||
}
|
||||
|
||||
if (startOffsetLimit != null) {
|
||||
if (widget.startOffset > startOffsetLimit)
|
||||
if (startOffset > startOffsetLimit)
|
||||
shouldLayout = true;
|
||||
else if (widget.startOffset == startOffsetLimit && oldWidget?.startOffset != startOffsetLimit)
|
||||
else if (startOffset == startOffsetLimit && oldWidget?.startOffset != startOffsetLimit)
|
||||
shouldLayout = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,11 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
|
||||
List<int> tapped = <int>[];
|
||||
|
||||
void main() {
|
||||
test('Tap item after scroll - horizontal', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
tester.pumpWidget(new Container());
|
||||
List<int> tapped = <int>[];
|
||||
tester.pumpWidget(new Center(
|
||||
child: new Container(
|
||||
height: 50.0,
|
||||
@@ -53,7 +52,7 @@ void main() {
|
||||
|
||||
test('Tap item after scroll - vertical', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
tester.pumpWidget(new Container());
|
||||
List<int> tapped = <int>[];
|
||||
tester.pumpWidget(new Center(
|
||||
child: new Container(
|
||||
width: 50.0,
|
||||
@@ -85,11 +84,80 @@ void main() {
|
||||
expect(tester.findText('3'), isNotNull);
|
||||
expect(tester.findText('4'), isNull);
|
||||
expect(tester.findText('5'), isNull);
|
||||
expect(tapped, equals([2]));
|
||||
expect(tapped, equals([]));
|
||||
tester.tap(tester.findText('1'));
|
||||
expect(tapped, equals([2, 1]));
|
||||
expect(tapped, equals([1]));
|
||||
tester.tap(tester.findText('3'));
|
||||
expect(tapped, equals([2, 1])); // the center of the third item is off-screen so it shouldn't get hit
|
||||
expect(tapped, equals([1])); // the center of the third item is off-screen so it shouldn't get hit
|
||||
});
|
||||
});
|
||||
|
||||
test('Padding scroll anchor start', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
List<int> tapped = <int>[];
|
||||
|
||||
tester.pumpWidget(
|
||||
new ScrollableList(
|
||||
key: new GlobalKey(),
|
||||
itemExtent: 290.0,
|
||||
padding: new EdgeDims.TRBL(20.0, 15.0, 10.0, 5.0),
|
||||
children: items.map((int item) {
|
||||
return new Container(
|
||||
child: new GestureDetector(
|
||||
onTap: () { tapped.add(item); },
|
||||
child: new Text('$item')
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
tester.tapAt(new Point(200.0, 19.0));
|
||||
expect(tapped, equals([]));
|
||||
tester.tapAt(new Point(200.0, 21.0));
|
||||
expect(tapped, equals([0]));
|
||||
tester.tapAt(new Point(4.0, 400.0));
|
||||
expect(tapped, equals([0]));
|
||||
tester.tapAt(new Point(6.0, 400.0));
|
||||
expect(tapped, equals([0, 1]));
|
||||
tester.tapAt(new Point(800.0 - 14.0, 400.0));
|
||||
expect(tapped, equals([0, 1]));
|
||||
tester.tapAt(new Point(800.0 - 16.0, 400.0));
|
||||
expect(tapped, equals([0, 1, 1]));
|
||||
});
|
||||
});
|
||||
|
||||
test('Padding scroll anchor end', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
List<int> tapped = <int>[];
|
||||
|
||||
tester.pumpWidget(
|
||||
new ScrollableList(
|
||||
key: new GlobalKey(),
|
||||
itemExtent: 290.0,
|
||||
scrollAnchor: ViewportAnchor.end,
|
||||
padding: new EdgeDims.TRBL(20.0, 15.0, 10.0, 5.0),
|
||||
children: items.map((int item) {
|
||||
return new Container(
|
||||
child: new GestureDetector(
|
||||
onTap: () { tapped.add(item); },
|
||||
child: new Text('$item')
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
tester.tapAt(new Point(200.0, 600.0 - 9.0));
|
||||
expect(tapped, equals([]));
|
||||
tester.tapAt(new Point(200.0, 600.0 - 11.0));
|
||||
expect(tapped, equals([5]));
|
||||
tester.tapAt(new Point(4.0, 200.0));
|
||||
expect(tapped, equals([5]));
|
||||
tester.tapAt(new Point(6.0, 200.0));
|
||||
expect(tapped, equals([5, 4]));
|
||||
tester.tapAt(new Point(800.0 - 14.0, 200.0));
|
||||
expect(tapped, equals([5, 4]));
|
||||
tester.tapAt(new Point(800.0 - 16.0, 200.0));
|
||||
expect(tapped, equals([5, 4, 4]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user