Commit 4c3018f7 authored by Hans Muller's avatar Hans Muller Committed by GitHub

A tap is not a scroll (#5038)

parent b30959d7
...@@ -132,7 +132,6 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> { ...@@ -132,7 +132,6 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
void _hide() { void _hide() {
_hideTimer?.cancel(); _hideTimer?.cancel();
_hideTimer = null; _hideTimer = null;
// Gaurding _hide() being called while indicator is already animating.
if (!_extentAnimation.isAnimating) { if (!_extentAnimation.isAnimating) {
_extentAnimation.reverse(); _extentAnimation.reverse();
} }
...@@ -188,9 +187,13 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> { ...@@ -188,9 +187,13 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
} }
bool _handleScrollNotification(ScrollNotification notification) { bool _handleScrollNotification(ScrollNotification notification) {
if (config.scrollableKey == null) {
if (notification.depth != 0) if (notification.depth != 0)
return false; return false;
if (config.scrollableKey == null || config.scrollableKey == notification.scrollable.config.key) { } else if (config.scrollableKey != notification.scrollable.config.key) {
return false;
}
final ScrollableState scrollable = notification.scrollable; final ScrollableState scrollable = notification.scrollable;
switch(notification.kind) { switch(notification.kind) {
case ScrollNotificationKind.started: case ScrollNotificationKind.started:
...@@ -203,7 +206,6 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> { ...@@ -203,7 +206,6 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
_onScrollEnded(scrollable); _onScrollEnded(scrollable);
break; break;
} }
}
return false; return false;
} }
......
...@@ -98,6 +98,7 @@ class Scrollbar extends StatefulWidget { ...@@ -98,6 +98,7 @@ class Scrollbar extends StatefulWidget {
class _ScrollbarState extends State<Scrollbar> { class _ScrollbarState extends State<Scrollbar> {
final AnimationController _fade = new AnimationController(duration: _kScrollbarThumbFadeDuration); final AnimationController _fade = new AnimationController(duration: _kScrollbarThumbFadeDuration);
CurvedAnimation _opacity; CurvedAnimation _opacity;
double _scrollOffsetAnchor;
double _scrollOffset; double _scrollOffset;
Axis _scrollDirection; Axis _scrollDirection;
double _containerExtent; double _containerExtent;
...@@ -109,6 +110,12 @@ class _ScrollbarState extends State<Scrollbar> { ...@@ -109,6 +110,12 @@ class _ScrollbarState extends State<Scrollbar> {
_opacity = new CurvedAnimation(parent: _fade, curve: Curves.ease); _opacity = new CurvedAnimation(parent: _fade, curve: Curves.ease);
} }
@override
void dispose() {
_fade.stop();
super.dispose();
}
void _updateState(ScrollableState scrollable) { void _updateState(ScrollableState scrollable) {
if (scrollable.scrollBehavior is! ExtentScrollBehavior) if (scrollable.scrollBehavior is! ExtentScrollBehavior)
return; return;
...@@ -121,14 +128,20 @@ class _ScrollbarState extends State<Scrollbar> { ...@@ -121,14 +128,20 @@ class _ScrollbarState extends State<Scrollbar> {
void _onScrollStarted(ScrollableState scrollable) { void _onScrollStarted(ScrollableState scrollable) {
_updateState(scrollable); _updateState(scrollable);
_fade.forward(); _scrollOffsetAnchor = _scrollOffset;
} }
void _onScrollUpdated(ScrollableState scrollable) { void _onScrollUpdated(ScrollableState scrollable) {
setState(() {
_updateState(scrollable); _updateState(scrollable);
if (!_fade.isAnimating) {
if (_scrollOffsetAnchor != _scrollOffset && _fade.value == 0.0)
_fade.forward(); // Lazily start the scrollbar fade-in.
setState(() {
// If the scrollbar has faded in, rebuild it per the new scrollable state.
// If the fade-in is underway this setState() will have no effect.
}); });
} }
}
void _onScrollEnded(ScrollableState scrollable) { void _onScrollEnded(ScrollableState scrollable) {
_updateState(scrollable); _updateState(scrollable);
...@@ -136,7 +149,13 @@ class _ScrollbarState extends State<Scrollbar> { ...@@ -136,7 +149,13 @@ class _ScrollbarState extends State<Scrollbar> {
} }
bool _handleScrollNotification(ScrollNotification notification) { bool _handleScrollNotification(ScrollNotification notification) {
if (config.scrollableKey == null || config.scrollableKey == notification.scrollable.config.key) { if (config.scrollableKey == null) {
if (notification.depth != 0)
return false;
} else if (config.scrollableKey != notification.scrollable.config.key) {
return false;
}
final ScrollableState scrollable = notification.scrollable; final ScrollableState scrollable = notification.scrollable;
switch(notification.kind) { switch(notification.kind) {
case ScrollNotificationKind.started: case ScrollNotificationKind.started:
...@@ -149,7 +168,6 @@ class _ScrollbarState extends State<Scrollbar> { ...@@ -149,7 +168,6 @@ class _ScrollbarState extends State<Scrollbar> {
_onScrollEnded(scrollable); _onScrollEnded(scrollable);
break; break;
} }
}
return false; return false;
} }
......
...@@ -7,7 +7,6 @@ import 'dart:math' as math; ...@@ -7,7 +7,6 @@ import 'dart:math' as math;
import 'package:flutter/physics.dart'; import 'package:flutter/physics.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
const double _kSecondsPerMillisecond = 1000.0;
const double _kScrollDrag = 0.025; const double _kScrollDrag = 0.025;
// TODO(hansmuller): Simplify these classes. We're no longer using the ScrollBehavior<T, U> // TODO(hansmuller): Simplify these classes. We're no longer using the ScrollBehavior<T, U>
...@@ -151,14 +150,12 @@ class BoundedBehavior extends ExtentScrollBehavior { ...@@ -151,14 +150,12 @@ class BoundedBehavior extends ExtentScrollBehavior {
} }
Simulation _createScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) { Simulation _createScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) {
final double startVelocity = velocity * _kSecondsPerMillisecond;
final SpringDescription spring = new SpringDescription.withDampingRatio(mass: 1.0, springConstant: 170.0, ratio: 1.1); final SpringDescription spring = new SpringDescription.withDampingRatio(mass: 1.0, springConstant: 170.0, ratio: 1.1);
return new ScrollSimulation(position, startVelocity, minScrollOffset, maxScrollOffset, spring, _kScrollDrag); return new ScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset, spring, _kScrollDrag);
} }
Simulation _createSnapScrollSimulation(double startOffset, double endOffset, double startVelocity, double endVelocity) { Simulation _createSnapScrollSimulation(double startOffset, double endOffset, double startVelocity, double endVelocity) {
final double velocity = startVelocity * _kSecondsPerMillisecond; return new FrictionSimulation.through(startOffset, endOffset, startVelocity, endVelocity);
return new FrictionSimulation.through(startOffset, endOffset, velocity, endVelocity);
} }
/// A scroll behavior that does not prevent the user from exeeding scroll bounds. /// A scroll behavior that does not prevent the user from exeeding scroll bounds.
...@@ -169,9 +166,8 @@ class UnboundedBehavior extends ExtentScrollBehavior { ...@@ -169,9 +166,8 @@ class UnboundedBehavior extends ExtentScrollBehavior {
@override @override
Simulation createScrollSimulation(double position, double velocity) { Simulation createScrollSimulation(double position, double velocity) {
double velocityPerSecond = velocity * 1000.0;
return new BoundedFrictionSimulation( return new BoundedFrictionSimulation(
_kScrollDrag, position, velocityPerSecond, double.NEGATIVE_INFINITY, double.INFINITY _kScrollDrag, position, velocity, double.NEGATIVE_INFINITY, double.INFINITY
); );
} }
......
...@@ -430,8 +430,7 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -430,8 +430,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
if (_numberOfInProgressScrolls > 0) { if (_numberOfInProgressScrolls > 0) {
if (_simulation != null) { if (_simulation != null) {
double dx = _simulation.dx(_controller.lastElapsedDuration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND); double dx = _simulation.dx(_controller.lastElapsedDuration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND);
// TODO(abarth): We should be consistent about the units we use for velocity (i.e., per second). _startToEndAnimation(dx); // dx - logical pixels / second
_startToEndAnimation(dx / Duration.MILLISECONDS_PER_SECOND);
} }
return; return;
} }
...@@ -456,14 +455,15 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -456,14 +455,15 @@ class ScrollableState<T extends Scrollable> extends State<T> {
updateGestureDetector(); updateGestureDetector();
} }
/// Fling the scroll offset with the given velocity. /// Fling the scroll offset with the given velocity in logical pixels/second.
/// ///
/// Calling this function starts a physics-based animation of the scroll /// Calling this function starts a physics-based animation of the scroll
/// offset with the given value as the initial velocity. The physics /// offset with the given value as the initial velocity. The physics
/// simulation used is determined by the scroll behavior. /// simulation is determined by the scroll behavior.
Future<Null> fling(double scrollVelocity) { Future<Null> fling(double scrollVelocity) {
if (scrollVelocity != 0.0 || !_controller.isAnimating) if (scrollVelocity.abs() > kPixelScrollTolerance.velocity || !_controller.isAnimating)
return _startToEndAnimation(scrollVelocity); return _startToEndAnimation(scrollVelocity);
return new Future<Null>.value(); return new Future<Null>.value();
} }
...@@ -587,9 +587,9 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -587,9 +587,9 @@ class ScrollableState<T extends Scrollable> extends State<T> {
scrollBy(pixelOffsetToScrollOffset(details.primaryDelta)); scrollBy(pixelOffsetToScrollOffset(details.primaryDelta));
} }
Future<Null> _handleDragEnd(DragEndDetails details) { void _handleDragEnd(DragEndDetails details) {
double scrollVelocity = pixelDeltaToScrollOffset(details.velocity.pixelsPerSecond) / Duration.MILLISECONDS_PER_SECOND; final double scrollVelocity = pixelDeltaToScrollOffset(details.velocity.pixelsPerSecond);
return fling(scrollVelocity).then(_endScroll); fling(scrollVelocity).then(_endScroll);
} }
Null _endScroll([Null _]) { Null _endScroll([Null _]) {
......
...@@ -51,7 +51,7 @@ Future<Null> fling(double velocity) { ...@@ -51,7 +51,7 @@ Future<Null> fling(double velocity) {
} }
void main() { void main() {
testWidgets('ScrollableList snap scrolling, fling(0.8)', (WidgetTester tester) async { testWidgets('ScrollableList snap scrolling', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame()); await tester.pumpWidget(buildFrame());
scrollOffset = 0.0; scrollOffset = 0.0;
...@@ -60,7 +60,7 @@ void main() { ...@@ -60,7 +60,7 @@ void main() {
Duration dt = const Duration(seconds: 2); Duration dt = const Duration(seconds: 2);
fling(1.0); fling(1000.0);
await tester.pump(); // Start the scheduler at 0.0 await tester.pump(); // Start the scheduler at 0.0
await tester.pump(dt); await tester.pump(dt);
expect(scrollOffset, closeTo(200.0, 1.0)); expect(scrollOffset, closeTo(200.0, 1.0));
...@@ -69,7 +69,7 @@ void main() { ...@@ -69,7 +69,7 @@ void main() {
await tester.pump(); await tester.pump();
expect(scrollOffset, 0.0); expect(scrollOffset, 0.0);
fling(2.0); fling(2000.0);
await tester.pump(); await tester.pump();
await tester.pump(dt); await tester.pump(dt);
expect(scrollOffset, closeTo(400.0, 1.0)); expect(scrollOffset, closeTo(400.0, 1.0));
...@@ -78,7 +78,7 @@ void main() { ...@@ -78,7 +78,7 @@ void main() {
await tester.pump(); await tester.pump();
expect(scrollOffset, 400.0); expect(scrollOffset, 400.0);
fling(-0.8); fling(-800.0);
await tester.pump(); await tester.pump();
await tester.pump(dt); await tester.pump(dt);
expect(scrollOffset, closeTo(0.0, 1.0)); expect(scrollOffset, closeTo(0.0, 1.0));
...@@ -87,7 +87,7 @@ void main() { ...@@ -87,7 +87,7 @@ void main() {
await tester.pump(); await tester.pump();
expect(scrollOffset, 800.0); expect(scrollOffset, 800.0);
fling(-2.0); fling(-2000.0);
await tester.pump(); await tester.pump();
await tester.pump(dt); await tester.pump(dt);
expect(scrollOffset, closeTo(200.0, 1.0)); expect(scrollOffset, closeTo(200.0, 1.0));
...@@ -97,7 +97,7 @@ void main() { ...@@ -97,7 +97,7 @@ void main() {
expect(scrollOffset, 800.0); expect(scrollOffset, 800.0);
bool completed = false; bool completed = false;
fling(-2.0).then((_) { fling(-2000.0).then((_) {
completed = true; completed = true;
expectSync(scrollOffset, closeTo(200.0, 1.0)); expectSync(scrollOffset, closeTo(200.0, 1.0));
}); });
......
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