forked from Nexus/nexus
parent
1380233f2e
commit
2f17f5b6c6
6 changed files with 116 additions and 17 deletions
58
lib/widgets/swipe_to_reply.dart
Normal file
58
lib/widgets/swipe_to_reply.dart
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import "package:flutter/services.dart";
|
||||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
|
||||
class const SwipeToReply(
|
||||
final Widget child, {
|
||||
required final VoidCallback? onSwipe,
|
||||
super.key,
|
||||
}) extends HookWidget {
|
||||
static const maxDrag = 64.0;
|
||||
static const threshold = 0.75;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller = useAnimationController(duration: Durations.short4);
|
||||
useListenable(controller);
|
||||
|
||||
if (onSwipe == null) return child;
|
||||
|
||||
final progress = controller.value;
|
||||
|
||||
return GestureDetector(
|
||||
onHorizontalDragUpdate: (details) {
|
||||
final previous = controller.value;
|
||||
controller.value = previous + details.primaryDelta! / maxDrag;
|
||||
|
||||
if (previous < threshold && controller.value >= threshold) {
|
||||
HapticFeedback.lightImpact();
|
||||
}
|
||||
},
|
||||
onHorizontalDragEnd: (_) {
|
||||
if (controller.value >= threshold) onSwipe!();
|
||||
controller.animateBack(0, curve: Curves.easeOut);
|
||||
},
|
||||
onHorizontalDragCancel: () =>
|
||||
controller.animateBack(0, curve: Curves.easeOut),
|
||||
child: Stack(
|
||||
alignment: .centerLeft,
|
||||
children: [
|
||||
Positioned(
|
||||
left: 16,
|
||||
child: Opacity(
|
||||
opacity: (progress / threshold).clamp(0, 1),
|
||||
child: Transform.scale(
|
||||
scale: progress >= threshold ? 1 : 0.75,
|
||||
child: const Icon(Icons.reply),
|
||||
),
|
||||
),
|
||||
),
|
||||
Transform.translate(
|
||||
offset: .new(progress * maxDrag, 0),
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue