diff --git a/packages/flutter/lib/src/material/overscroll_indicator.dart b/packages/flutter/lib/src/material/overscroll_indicator.dart index 1d312ba9de..08a0b2e920 100644 --- a/packages/flutter/lib/src/material/overscroll_indicator.dart +++ b/packages/flutter/lib/src/material/overscroll_indicator.dart @@ -13,7 +13,7 @@ const double _kMaxIndicatorExtent = 64.0; const double _kMinIndicatorOpacity = 0.0; const double _kMaxIndicatorOpacity = 0.25; const Duration _kIndicatorHideDuration = const Duration(milliseconds: 200); -const Duration _kIndicatorTimeoutDuration = const Duration(seconds: 1); +const Duration _kIndicatorTimeoutDuration = const Duration(milliseconds: 500); final Tween _kIndicatorOpacity = new Tween(begin: 0.0, end: 0.3); class _Painter extends CustomPainter { @@ -132,7 +132,10 @@ class _OverscrollIndicatorState extends State { void _hide() { _hideTimer?.cancel(); _hideTimer = null; - _extentAnimation.reverse(); + // Gaurding _hide() being called while indicator is already animating. + if (!_extentAnimation.isAnimating) { + _extentAnimation.reverse(); + } } void _updateState(ScrollableState scrollable) { @@ -151,12 +154,15 @@ class _OverscrollIndicatorState extends State { void _onScrollUpdated(ScrollableState scrollable) { final double value = scrollable.scrollOffset; - if ((value < _minScrollOffset || value > _maxScrollOffset) && - ((value - _scrollOffset).abs() > kPixelScrollTolerance.distance)) { - _hideTimer?.cancel(); - _hideTimer = new Timer(_kIndicatorTimeoutDuration, _hide); - // Changing the animation's value causes an implicit setState(). - _extentAnimation.value = value < _minScrollOffset ? _minScrollOffset - value : value - _maxScrollOffset; + if (_isOverscroll(value)) { + _refreshHideTimer(); + // Hide the indicator as soon as user starts scrolling in the reverse direction of overscroll. + if (_isReverseScroll(value)) { + _hide(); + } else { + // Changing the animation's value causes an implicit setState(). + _extentAnimation.value = value < _minScrollOffset ? _minScrollOffset - value : value - _maxScrollOffset; + } } _updateState(scrollable); } @@ -166,6 +172,21 @@ class _OverscrollIndicatorState extends State { _hide(); } + void _refreshHideTimer() { + _hideTimer?.cancel(); + _hideTimer = new Timer(_kIndicatorTimeoutDuration, _hide); + } + + bool _isOverscroll(double scrollOffset) { + return (scrollOffset < _minScrollOffset || scrollOffset > _maxScrollOffset) && + ((scrollOffset - _scrollOffset).abs() > kPixelScrollTolerance.distance); + } + + bool _isReverseScroll(double scrollOffset) { + final double delta = _scrollOffset - scrollOffset; + return scrollOffset < _minScrollOffset ? delta < 0 : delta > 0; + } + bool _handleScrollNotification(ScrollNotification notification) { if (config.scrollableKey == null || config.scrollableKey == notification.scrollable.config.key) { final ScrollableState scrollable = notification.scrollable;