Commit 021a2688 authored by Jeff McGlynn's avatar Jeff McGlynn Committed by Ian Hickson

Add a pageSnapping parameter to PageView (#12596)

* Add a pageSnapping parameter to PageView

Setting the pageSnapping property allows extending the PageView scroll
behavior, such as custom scroll animations or custom scroll bars.

* Apply pageSnapping CR feedback

- Remove _kNonSnappingPhysics, use null instead.
- Minor code style fixes.
- It turns out that the forth state is Arkansas, not California.
parent ea679171
......@@ -344,6 +344,7 @@ class PageView extends StatefulWidget {
this.reverse: false,
PageController controller,
this.physics,
this.pageSnapping: true,
this.onPageChanged,
List<Widget> children: const <Widget>[],
}) : controller = controller ?? _defaultPageController,
......@@ -368,6 +369,7 @@ class PageView extends StatefulWidget {
this.reverse: false,
PageController controller,
this.physics,
this.pageSnapping: true,
this.onPageChanged,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
......@@ -383,6 +385,7 @@ class PageView extends StatefulWidget {
this.reverse: false,
PageController controller,
this.physics,
this.pageSnapping: true,
this.onPageChanged,
@required this.childrenDelegate,
}) : assert(childrenDelegate != null),
......@@ -423,6 +426,9 @@ class PageView extends StatefulWidget {
/// Defaults to matching platform conventions.
final ScrollPhysics physics;
/// Set to false to disable page snapping, useful for custom scroll behavior.
final bool pageSnapping;
/// Called whenever the page in the center of the viewport changes.
final ValueChanged<int> onPageChanged;
......@@ -463,6 +469,10 @@ class _PageViewState extends State<PageView> {
@override
Widget build(BuildContext context) {
final AxisDirection axisDirection = _getDirection(context);
final ScrollPhysics physics = widget.pageSnapping
? _kPagePhysics.applyTo(widget.physics)
: widget.physics;
return new NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.depth == 0 && widget.onPageChanged != null && notification is ScrollUpdateNotification) {
......@@ -478,7 +488,7 @@ class _PageViewState extends State<PageView> {
child: new Scrollable(
axisDirection: axisDirection,
controller: widget.controller,
physics: widget.physics == null ? _kPagePhysics : _kPagePhysics.applyTo(widget.physics),
physics: physics,
viewportBuilder: (BuildContext context, ViewportOffset position) {
return new Viewport(
axisDirection: axisDirection,
......@@ -502,5 +512,6 @@ class _PageViewState extends State<PageView> {
description.add(new FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'));
description.add(new DiagnosticsProperty<PageController>('controller', widget.controller, showName: false));
description.add(new DiagnosticsProperty<ScrollPhysics>('physics', widget.physics, showName: false));
description.add(new FlagProperty('pageSnapping', value: widget.pageSnapping, ifFalse: 'snapping disabled'));
}
}
......@@ -368,6 +368,69 @@ void main() {
expect(tester.getTopLeft(find.text('Idaho')), const Offset(790.0, 0.0));
});
testWidgets('Page snapping disable and reenable', (WidgetTester tester) async {
final List<int> log = <int>[];
Widget build({ bool pageSnapping }) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
pageSnapping: pageSnapping,
onPageChanged: log.add,
children:
kStates.map<Widget>((String state) => new Text(state)).toList(),
),
);
}
await tester.pumpWidget(build(pageSnapping: true));
expect(log, isEmpty);
// Drag more than halfway to the next page, to confirm the default behavior.
TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
// The page view is 800.0 wide, so this move is just beyond halfway.
await gesture.moveBy(const Offset(-420.0, 0.0));
expect(log, equals(const <int>[1]));
log.clear();
// Release the gesture, confirm that the page settles on the next.
await gesture.up();
await tester.pumpAndSettle();
expect(find.text('Alabama'), findsNothing);
expect(find.text('Alaska'), findsOneWidget);
// Disable page snapping, and try moving halfway. Confirm it doesn't snap.
await tester.pumpWidget(build(pageSnapping: false));
gesture = await tester.startGesture(const Offset(100.0, 100.0));
// Move just beyond halfway, again.
await gesture.moveBy(const Offset(-420.0, 0.0));
// Page notifications still get sent.
expect(log, equals(const <int>[2]));
log.clear();
// Release the gesture, confirm that both pages are visible.
await gesture.up();
await tester.pumpAndSettle();
expect(find.text('Alabama'), findsNothing);
expect(find.text('Alaska'), findsOneWidget);
expect(find.text('Arizona'), findsOneWidget);
expect(find.text('Arkansas'), findsNothing);
// Now re-enable snapping, confirm that we've settled on a page.
await tester.pumpWidget(build(pageSnapping: true));
await tester.pumpAndSettle();
expect(log, isEmpty);
expect(find.text('Alaska'), findsNothing);
expect(find.text('Arizona'), findsOneWidget);
expect(find.text('Arkansas'), findsNothing);
});
testWidgets('PageView small viewportFraction', (WidgetTester tester) async {
final PageController controller = new PageController(viewportFraction: 1/8);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment