91 lines
2.1 KiB
Dart
91 lines
2.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
class BounceTap extends StatefulWidget {
|
|
final Widget child;
|
|
final VoidCallback? onTap;
|
|
final HitTestBehavior behavior;
|
|
final Duration pressDuration;
|
|
final Duration releaseDuration;
|
|
|
|
const BounceTap({
|
|
super.key,
|
|
required this.child,
|
|
this.onTap,
|
|
this.behavior = HitTestBehavior.opaque,
|
|
this.pressDuration = const Duration(milliseconds: 80),
|
|
this.releaseDuration = const Duration(milliseconds: 120),
|
|
});
|
|
|
|
@override
|
|
State<BounceTap> createState() => _BounceTapState();
|
|
}
|
|
|
|
class _BounceTapState extends State<BounceTap>
|
|
with SingleTickerProviderStateMixin {
|
|
late final AnimationController _controller;
|
|
late final Animation<double> _scale;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
vsync: this,
|
|
value: 1,
|
|
lowerBound: 0.94,
|
|
upperBound: 1,
|
|
duration: widget.pressDuration,
|
|
reverseDuration: widget.releaseDuration,
|
|
);
|
|
_scale = CurvedAnimation(
|
|
parent: _controller,
|
|
curve: Curves.easeOut,
|
|
reverseCurve: Curves.elasticOut,
|
|
);
|
|
}
|
|
|
|
void _press() {
|
|
if (widget.onTap == null) return;
|
|
_controller.animateTo(
|
|
0.94,
|
|
duration: widget.pressDuration,
|
|
curve: Curves.easeOut,
|
|
);
|
|
}
|
|
|
|
void _release({bool triggerTap = false}) {
|
|
if (widget.onTap == null) return;
|
|
_controller.animateTo(
|
|
1,
|
|
duration: widget.releaseDuration,
|
|
curve: Curves.elasticOut,
|
|
);
|
|
if (triggerTap) {
|
|
widget.onTap?.call();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Listener(
|
|
behavior: widget.behavior,
|
|
onPointerDown: (_) => _press(),
|
|
onPointerCancel: (_) => _release(),
|
|
onPointerUp: (_) => _release(triggerTap: true),
|
|
child: AnimatedBuilder(
|
|
animation: _scale,
|
|
child: widget.child,
|
|
builder: (context, child) => Transform.scale(
|
|
scale: _scale.value,
|
|
child: child,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|