Untitled

mail@pastecode.io avatar
unknown
plain_text
3 years ago
4.7 kB
3
Indexable
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),
            ),
          )
        ],
      ),
    );
  }
}