Add PageView (#7809)
This widget is a start towards replacing PageableList. There are still a number of features that we'll need to add before this widget can replace PageableList.
This commit is contained in:
parent
3831e0b06d
commit
3231465769
@ -124,9 +124,9 @@ class PageableListAppState extends State<PageableListApp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context) {
|
Widget _buildBody(BuildContext context) {
|
||||||
return new PageableList(
|
return new PageView(
|
||||||
children: cardModels.map(buildCard),
|
children: cardModels.map(buildCard).toList(),
|
||||||
itemsWrap: itemsWrap,
|
// TODO(abarth): itemsWrap: itemsWrap,
|
||||||
scrollDirection: scrollDirection
|
scrollDirection: scrollDirection
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ void main() {
|
|||||||
theme: new ThemeData(
|
theme: new ThemeData(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
accentColor: Colors.redAccent[200]
|
accentColor: Colors.redAccent[200],
|
||||||
),
|
),
|
||||||
home: new PageableListApp()
|
home: new PageableListApp()
|
||||||
));
|
));
|
||||||
|
49
packages/flutter/lib/src/widgets/page_scroll_physics.dart
Normal file
49
packages/flutter/lib/src/widgets/page_scroll_physics.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/physics.dart';
|
||||||
|
|
||||||
|
import 'scroll_absolute.dart';
|
||||||
|
|
||||||
|
class PageScrollPhysics extends ScrollPhysicsProxy {
|
||||||
|
const PageScrollPhysics({
|
||||||
|
ScrollPhysics parent,
|
||||||
|
this.springDescription,
|
||||||
|
}) : super(parent);
|
||||||
|
|
||||||
|
final SpringDescription springDescription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
PageScrollPhysics applyTo(ScrollPhysics parent) {
|
||||||
|
return new PageScrollPhysics(
|
||||||
|
parent: parent,
|
||||||
|
springDescription: springDescription,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _roundToPage(AbsoluteScrollPosition position, double pixels, double pageSize) {
|
||||||
|
final int index = (pixels + pageSize / 2.0) ~/ pageSize;
|
||||||
|
return (pageSize * index).clamp(position.minScrollExtent, position.maxScrollExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getTargetPixels(AbsoluteScrollPosition position, double velocity) {
|
||||||
|
final double pageSize = position.viewportDimension;
|
||||||
|
if (velocity < -position.scrollTolerances.velocity)
|
||||||
|
return _roundToPage(position, position.pixels - pageSize / 2.0, pageSize);
|
||||||
|
if (velocity > position.scrollTolerances.velocity)
|
||||||
|
return _roundToPage(position, position.pixels + pageSize / 2.0, pageSize);
|
||||||
|
return _roundToPage(position, position.pixels, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
||||||
|
// If we're out of range and not headed back in range, defer to the parent
|
||||||
|
// ballistics, which should put us back in range at a page boundary.
|
||||||
|
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
|
||||||
|
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
|
||||||
|
return super.createBallisticSimulation(position, velocity);
|
||||||
|
final double target = _getTargetPixels(position, velocity);
|
||||||
|
return new ScrollSpringSimulation(scrollSpring, position.pixels, target, velocity);
|
||||||
|
}
|
||||||
|
}
|
@ -94,9 +94,16 @@ class ViewportScrollBehavior extends ScrollBehavior2 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollPhysics _getEffectiveScrollPhysics(BuildContext context, ScrollPhysics physics) {
|
||||||
|
final ScrollPhysics defaultPhysics = getScrollPhysics(getPlatform(context));
|
||||||
|
if (physics != null)
|
||||||
|
return physics.applyTo(defaultPhysics);
|
||||||
|
return defaultPhysics;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
|
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition, ScrollPhysics physics) {
|
||||||
return new AbsoluteScrollPosition(state, scrollTolerances, oldPosition, getScrollPhysics(getPlatform(context)));
|
return new AbsoluteScrollPosition(state, scrollTolerances, oldPosition, _getEffectiveScrollPhysics(context, physics));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -108,6 +115,8 @@ class ViewportScrollBehavior extends ScrollBehavior2 {
|
|||||||
abstract class ScrollPhysics {
|
abstract class ScrollPhysics {
|
||||||
const ScrollPhysics();
|
const ScrollPhysics();
|
||||||
|
|
||||||
|
ScrollPhysicsProxy applyTo(ScrollPhysics parent) => this;
|
||||||
|
|
||||||
/// Used by [AbsoluteDragScrollActivity] and other user-driven activities to
|
/// Used by [AbsoluteDragScrollActivity] and other user-driven activities to
|
||||||
/// convert an offset in logical pixels as provided by the [DragUpdateDetails]
|
/// convert an offset in logical pixels as provided by the [DragUpdateDetails]
|
||||||
/// into a delta to apply using [setPixels].
|
/// into a delta to apply using [setPixels].
|
||||||
@ -133,6 +142,58 @@ abstract class ScrollPhysics {
|
|||||||
/// [AbsoluteBallisticScrollActivity] with the returned value. Otherwise, the
|
/// [AbsoluteBallisticScrollActivity] with the returned value. Otherwise, the
|
||||||
/// [ScrollPosition] will begin an idle activity instead.
|
/// [ScrollPosition] will begin an idle activity instead.
|
||||||
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) => null;
|
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) => null;
|
||||||
|
|
||||||
|
static final SpringDescription _kDefaultScrollSpring = new SpringDescription.withDampingRatio(
|
||||||
|
mass: 0.5,
|
||||||
|
springConstant: 100.0,
|
||||||
|
ratio: 1.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
SpringDescription get scrollSpring => _kDefaultScrollSpring;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ScrollPhysicsProxy extends ScrollPhysics {
|
||||||
|
const ScrollPhysicsProxy(this.parent);
|
||||||
|
|
||||||
|
final ScrollPhysics parent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ScrollPhysicsProxy applyTo(ScrollPhysics parent) {
|
||||||
|
throw new FlutterError(
|
||||||
|
'$runtimeType must override applyTo.\n'
|
||||||
|
'The default implementation of applyTo is not appropriate for subclasses '
|
||||||
|
'of ScrollPhysicsProxy because they should return an instance of themselves '
|
||||||
|
'with their parent property replaced with the given ScrollPhysics instance.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double applyPhysicsToUserOffset(AbsoluteScrollPosition position, double offset) {
|
||||||
|
if (parent == null)
|
||||||
|
return super.applyPhysicsToUserOffset(position, offset);
|
||||||
|
return parent.applyPhysicsToUserOffset(position, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double applyBoundaryConditions(AbsoluteScrollPosition position, double value) {
|
||||||
|
if (parent == null)
|
||||||
|
return super.applyBoundaryConditions(position, value);
|
||||||
|
return parent.applyBoundaryConditions(position, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
||||||
|
if (parent == null)
|
||||||
|
return super.createBallisticSimulation(position, velocity);
|
||||||
|
return parent.createBallisticSimulation(position, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
SpringDescription get scrollSpring {
|
||||||
|
if (parent == null)
|
||||||
|
return super.scrollSpring;
|
||||||
|
return parent.scrollSpring;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AbsoluteScrollPosition extends ScrollPosition {
|
class AbsoluteScrollPosition extends ScrollPosition {
|
||||||
@ -405,6 +466,7 @@ class BouncingScrollPhysics extends ScrollPhysics {
|
|||||||
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
||||||
if (velocity.abs() >= position.scrollTolerances.velocity || position.outOfRange) {
|
if (velocity.abs() >= position.scrollTolerances.velocity || position.outOfRange) {
|
||||||
return new BouncingScrollSimulation(
|
return new BouncingScrollSimulation(
|
||||||
|
spring: scrollSpring,
|
||||||
position: position.pixels,
|
position: position.pixels,
|
||||||
velocity: velocity,
|
velocity: velocity,
|
||||||
leadingExtent: position.minScrollExtent,
|
leadingExtent: position.minScrollExtent,
|
||||||
@ -446,19 +508,13 @@ class ClampingScrollPhysics extends ScrollPhysics {
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final SpringDescription _defaultScrollSpring = new SpringDescription.withDampingRatio(
|
|
||||||
mass: 0.5,
|
|
||||||
springConstant: 100.0,
|
|
||||||
ratio: 1.1,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
Simulation createBallisticSimulation(AbsoluteScrollPosition position, double velocity) {
|
||||||
if (position.outOfRange) {
|
if (position.outOfRange) {
|
||||||
if (position.pixels > position.maxScrollExtent)
|
if (position.pixels > position.maxScrollExtent)
|
||||||
return new ScrollSpringSimulation(_defaultScrollSpring, position.pixels, position.maxScrollExtent, math.min(0.0, velocity));
|
return new ScrollSpringSimulation(scrollSpring, position.pixels, position.maxScrollExtent, math.min(0.0, velocity));
|
||||||
if (position.pixels < position.minScrollExtent)
|
if (position.pixels < position.minScrollExtent)
|
||||||
return new ScrollSpringSimulation(_defaultScrollSpring, position.pixels, position.minScrollExtent, math.max(0.0, velocity));
|
return new ScrollSpringSimulation(scrollSpring, position.pixels, position.minScrollExtent, math.max(0.0, velocity));
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
if (!position.atEdge && velocity.abs() >= position.scrollTolerances.velocity) {
|
if (!position.atEdge && velocity.abs() >= position.scrollTolerances.velocity) {
|
||||||
|
@ -33,10 +33,10 @@ class BouncingScrollSimulation extends SimulationGroup {
|
|||||||
@required double velocity,
|
@required double velocity,
|
||||||
@required double leadingExtent,
|
@required double leadingExtent,
|
||||||
@required double trailingExtent,
|
@required double trailingExtent,
|
||||||
SpringDescription spring,
|
@required SpringDescription spring,
|
||||||
}) : _leadingExtent = leadingExtent,
|
}) : _leadingExtent = leadingExtent,
|
||||||
_trailingExtent = trailingExtent,
|
_trailingExtent = trailingExtent,
|
||||||
_spring = spring ?? _defaultScrollSpring {
|
_spring = spring {
|
||||||
assert(position != null);
|
assert(position != null);
|
||||||
assert(velocity != null);
|
assert(velocity != null);
|
||||||
assert(_leadingExtent != null);
|
assert(_leadingExtent != null);
|
||||||
@ -50,12 +50,6 @@ class BouncingScrollSimulation extends SimulationGroup {
|
|||||||
final double _trailingExtent;
|
final double _trailingExtent;
|
||||||
final SpringDescription _spring;
|
final SpringDescription _spring;
|
||||||
|
|
||||||
static final SpringDescription _defaultScrollSpring = new SpringDescription.withDampingRatio(
|
|
||||||
mass: 0.5,
|
|
||||||
springConstant: 100.0,
|
|
||||||
ratio: 1.1,
|
|
||||||
);
|
|
||||||
|
|
||||||
bool _isSpringing = false;
|
bool _isSpringing = false;
|
||||||
Simulation _currentSimulation;
|
Simulation _currentSimulation;
|
||||||
double _offset = 0.0;
|
double _offset = 0.0;
|
||||||
|
@ -7,6 +7,8 @@ import 'package:meta/meta.dart';
|
|||||||
|
|
||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
import 'basic.dart';
|
import 'basic.dart';
|
||||||
|
import 'page_scroll_physics.dart';
|
||||||
|
import 'scroll_absolute.dart';
|
||||||
import 'scrollable.dart';
|
import 'scrollable.dart';
|
||||||
import 'sliver.dart';
|
import 'sliver.dart';
|
||||||
import 'viewport.dart';
|
import 'viewport.dart';
|
||||||
@ -20,6 +22,7 @@ class ScrollView extends StatelessWidget {
|
|||||||
this.padding,
|
this.padding,
|
||||||
this.initialScrollOffset: 0.0,
|
this.initialScrollOffset: 0.0,
|
||||||
this.itemExtent,
|
this.itemExtent,
|
||||||
|
this.physics,
|
||||||
this.shrinkWrap: false,
|
this.shrinkWrap: false,
|
||||||
this.children: const <Widget>[],
|
this.children: const <Widget>[],
|
||||||
}) : super(key: key) {
|
}) : super(key: key) {
|
||||||
@ -38,6 +41,8 @@ class ScrollView extends StatelessWidget {
|
|||||||
|
|
||||||
final double itemExtent;
|
final double itemExtent;
|
||||||
|
|
||||||
|
final ScrollPhysics physics;
|
||||||
|
|
||||||
final bool shrinkWrap;
|
final bool shrinkWrap;
|
||||||
|
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
@ -76,6 +81,7 @@ class ScrollView extends StatelessWidget {
|
|||||||
return new Scrollable2(
|
return new Scrollable2(
|
||||||
axisDirection: axisDirection,
|
axisDirection: axisDirection,
|
||||||
initialScrollOffset: initialScrollOffset,
|
initialScrollOffset: initialScrollOffset,
|
||||||
|
physics: physics,
|
||||||
viewportBuilder: (BuildContext context, ViewportOffset offset) {
|
viewportBuilder: (BuildContext context, ViewportOffset offset) {
|
||||||
if (shrinkWrap) {
|
if (shrinkWrap) {
|
||||||
return new ShrinkWrappingViewport(
|
return new ShrinkWrappingViewport(
|
||||||
@ -166,3 +172,21 @@ class ScrollGrid extends ScrollView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PageView extends ScrollView {
|
||||||
|
PageView({
|
||||||
|
Key key,
|
||||||
|
Axis scrollDirection: Axis.horizontal,
|
||||||
|
List<Widget> children: const <Widget>[],
|
||||||
|
}) : super(
|
||||||
|
key: key,
|
||||||
|
scrollDirection: scrollDirection,
|
||||||
|
physics: const PageScrollPhysics(),
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildChildLayout(BuildContext context) {
|
||||||
|
return new SliverFill(delegate: childrenDelegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,13 +20,15 @@ import 'framework.dart';
|
|||||||
import 'gesture_detector.dart';
|
import 'gesture_detector.dart';
|
||||||
import 'notification_listener.dart';
|
import 'notification_listener.dart';
|
||||||
import 'page_storage.dart';
|
import 'page_storage.dart';
|
||||||
import 'scroll_absolute.dart' show ViewportScrollBehavior;
|
|
||||||
import 'scroll_behavior.dart';
|
import 'scroll_behavior.dart';
|
||||||
import 'scroll_configuration.dart';
|
import 'scroll_configuration.dart';
|
||||||
import 'scroll_notification.dart';
|
import 'scroll_notification.dart';
|
||||||
import 'ticker_provider.dart';
|
import 'ticker_provider.dart';
|
||||||
import 'viewport.dart';
|
import 'viewport.dart';
|
||||||
|
|
||||||
|
// TODO(abarth): Merge AbsoluteScrollPosition and ScrollPosition.
|
||||||
|
import 'scroll_absolute.dart' show ViewportScrollBehavior, ScrollPhysics;
|
||||||
|
|
||||||
export 'package:flutter/physics.dart' show Tolerance;
|
export 'package:flutter/physics.dart' show Tolerance;
|
||||||
|
|
||||||
// This file defines an unopinionated scrolling mechanism.
|
// This file defines an unopinionated scrolling mechanism.
|
||||||
@ -360,7 +362,7 @@ abstract class ScrollBehavior2 {
|
|||||||
/// object must be disposed (via [ScrollPosition.oldPosition]) in the same
|
/// object must be disposed (via [ScrollPosition.oldPosition]) in the same
|
||||||
/// call stack. Passing a non-null `oldPosition` is a destructive operation
|
/// call stack. Passing a non-null `oldPosition` is a destructive operation
|
||||||
/// for that [ScrollPosition].
|
/// for that [ScrollPosition].
|
||||||
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition);
|
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition, ScrollPhysics physics);
|
||||||
|
|
||||||
/// Whether this delegate is different than the old delegate, or would now
|
/// Whether this delegate is different than the old delegate, or would now
|
||||||
/// return meaningfully different widgets from [wrap] or a meaningfully
|
/// return meaningfully different widgets from [wrap] or a meaningfully
|
||||||
@ -405,6 +407,7 @@ class Scrollable2 extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.initialScrollOffset: 0.0,
|
this.initialScrollOffset: 0.0,
|
||||||
this.axisDirection: AxisDirection.down,
|
this.axisDirection: AxisDirection.down,
|
||||||
|
this.physics,
|
||||||
this.scrollBehavior,
|
this.scrollBehavior,
|
||||||
@required this.viewportBuilder,
|
@required this.viewportBuilder,
|
||||||
}) : super (key: key) {
|
}) : super (key: key) {
|
||||||
@ -417,6 +420,8 @@ class Scrollable2 extends StatefulWidget {
|
|||||||
|
|
||||||
final AxisDirection axisDirection;
|
final AxisDirection axisDirection;
|
||||||
|
|
||||||
|
final ScrollPhysics physics;
|
||||||
|
|
||||||
/// The delegate that creates the [ScrollPosition] and wraps the viewport
|
/// The delegate that creates the [ScrollPosition] and wraps the viewport
|
||||||
/// in extra widgets (e.g. for overscroll effects).
|
/// in extra widgets (e.g. for overscroll effects).
|
||||||
///
|
///
|
||||||
@ -484,7 +489,7 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
|
|||||||
void _updatePosition() {
|
void _updatePosition() {
|
||||||
_scrollBehavior = config.scrollBehavior ?? Scrollable2.getScrollBehavior(context);
|
_scrollBehavior = config.scrollBehavior ?? Scrollable2.getScrollBehavior(context);
|
||||||
final ScrollPosition oldPosition = position;
|
final ScrollPosition oldPosition = position;
|
||||||
_position = _scrollBehavior.createScrollPosition(context, this, oldPosition);
|
_position = _scrollBehavior.createScrollPosition(context, this, oldPosition, config.physics);
|
||||||
assert(position != null);
|
assert(position != null);
|
||||||
if (oldPosition != null) {
|
if (oldPosition != null) {
|
||||||
// It's important that we not do this until after the viewport has had a
|
// It's important that we not do this until after the viewport has had a
|
||||||
|
@ -54,8 +54,6 @@ abstract class SliverChildDelegate {
|
|||||||
// /// demand). For example, the body of a dialog box might fit both of these
|
// /// demand). For example, the body of a dialog box might fit both of these
|
||||||
// /// conditions.
|
// /// conditions.
|
||||||
class SliverChildListDelegate extends SliverChildDelegate {
|
class SliverChildListDelegate extends SliverChildDelegate {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
|
||||||
/// const constructors so that they can be used in const expressions.
|
|
||||||
const SliverChildListDelegate(this.children);
|
const SliverChildListDelegate(this.children);
|
||||||
|
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
|
@ -313,9 +313,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 100));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
expect(scrollableState.position.pixels, greaterThan(0.0));
|
expect(scrollableState.position.pixels, greaterThan(0.0));
|
||||||
}, skip: Scrollable == Scrollable &&
|
}, skip: Scrollable != Scrollable2); // TODO(abarth): re-enable when ensureVisible is implemented
|
||||||
ScrollableViewport == ScrollableViewport &&
|
|
||||||
Block == Block); // TODO(abarth): re-enable when ensureVisible is implemented
|
|
||||||
|
|
||||||
testWidgets('Stepper index test', (WidgetTester tester) async {
|
testWidgets('Stepper index test', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
76
packages/flutter/test/widgets/page_view_test.dart
Normal file
76
packages/flutter/test/widgets/page_view_test.dart
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'states.dart';
|
||||||
|
|
||||||
|
const Duration _frameDuration = const Duration(milliseconds: 100);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('PageView control test', (WidgetTester tester) async {
|
||||||
|
List<String> log = <String>[];
|
||||||
|
|
||||||
|
await tester.pumpWidget(new PageView(
|
||||||
|
children: kStates.map<Widget>((String state) {
|
||||||
|
return new GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
log.add(state);
|
||||||
|
},
|
||||||
|
child: new Container(
|
||||||
|
height: 200.0,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
backgroundColor: const Color(0xFF0000FF),
|
||||||
|
),
|
||||||
|
child: new Text(state),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList()
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.text('Alabama'));
|
||||||
|
expect(log, equals(<String>['Alabama']));
|
||||||
|
log.clear();
|
||||||
|
|
||||||
|
expect(find.text('Alaska'), findsNothing);
|
||||||
|
|
||||||
|
await tester.scroll(find.byType(PageView), const Offset(-10.0, 0.0));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Alabama'), findsOneWidget);
|
||||||
|
expect(find.text('Alaska'), findsOneWidget);
|
||||||
|
expect(find.text('Arizona'), findsNothing);
|
||||||
|
|
||||||
|
await tester.pumpUntilNoTransientCallbacks(_frameDuration);
|
||||||
|
|
||||||
|
expect(find.text('Alabama'), findsOneWidget);
|
||||||
|
expect(find.text('Alaska'), findsNothing);
|
||||||
|
|
||||||
|
await tester.scroll(find.byType(PageView), const Offset(-401.0, 0.0));
|
||||||
|
await tester.pumpUntilNoTransientCallbacks(_frameDuration);
|
||||||
|
|
||||||
|
expect(find.text('Alabama'), findsNothing);
|
||||||
|
expect(find.text('Alaska'), findsOneWidget);
|
||||||
|
expect(find.text('Arizona'), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Alaska'));
|
||||||
|
expect(log, equals(<String>['Alaska']));
|
||||||
|
log.clear();
|
||||||
|
|
||||||
|
await tester.fling(find.byType(PageView), const Offset(-200.0, 0.0), 1000.0);
|
||||||
|
await tester.pumpUntilNoTransientCallbacks(_frameDuration);
|
||||||
|
|
||||||
|
expect(find.text('Alabama'), findsNothing);
|
||||||
|
expect(find.text('Alaska'), findsNothing);
|
||||||
|
expect(find.text('Arizona'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.fling(find.byType(PageView), const Offset(200.0, 0.0), 1000.0);
|
||||||
|
await tester.pumpUntilNoTransientCallbacks(_frameDuration);
|
||||||
|
|
||||||
|
expect(find.text('Alabama'), findsNothing);
|
||||||
|
expect(find.text('Alaska'), findsOneWidget);
|
||||||
|
expect(find.text('Arizona'), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
@ -78,7 +78,7 @@ class TestScrollBehavior extends ScrollBehavior2 {
|
|||||||
Widget wrap(BuildContext context, Widget child, AxisDirection axisDirection) => child;
|
Widget wrap(BuildContext context, Widget child, AxisDirection axisDirection) => child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
|
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition, ScrollPhysics physics) {
|
||||||
return new TestScrollPosition(extentMultiplier, state, ViewportScrollBehavior.defaultScrollTolerances, oldPosition);
|
return new TestScrollPosition(extentMultiplier, state, ViewportScrollBehavior.defaultScrollTolerances, oldPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ class TestBehavior extends ScrollBehavior2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
|
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition, ScrollPhysics physics) {
|
||||||
return new TestViewportScrollPosition(
|
return new TestViewportScrollPosition(
|
||||||
state,
|
state,
|
||||||
new Tolerance(velocity: 20.0, distance: 1.0),
|
new Tolerance(velocity: 20.0, distance: 1.0),
|
||||||
oldPosition,
|
oldPosition,
|
||||||
const ClampingScrollPhysics(),
|
physics,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +80,7 @@ void main() {
|
|||||||
axisDirection: AxisDirection.down,
|
axisDirection: AxisDirection.down,
|
||||||
center: centerKey,
|
center: centerKey,
|
||||||
anchor: 0.25,
|
anchor: 0.25,
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
scrollBehavior: new TestBehavior(),
|
scrollBehavior: new TestBehavior(),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
new SliverToBoxAdapter(child: new Container(height: 5.0)),
|
new SliverToBoxAdapter(child: new Container(height: 5.0)),
|
||||||
|
@ -63,6 +63,7 @@ class TestScrollable extends StatelessWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.initialScrollOffset: 0.0,
|
this.initialScrollOffset: 0.0,
|
||||||
this.axisDirection: AxisDirection.down,
|
this.axisDirection: AxisDirection.down,
|
||||||
|
this.physics,
|
||||||
this.anchor: 0.0,
|
this.anchor: 0.0,
|
||||||
this.center,
|
this.center,
|
||||||
this.scrollBehavior,
|
this.scrollBehavior,
|
||||||
@ -75,6 +76,8 @@ class TestScrollable extends StatelessWidget {
|
|||||||
|
|
||||||
final AxisDirection axisDirection;
|
final AxisDirection axisDirection;
|
||||||
|
|
||||||
|
final ScrollPhysics physics;
|
||||||
|
|
||||||
final double anchor;
|
final double anchor;
|
||||||
|
|
||||||
final Key center;
|
final Key center;
|
||||||
@ -90,6 +93,7 @@ class TestScrollable extends StatelessWidget {
|
|||||||
return new Scrollable2(
|
return new Scrollable2(
|
||||||
initialScrollOffset: initialScrollOffset,
|
initialScrollOffset: initialScrollOffset,
|
||||||
axisDirection: axisDirection,
|
axisDirection: axisDirection,
|
||||||
|
physics: physics,
|
||||||
scrollBehavior: scrollBehavior,
|
scrollBehavior: scrollBehavior,
|
||||||
viewportBuilder: (BuildContext context, ViewportOffset offset) {
|
viewportBuilder: (BuildContext context, ViewportOffset offset) {
|
||||||
return new Viewport2(
|
return new Viewport2(
|
||||||
@ -102,4 +106,4 @@ class TestScrollable extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user