Untitled
unknown
plain_text
2 years ago
4.7 kB
3
Indexable
Never
import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:meeyteam/shared/utils/app_utils.dart'; class Swipeable extends StatefulWidget { final Widget child; final VoidCallback? onSwipeStart; final VoidCallback? onSwipeLeft; final VoidCallback? onSwipeRight; final VoidCallback? onSwipeCancel; final VoidCallback? onSwipeEnd; final double threshold; const Swipeable({ required this.child, this.onSwipeStart, this.onSwipeLeft, this.onSwipeRight, this.onSwipeCancel, this.onSwipeEnd, this.threshold = 64.0, }); @override State<StatefulWidget> createState() { return _SwipeableState(); } } class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin { double _dragExtent = 0.0; late AnimationController _moveController; late Animation<Offset> _moveAnimation; bool _pastLeftThreshold = false; bool _pastRightThreshold = false; final controllerValue = 0.0; bool showIcon = false; @override void initState() { super.initState(); _moveController = AnimationController( duration: const Duration(milliseconds: 200), vsync: this); _moveAnimation = Tween<Offset>(begin: Offset.zero, end: const Offset(1.0, 0.0)) .animate(_moveController); _moveController.animateTo(controllerValue); } @override void dispose() { _moveController.dispose(); super.dispose(); } void _handleDragStart(DragStartDetails details) { if (widget.onSwipeStart != null) { widget.onSwipeStart!(); } } void _handleDragUpdate(DragUpdateDetails details) { final delta = details.primaryDelta; final oldDragExtent = _dragExtent; _dragExtent += delta ?? 0; if (_dragExtent > 0) return; if (oldDragExtent.sign != _dragExtent.sign) { setState(() { _updateMoveAnimation(); }); } final movePastThresholdPixels = widget.threshold; var newPos = _dragExtent.abs() / (context.size?.width ?? 1); // print(_dragExtent.abs()); if (_dragExtent.abs() > movePastThresholdPixels) { // how many "thresholds" past the threshold we are. 1 = the threshold 2 // = two thresholds. final n = _dragExtent.abs() / movePastThresholdPixels; // Take the number of thresholds past the threshold, and reduce this // number final reducedThreshold = math.pow(n, 0.3); final adjustedPixelPos = movePastThresholdPixels * reducedThreshold; newPos = adjustedPixelPos / (context.size?.width ?? 1); if (_dragExtent < 0 && _dragExtent.abs() > 32 && !showIcon) { haptic(); setState(() { showIcon = true; }); } // if (_dragExtent > 0 && !_pastLeftThreshold) { // _pastLeftThreshold = true; // // if (widget.onSwipeRight != null) { // widget.onSwipeRight!(); // } // } if (_dragExtent < 0 && !_pastRightThreshold) { _pastRightThreshold = true; if (widget.onSwipeLeft != null) { widget.onSwipeLeft!(); } } } else { setState(() { showIcon = false; }); // Send a cancel event if the user has swiped back underneath the // threshold if (_pastLeftThreshold || _pastRightThreshold) { if (widget.onSwipeCancel != null) { widget.onSwipeCancel!(); } } _pastLeftThreshold = false; _pastRightThreshold = false; } _moveController.value = newPos; } void _handleDragEnd(DragEndDetails details) { _moveController.animateTo(0.0, duration: const Duration(milliseconds: 200)); _dragExtent = 0.0; if (widget.onSwipeEnd != null && showIcon) { widget.onSwipeEnd!(); setState(() { showIcon = false; }); } } void _updateMoveAnimation() { final end = _dragExtent.sign; _moveAnimation = Tween<Offset>(begin: const Offset(0.0, 0.0), end: Offset(end, 0.0)) .animate(_moveController); } @override Widget build(BuildContext context) { return GestureDetector( onHorizontalDragStart: _handleDragStart, onHorizontalDragUpdate: _handleDragUpdate, onHorizontalDragEnd: _handleDragEnd, behavior: HitTestBehavior.opaque, child: Stack( children: [ SlideTransition( position: _moveAnimation, child: widget.child, ), Align( alignment: Alignment.centerRight, child: Opacity( opacity: showIcon ? 1 : 0, child: const Icon(Icons.reply), ), ) ], ), ); } }