Add max offset listener

This commit is contained in:
sherlock 2023-08-04 14:11:32 +07:00
parent 976427f9df
commit a6ae48da8b

View file

@ -19,6 +19,8 @@ typedef SwipeDirectionCallback = void Function(SwipeDirection direction);
/// Used by [Swipeable.confirmSwipe]. /// Used by [Swipeable.confirmSwipe].
typedef ConfirmSwipeCallback = Future<bool> Function(SwipeDirection direction); typedef ConfirmSwipeCallback = Future<bool> Function(SwipeDirection direction);
typedef OnOverScrollTheMaxOffset = void Function();
/// The direction in which a [Swipeable] can be swiped. /// The direction in which a [Swipeable] can be swiped.
enum SwipeDirection { enum SwipeDirection {
/// The [Swipeable] can be swiped by dragging either left or right. /// The [Swipeable] can be swiped by dragging either left or right.
@ -58,11 +60,13 @@ class Swipeable extends StatefulWidget {
this.movementDuration = const Duration(milliseconds: 200), this.movementDuration = const Duration(milliseconds: 200),
this.crossAxisEndOffset = 0.0, this.crossAxisEndOffset = 0.0,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.swipeIntensity = 1.0,
this.allowedPointerKinds = const { this.allowedPointerKinds = const {
PointerDeviceKind.invertedStylus, PointerDeviceKind.invertedStylus,
PointerDeviceKind.stylus, PointerDeviceKind.stylus,
PointerDeviceKind.touch PointerDeviceKind.touch
}, },
this.onOverScrollTheMaxOffset,
}) : assert(secondaryBackground == null || background != null), }) : assert(secondaryBackground == null || background != null),
super(key: key); super(key: key);
@ -154,6 +158,13 @@ class Swipeable extends StatefulWidget {
/// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors. /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors.
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
///If the swipeIntensity is low, the message requires a stronger swipe.
///The default value is set to 1. Consider increasing the swipeIntensity to make it easier for users to swipe.
final double swipeIntensity;
/// When the user scrolls beyond the maximum offset, the function will be invoked, and it will only be called once.
final OnOverScrollTheMaxOffset? onOverScrollTheMaxOffset;
@override @override
_SwipeableState createState() => _SwipeableState(); _SwipeableState createState() => _SwipeableState();
} }
@ -191,6 +202,19 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
_moveController = AnimationController(duration: widget.movementDuration, vsync: this) _moveController = AnimationController(duration: widget.movementDuration, vsync: this)
..addStatusListener(_handleDismissStatusChanged); ..addStatusListener(_handleDismissStatusChanged);
_updateMoveAnimation(); _updateMoveAnimation();
_moveController.addListener(() {
if (!isInSwipe) {
isSwipeAnimationRunning = true;
}
if (isSwipeAnimationRunning && _moveController.value >= widget.maxOffset) {
if (widget.onOverScrollTheMaxOffset != null) {
widget.onOverScrollTheMaxOffset!();
}
isSwipeAnimationRunning = false;
}
});
super.initState(); super.initState();
} }
@ -199,6 +223,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
late Animation<Offset> _moveAnimation; late Animation<Offset> _moveAnimation;
double _dragExtent = 0.0; double _dragExtent = 0.0;
bool isSwipeAnimationRunning = false;
bool _dragUnderway = false; bool _dragUnderway = false;
Size? _sizePriorToCollapse; Size? _sizePriorToCollapse;
@ -207,6 +232,8 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
@override @override
bool get wantKeepAlive => _moveController.isAnimating == true; bool get wantKeepAlive => _moveController.isAnimating == true;
bool get isInSwipe => _moveController.value != 0;
@override @override
void dispose() { void dispose() {
_moveController.dispose(); _moveController.dispose();
@ -261,7 +288,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
return; return;
} }
final delta = details.primaryDelta ?? 0.0; final delta = (details.primaryDelta ?? 0.0) * widget.swipeIntensity;
final oldDragExtent = _dragExtent; final oldDragExtent = _dragExtent;
switch (widget.direction) { switch (widget.direction) {
case SwipeDirection.none: case SwipeDirection.none:
@ -351,7 +378,7 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
return; return;
} }
_dragUnderway = false; _dragUnderway = false;
if (_moveController.isCompleted && await _confirmStartSwipeAnimation() == true) { if (_moveController.value >= widget.maxOffset && await _confirmStartSwipeAnimation() == true) {
_startSwipeAnimation(); _startSwipeAnimation();
return; return;
} }
@ -375,13 +402,8 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
break; break;
case _FlingGestureKind.none: case _FlingGestureKind.none:
if (!_moveController.isDismissed) { if (!_moveController.isDismissed) {
// we already know it's not completed, we check that above
if (_moveController.value > (widget.dismissThresholds[_swipeDirection] ?? _kDismissThreshold)) {
await _moveController.forward();
} else {
await _moveController.reverse(); await _moveController.reverse();
} }
}
break; break;
} }
} }
@ -406,7 +428,6 @@ class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin, Au
} }
void _startSwipeAnimation() { void _startSwipeAnimation() {
assert(_moveController.isCompleted);
assert(_sizePriorToCollapse == null); assert(_sizePriorToCollapse == null);
final direction = _swipeDirection; final direction = _swipeDirection;