Files
stream-chat-flutter/lib/src/swipeable.dart
T
Sahil Kumar 328afc44d8 merge fixes
Signed-off-by: Sahil Kumar <[email protected]>
2020-12-31 18:53:33 +05:30

164 lines
4.7 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'stream_chat_theme.dart';
///
class Swipeable extends StatefulWidget {
final Widget child;
final Widget backgroundIcon;
final VoidCallback onSwipeStart;
final VoidCallback onSwipeCancel;
final VoidCallback onSwipeEnd;
final double threshold;
///
const Swipeable({
@required this.child,
@required this.backgroundIcon,
this.onSwipeStart,
this.onSwipeCancel,
this.onSwipeEnd,
this.threshold = 82.0,
});
@override
State<StatefulWidget> createState() => _SwipeableState();
}
class _SwipeableState extends State<Swipeable> with TickerProviderStateMixin {
double _dragExtent = 0.0;
AnimationController _moveController;
AnimationController _iconMoveController;
Animation<Offset> _moveAnimation;
Animation<Offset> _iconTransitionAnimation;
Animation<double> _iconFadeAnimation;
bool _pastThreshold = false;
final _animationDuration = const Duration(milliseconds: 200);
@override
void initState() {
super.initState();
_moveController =
AnimationController(duration: _animationDuration, vsync: this);
_iconMoveController =
AnimationController(duration: _animationDuration, vsync: this);
_moveAnimation = Tween<Offset>(begin: Offset.zero, end: Offset(1.0, 0.0))
.animate(_moveController);
_iconTransitionAnimation =
Tween<Offset>(begin: Offset(-0.1, 0.0), end: Offset(0.4, 0.0))
.animate(_moveController);
_iconFadeAnimation =
Tween<double>(begin: 0.7, end: 1.0).animate(_iconMoveController);
final controllerValue = 0.0;
_moveController.animateTo(controllerValue);
_iconMoveController.animateTo(controllerValue);
}
@override
void dispose() {
_moveController.dispose();
_iconMoveController.dispose();
super.dispose();
}
void _handleDragStart(DragStartDetails details) {
if (widget.onSwipeStart != null) {
widget.onSwipeStart();
}
}
void _handleDragUpdate(DragUpdateDetails details) {
final delta = details.primaryDelta;
_dragExtent += delta;
if (_dragExtent.isNegative) return;
var movePastThresholdPixels = widget.threshold;
var newPos = _dragExtent.abs() / context.size.width;
if (_dragExtent.abs() > movePastThresholdPixels) {
// how many "thresholds" past the threshold we are. 1 = the threshold 2
// = two thresholds.
var n = _dragExtent.abs() / movePastThresholdPixels;
// Take the number of thresholds past the threshold, and reduce this
// number
var reducedThreshold = math.pow(n, 0.3);
var adjustedPixelPos = movePastThresholdPixels * reducedThreshold;
newPos = adjustedPixelPos / context.size.width;
if (_dragExtent > 0 && !_pastThreshold) {
_iconMoveController.value = 1;
_pastThreshold = true;
}
} else {
// Send a cancel event if the user has swiped back underneath the
// threshold
if (_pastThreshold && widget.onSwipeCancel != null) {
widget.onSwipeCancel();
}
_pastThreshold = false;
}
if (!_pastThreshold || newPos < _moveController.value) {
_iconMoveController.value = newPos;
}
_moveController.value = newPos;
}
void _handleDragEnd(DragEndDetails details) {
_moveController.animateTo(0.0, duration: _animationDuration);
_iconMoveController.animateTo(0.0, duration: _animationDuration);
_dragExtent = 0.0;
if (_pastThreshold && widget.onSwipeEnd != null) {
widget.onSwipeEnd();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: _handleDragStart,
onHorizontalDragUpdate: _handleDragUpdate,
onHorizontalDragEnd: _handleDragEnd,
behavior: HitTestBehavior.opaque,
child: Stack(
alignment: Alignment.center,
fit: StackFit.passthrough,
children: [
SlideTransition(
position: _iconTransitionAnimation,
child: Row(
children: [
FadeTransition(
opacity: _iconFadeAnimation,
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: StreamChatTheme.of(context)
.colorTheme
.greyGainsboro,
),
),
child: widget.backgroundIcon,
),
),
],
),
),
SlideTransition(
position: _moveAnimation,
child: widget.child,
),
],
),
);
}
}