Commit 0f15263c authored by Hans Muller's avatar Hans Muller Committed by GitHub

RefreshIndicator dismiss transition, remain visible during refresh, etc (#4844)

parent a33fc496
...@@ -70,7 +70,8 @@ class OverscrollDemoState extends State<OverscrollDemo> { ...@@ -70,7 +70,8 @@ class OverscrollDemoState extends State<OverscrollDemo> {
body = new RefreshIndicator( body = new RefreshIndicator(
child: body, child: body,
refresh: refresh, refresh: refresh,
scrollableKey: _scrollableKey scrollableKey: _scrollableKey,
location: RefreshIndicatorLocation.top
); );
break; break;
} }
......
...@@ -57,6 +57,11 @@ enum _RefreshIndicatorMode { ...@@ -57,6 +57,11 @@ enum _RefreshIndicatorMode {
dismiss // Animating the indicator's fade-out. dismiss // Animating the indicator's fade-out.
} }
enum _DismissTransition {
shrink, // Refresh callback completed, scale the indicator to 0.
slide // No refresh, translate the indicator out of view.
}
/// A widget that supports the Material "swipe to refresh" idiom. /// A widget that supports the Material "swipe to refresh" idiom.
/// ///
/// When the child's vertical Scrollable descendant overscrolls, an /// When the child's vertical Scrollable descendant overscrolls, an
...@@ -127,10 +132,7 @@ class _RefreshIndicatorState extends State<RefreshIndicator> { ...@@ -127,10 +132,7 @@ class _RefreshIndicatorState extends State<RefreshIndicator> {
Animation<double> _value; Animation<double> _value;
Animation<Color> _valueColor; Animation<Color> _valueColor;
double _scrollOffset; double _dragOffset;
double _containerExtent;
double _minScrollOffset;
double _maxScrollOffset;
bool _isIndicatorAtTop = true; bool _isIndicatorAtTop = true;
_RefreshIndicatorMode _mode; _RefreshIndicatorMode _mode;
Future<Null> _pendingRefreshFuture; Future<Null> _pendingRefreshFuture;
...@@ -169,54 +171,42 @@ class _RefreshIndicatorState extends State<RefreshIndicator> { ...@@ -169,54 +171,42 @@ class _RefreshIndicatorState extends State<RefreshIndicator> {
super.dispose(); super.dispose();
} }
void _updateState(ScrollableState scrollable) { bool _isValidScrollable(ScrollableState scrollable) {
if (scrollable == null)
return false;
final Axis axis = scrollable.config.scrollDirection; final Axis axis = scrollable.config.scrollDirection;
if (axis != Axis.vertical || scrollable.scrollBehavior is! ExtentScrollBehavior) return axis == Axis.vertical && scrollable.scrollBehavior is ExtentScrollBehavior;
return;
final ExtentScrollBehavior scrollBehavior = scrollable.scrollBehavior;
_scrollOffset = scrollable.scrollOffset;
_containerExtent = scrollBehavior.containerExtent;
_minScrollOffset = scrollBehavior.minScrollOffset;
_maxScrollOffset = scrollBehavior.maxScrollOffset;
} }
void _handlePointerDown(PointerDownEvent event) { bool _isScrolledToLimit(ScrollableState scrollable) {
final ScrollableState scrollable = config.scrollableKey?.currentState; final double minScrollOffset = scrollable.scrollBehavior.minScrollOffset;
if (scrollable == null) final double maxScrollOffset = scrollable.scrollBehavior.maxScrollOffset;
return; final double scrollOffset = scrollable.scrollOffset;
switch (config.location) {
_updateState(scrollable); case RefreshIndicatorLocation.top:
_scaleController.value = 0.0; return scrollOffset <= minScrollOffset;
_sizeController.value = 0.0; case RefreshIndicatorLocation.bottom:
setState(() { return scrollOffset >= maxScrollOffset;
_mode = _RefreshIndicatorMode.drag; case RefreshIndicatorLocation.both:
}); return scrollOffset <= minScrollOffset || scrollOffset >= maxScrollOffset;
}
return false;
} }
double _overscrollDistance() { double _overscrollDistance(ScrollableState scrollable) {
final ScrollableState scrollable = config.scrollableKey?.currentState; final double minScrollOffset = scrollable.scrollBehavior.minScrollOffset;
if (scrollable == null) final double maxScrollOffset = scrollable.scrollBehavior.maxScrollOffset;
return 0.0; final double scrollOffset = scrollable.scrollOffset;
final double oldOffset = _scrollOffset;
final double newOffset = scrollable.scrollOffset;
_updateState(scrollable);
if ((newOffset - oldOffset).abs() < kPixelScrollTolerance.distance)
return 0.0;
switch (config.location) { switch (config.location) {
case RefreshIndicatorLocation.top: case RefreshIndicatorLocation.top:
return newOffset < _minScrollOffset ? _minScrollOffset - newOffset : 0.0; return scrollOffset <= minScrollOffset ? -_dragOffset : 0.0;
case RefreshIndicatorLocation.bottom: case RefreshIndicatorLocation.bottom:
return newOffset > _maxScrollOffset ? newOffset - _maxScrollOffset : 0.0; return scrollOffset >= maxScrollOffset ? _dragOffset : 0.0;
case RefreshIndicatorLocation.both: { case RefreshIndicatorLocation.both: {
if (newOffset < _minScrollOffset) if (scrollOffset <= minScrollOffset)
return _minScrollOffset - newOffset; return -_dragOffset;
else if (newOffset > _maxScrollOffset) else if (scrollOffset >= maxScrollOffset)
return newOffset - _maxScrollOffset; return _dragOffset;
else else
return 0.0; return 0.0;
} }
...@@ -224,13 +214,42 @@ class _RefreshIndicatorState extends State<RefreshIndicator> { ...@@ -224,13 +214,42 @@ class _RefreshIndicatorState extends State<RefreshIndicator> {
return 0.0; return 0.0;
} }
void _handlePointerDown(PointerDownEvent event) {
if (_mode != null)
return;
final ScrollableState scrollable = config.scrollableKey.currentState;
if (!_isValidScrollable(scrollable) || !_isScrolledToLimit(scrollable))
return;
_dragOffset = 0.0;
_scaleController.value = 0.0;
_sizeController.value = 0.0;
setState(() {
_mode = _RefreshIndicatorMode.drag;
});
}
void _handlePointerMove(PointerMoveEvent event) { void _handlePointerMove(PointerMoveEvent event) {
final double overscroll = _overscrollDistance(); if (_mode != _RefreshIndicatorMode.drag && _mode != _RefreshIndicatorMode.armed)
return;
final ScrollableState scrollable = config.scrollableKey?.currentState;
if (!_isValidScrollable(scrollable))
return;
final double dragOffsetDelta = scrollable.pixelOffsetToScrollOffset(event.delta.dy);
_dragOffset += dragOffsetDelta / 2.0;
if (_dragOffset.abs() < kPixelScrollTolerance.distance)
return;
final double containerExtent = scrollable.scrollBehavior.containerExtent;
final double overscroll = _overscrollDistance(scrollable);
if (overscroll > 0.0) { if (overscroll > 0.0) {
final double newValue = overscroll / (_containerExtent * _kDragContainerExtentPercentage); final double newValue = overscroll / (containerExtent * _kDragContainerExtentPercentage);
_sizeController.value = newValue.clamp(0.0, 1.0); _sizeController.value = newValue.clamp(0.0, 1.0);
final bool newIsAtTop = _scrollOffset < _minScrollOffset; final bool newIsAtTop = _dragOffset < 0;
if (_isIndicatorAtTop != newIsAtTop) { if (_isIndicatorAtTop != newIsAtTop) {
setState(() { setState(() {
_isIndicatorAtTop = newIsAtTop; _isIndicatorAtTop = newIsAtTop;
...@@ -242,11 +261,18 @@ class _RefreshIndicatorState extends State<RefreshIndicator> { ...@@ -242,11 +261,18 @@ class _RefreshIndicatorState extends State<RefreshIndicator> {
} }
// Stop showing the refresh indicator // Stop showing the refresh indicator
Future<Null> _dismiss() async { Future<Null> _dismiss(_DismissTransition transition) async {
setState(() { setState(() {
_mode = _RefreshIndicatorMode.dismiss; _mode = _RefreshIndicatorMode.dismiss;
}); });
await _scaleController.animateTo(1.0, duration: _kIndicatorScaleDuration); switch(transition) {
case _DismissTransition.shrink:
await _sizeController.animateTo(0.0, duration: _kIndicatorScaleDuration);
break;
case _DismissTransition.slide:
await _scaleController.animateTo(1.0, duration: _kIndicatorScaleDuration);
break;
}
if (mounted && _mode == _RefreshIndicatorMode.dismiss) { if (mounted && _mode == _RefreshIndicatorMode.dismiss) {
setState(() { setState(() {
_mode = null; _mode = null;
...@@ -273,10 +299,10 @@ class _RefreshIndicatorState extends State<RefreshIndicator> { ...@@ -273,10 +299,10 @@ class _RefreshIndicatorState extends State<RefreshIndicator> {
_pendingRefreshFuture = null; _pendingRefreshFuture = null;
if (mounted && completed && _mode == _RefreshIndicatorMode.refresh) if (mounted && completed && _mode == _RefreshIndicatorMode.refresh)
_dismiss(); _dismiss(_DismissTransition.slide);
} }
} else if (_mode == _RefreshIndicatorMode.drag) { } else if (_mode == _RefreshIndicatorMode.drag) {
_dismiss(); _dismiss(_DismissTransition.shrink);
} }
} }
......
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