Skip to content

Commit

Permalink
Snap scrolling: additional tests, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Hans Muller committed Sep 30, 2015
1 parent 200426e commit b77990c
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 51 deletions.
59 changes: 31 additions & 28 deletions examples/widgets/card_collection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,36 @@ class CardCollectionAppState extends State<CardCollectionApp> {
_initCardModels();
}

double _variableSizeToSnapOffset(double scrollOffset) {
double cumulativeHeight = 0.0;
double margins = 8.0;
List<double> cumulativeHeights = _cardModels.map((card) {
cumulativeHeight += card.height + margins;
return cumulativeHeight;
})
.toList();

double offsetForIndex(int i) {
return 12.0 + (margins + _cardModels[i].height) / 2.0 + ((i == 0) ? 0.0 : cumulativeHeights[i - 1]);
}

for (int i = 0; i < cumulativeHeights.length; i++) {
if (cumulativeHeights[i] >= scrollOffset)
return offsetForIndex(i);
}
return offsetForIndex(cumulativeHeights.length - 1);
}

double _fixedSizeToSnapOffset(double scrollOffset) {
double cardHeight = _cardModels[0].height;
int cardIndex = (scrollOffset.clamp(0.0, cardHeight * (_cardModels.length - 1)) / cardHeight).floor();
return 12.0 + cardIndex * cardHeight + cardHeight * 0.5;
}

double _toSnapOffset(double scrollOffset) {
return _fixedSizeCards ? _fixedSizeToSnapOffset(scrollOffset) : _variableSizeToSnapOffset(scrollOffset);
}

void dismissCard(CardModel card) {
if (_cardModels.contains(card)) {
setState(() {
Expand Down Expand Up @@ -155,7 +185,7 @@ class CardCollectionAppState extends State<CardCollectionApp> {
new DrawerHeader(child: new Text('Options')),
buildDrawerCheckbox("Snap fling scrolls to center", _snapToCenter, _toggleSnapToCenter),
buildDrawerCheckbox("Fixed size cards", _fixedSizeCards, _toggleFixedSizeCards),
new DrawerDivider(),//buildDrawerSpacerItem(),
new DrawerDivider(),
buildDrawerRadioItem(DismissDirection.horizontal, 'action/code'),
buildDrawerRadioItem(DismissDirection.left, 'navigation/arrow_back'),
buildDrawerRadioItem(DismissDirection.right, 'navigation/arrow_forward'),
Expand Down Expand Up @@ -255,33 +285,6 @@ class CardCollectionAppState extends State<CardCollectionApp> {
});
}

double _variableSizeToSnapOffset(double scrollOffset) {
double cumulativeHeight = 0.0;
double margins = 8.0;
List<double> cumulativeHeights = _cardModels.map((card) {
cumulativeHeight += card.height + margins;
return cumulativeHeight;
})
.toList();

for (int i = 0; i < cumulativeHeights.length; i++) {
if (cumulativeHeights[i] >= scrollOffset)
return 12.0 + (margins + _cardModels[i].height) / 2.0 + ((i == 0) ? 0.0 : cumulativeHeights[i - 1]);
}

assert(false);
return 0.0;
}

double _fixedSizeToSnapOffset(double scrollOffset) {
double cardHeight = _cardModels[0].height;
return 12.0 + (scrollOffset / cardHeight).floor() * cardHeight + cardHeight * 0.5;
}

double _toSnapOffset(double scrollOffset) {
return _fixedSizeCards ? _fixedSizeToSnapOffset(scrollOffset) : _variableSizeToSnapOffset(scrollOffset);
}

Widget build(BuildContext context) {

Widget cardCollection;
Expand Down
3 changes: 2 additions & 1 deletion sky/packages/sky/lib/src/fn3/scrollable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
if (!endScrollOffset.isNaN) {
double alignedScrollOffset = _alignedScrollSnapOffset(endScrollOffset);
if (_scrollOffsetIsInBounds(alignedScrollOffset)) {
double snapVelocity = velocity.abs() * (alignedScrollOffset - scrollOffset).sign;
Simulation toSnapSimulation = scrollBehavior.createSnapScrollSimulation(
scrollOffset, alignedScrollOffset, velocity);
scrollOffset, alignedScrollOffset, snapVelocity);
_toEndAnimation.start(toSnapSimulation);
return;
}
Expand Down
84 changes: 62 additions & 22 deletions sky/unit/test/widget/snap_scrolling_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@ double snapOffsetCallback(double offset) {
return (offset / itemExtent).floor() * itemExtent;
}

Widget buildScrollableList() {
Widget buildFrame() {
scrollableListKey = new GlobalKey();
return new Container(
height: itemExtent * 2.0,
child: new ScrollableList<int>(
key: scrollableListKey,
snapOffsetCallback: snapOffsetCallback,
scrollDirection: scrollDirection,
items: [0, 1, 2, 3, 4, 5, 7, 8, 9],
itemBuilder: buildItem,
itemExtent: itemExtent
return new Center(
child: new Container(
height: itemExtent * 2.0,
child: new ScrollableList<int>(
key: scrollableListKey,
snapOffsetCallback: snapOffsetCallback,
scrollDirection: scrollDirection,
items: [0, 1, 2, 3, 4, 5, 7, 8, 9],
itemBuilder: buildItem,
itemExtent: itemExtent
)
)
);
}
Expand All @@ -55,33 +57,71 @@ void fling(double velocity) {
}

void main() {
test('ScrollableList snap scrolling, fling(-800)', () {
WidgetTester tester = new WidgetTester();
WidgetTester tester = new WidgetTester();
tester.pumpFrame(buildFrame());

tester.pumpFrame(new Center(child: buildScrollableList()));
test('ScrollableList snap scrolling, fling(-800)', () {
scrollOffset = 0.0;
tester.pumpFrameWithoutChange();
expect(scrollOffset, 0.0);

double t0 = 0.0;
int dt = 1000;
new FakeAsync().run((async) {
fling(-800.0);
tester.pumpFrameWithoutChange(); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(1000.0);
async.elapse(new Duration(seconds: 1));
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(200.0, 1.0));
});
});

test('ScrollableList snap scrolling, fling(-2000)', () {
WidgetTester tester = new WidgetTester();

tester.pumpFrame(new Center(child: buildScrollableList()));
scrollOffset = 0.0;
tester.pumpFrameWithoutChange();
expect(scrollOffset, 0.0);

double t0 = 0.0;
int dt = 1000;
new FakeAsync().run((async) {
fling(-2000.0);
tester.pumpFrameWithoutChange(); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(1000.0);
async.elapse(new Duration(seconds: 1));
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(400.0, 1.0));
});
});

test('ScrollableList snap scrolling, fling(800)', () {
scrollOffset = 400.0;
tester.pumpFrameWithoutChange(1000.0);
expect(scrollOffset, 400.0);

double t0 = 0.0;
int dt = 2000;
new FakeAsync().run((async) {
fling(800.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(0.0, 1.0));
});
});

test('ScrollableList snap scrolling, fling(2000)', () {
scrollOffset = 800.0;
tester.pumpFrameWithoutChange(1000.0);
expect(scrollOffset, 800.0);

double t0 = 0.0;
int dt = 1500;
new FakeAsync().run((async) {
fling(2000.0);
tester.pumpFrameWithoutChange(t0); // Start the scheduler at 0.0
tester.pumpFrameWithoutChange(t0 + dt);
async.elapse(new Duration(milliseconds: dt));
expect(scrollOffset, closeTo(200.0, 1.0));
});
});

}

0 comments on commit b77990c

Please sign in to comment.