mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #894 from eseidelGoogle/page_changed
Add a pageChanged callback to PageableList
This commit is contained in:
commit
7051cf7c9e
@ -542,6 +542,8 @@ class ScrollableList<T> extends ScrollableWidgetList {
|
||||
}
|
||||
}
|
||||
|
||||
typedef void PageChangedCallback(int newPage);
|
||||
|
||||
class PageableList<T> extends ScrollableList<T> {
|
||||
PageableList({
|
||||
Key key,
|
||||
@ -551,6 +553,7 @@ class PageableList<T> extends ScrollableList<T> {
|
||||
ItemBuilder<T> itemBuilder,
|
||||
bool itemsWrap: false,
|
||||
double itemExtent,
|
||||
PageChangedCallback this.pageChanged,
|
||||
EdgeDims padding,
|
||||
this.duration: const Duration(milliseconds: 200),
|
||||
this.curve: ease
|
||||
@ -567,10 +570,12 @@ class PageableList<T> extends ScrollableList<T> {
|
||||
|
||||
Duration duration;
|
||||
Curve curve;
|
||||
PageChangedCallback pageChanged;
|
||||
|
||||
void syncConstructorArguments(PageableList<T> source) {
|
||||
duration = source.duration;
|
||||
curve = source.curve;
|
||||
pageChanged = source.pageChanged;
|
||||
super.syncConstructorArguments(source);
|
||||
}
|
||||
|
||||
@ -592,12 +597,19 @@ class PageableList<T> extends ScrollableList<T> {
|
||||
double newScrollOffset = _snapScrollOffset(scrollOffset + velocity.sign * itemExtent)
|
||||
.clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0),
|
||||
_snapScrollOffset(scrollOffset + itemExtent / 2.0));
|
||||
scrollTo(newScrollOffset, duration: duration, curve: curve);
|
||||
scrollTo(newScrollOffset, duration: duration, curve: curve).then(_notifyPageChanged);
|
||||
return EventDisposition.processed;
|
||||
}
|
||||
|
||||
int get currentPage => (scrollOffset / itemExtent).floor();
|
||||
|
||||
void _notifyPageChanged(_) {
|
||||
if (pageChanged != null)
|
||||
pageChanged(currentPage);
|
||||
}
|
||||
|
||||
void settleScrollOffset() {
|
||||
scrollTo(_snapScrollOffset(scrollOffset), duration: duration, curve: curve);
|
||||
scrollTo(_snapScrollOffset(scrollOffset), duration: duration, curve: curve).then(_notifyPageChanged);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ dependencies:
|
||||
sky: any
|
||||
sky_tools: any
|
||||
test: any
|
||||
quiver: any
|
||||
dependency_overrides:
|
||||
material_design_icons:
|
||||
path: ../packages/material_design_icons
|
||||
|
||||
54
sky/unit/test/widget/pageable_list_test.dart
Normal file
54
sky/unit/test/widget/pageable_list_test.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:sky/widgets.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:quiver/testing/async.dart';
|
||||
|
||||
import 'widget_tester.dart';
|
||||
|
||||
void main() {
|
||||
test('Scrolling changes page', () {
|
||||
WidgetTester tester = new WidgetTester();
|
||||
|
||||
List<int> pages = [0, 1, 2, 3, 4, 5];
|
||||
Size pageSize = new Size(200.0, 200.0);
|
||||
int currentPage;
|
||||
|
||||
Widget buildPage(int page) {
|
||||
return new Container(
|
||||
key: new StringKey(page.toString()),
|
||||
width: pageSize.width,
|
||||
height: pageSize.height,
|
||||
child: new Text(page.toString())
|
||||
);
|
||||
}
|
||||
|
||||
Widget builder() {
|
||||
return new Container(
|
||||
height: pageSize.height,
|
||||
child: new PageableList<int>(
|
||||
padding: new EdgeDims.symmetric(horizontal: 10.0),
|
||||
items: pages,
|
||||
itemBuilder: buildPage,
|
||||
scrollDirection: ScrollDirection.horizontal,
|
||||
itemExtent: pageSize.width,
|
||||
pageChanged: (int page) {
|
||||
currentPage = page;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
tester.pumpFrame(builder);
|
||||
// TODO(abarth): We shouldn't need to pump a second frame here.
|
||||
tester.pumpFrame(builder);
|
||||
|
||||
expect(currentPage, isNull);
|
||||
new FakeAsync().run((async) {
|
||||
tester.scroll(tester.findText('1'), new Offset(300.0, 0.0));
|
||||
// One frame to start the animation, a second to complete it.
|
||||
tester.pumpFrame(builder);
|
||||
tester.pumpFrame(builder, 5000.0);
|
||||
async.elapse(new Duration(seconds: 5));
|
||||
expect(currentPage, equals(2));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import 'dart:sky' as sky;
|
||||
import 'package:sky/rendering.dart';
|
||||
import 'package:sky/widgets.dart';
|
||||
import 'package:sky/base/scheduler.dart' as scheduler;
|
||||
|
||||
typedef Widget WidgetBuilder();
|
||||
|
||||
@ -21,6 +22,63 @@ class TestApp extends App {
|
||||
}
|
||||
}
|
||||
|
||||
class TestPointerEvent extends sky.PointerEvent {
|
||||
TestPointerEvent({
|
||||
this.type,
|
||||
this.pointer,
|
||||
this.kind,
|
||||
this.x,
|
||||
this.y,
|
||||
this.dx,
|
||||
this.dy,
|
||||
this.velocityX,
|
||||
this.velocityY,
|
||||
this.buttons,
|
||||
this.down,
|
||||
this.primary,
|
||||
this.obscured,
|
||||
this.pressure,
|
||||
this.pressureMin,
|
||||
this.pressureMax,
|
||||
this.distance,
|
||||
this.distanceMin,
|
||||
this.distanceMax,
|
||||
this.radiusMajor,
|
||||
this.radiusMinor,
|
||||
this.radiusMin,
|
||||
this.radiusMax,
|
||||
this.orientation,
|
||||
this.tilt
|
||||
});
|
||||
|
||||
// These are all of the PointerEvent members, but not all of Event.
|
||||
String type;
|
||||
int pointer;
|
||||
String kind;
|
||||
double x;
|
||||
double y;
|
||||
double dx;
|
||||
double dy;
|
||||
double velocityX;
|
||||
double velocityY;
|
||||
int buttons;
|
||||
bool down;
|
||||
bool primary;
|
||||
bool obscured;
|
||||
double pressure;
|
||||
double pressureMin;
|
||||
double pressureMax;
|
||||
double distance;
|
||||
double distanceMin;
|
||||
double distanceMax;
|
||||
double radiusMajor;
|
||||
double radiusMinor;
|
||||
double radiusMin;
|
||||
double radiusMax;
|
||||
double orientation;
|
||||
double tilt;
|
||||
}
|
||||
|
||||
class TestGestureEvent extends sky.GestureEvent {
|
||||
TestGestureEvent({
|
||||
this.type,
|
||||
@ -33,6 +91,7 @@ class TestGestureEvent extends sky.GestureEvent {
|
||||
this.velocityY
|
||||
});
|
||||
|
||||
// These are all of the GestureEvent members, but not all of Event.
|
||||
String type;
|
||||
int primaryPointer;
|
||||
double x;
|
||||
@ -105,14 +164,25 @@ class WidgetTester {
|
||||
dispatchEvent(new TestGestureEvent(type: 'gesturetap'), getCenter(widget));
|
||||
}
|
||||
|
||||
void scroll(Widget widget, Offset offset) {
|
||||
dispatchEvent(new TestGestureEvent(type: 'gesturescrollstart'), getCenter(widget));
|
||||
dispatchEvent(new TestGestureEvent(
|
||||
type: 'gesturescrollupdate',
|
||||
dx: offset.dx,
|
||||
dy: offset.dy), getCenter(widget));
|
||||
// pointerup to trigger scroll settling in Scrollable<T>
|
||||
dispatchEvent(new TestPointerEvent(
|
||||
type: 'pointerup', down: false, primary: true), getCenter(widget));
|
||||
}
|
||||
|
||||
void dispatchEvent(sky.Event event, Point position) {
|
||||
HitTestResult result = SkyBinding.instance.hitTest(position);
|
||||
SkyBinding.instance.dispatchEvent(event, result);
|
||||
}
|
||||
|
||||
void pumpFrame(WidgetBuilder builder) {
|
||||
void pumpFrame(WidgetBuilder builder, [double frameTimeMs = 0.0]) {
|
||||
_app.builder = builder;
|
||||
SkyBinding.instance.beginFrame(0.0);
|
||||
scheduler.beginFrame(frameTimeMs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user