|
// Interactive Demo: https://dartpad.dev/b1a15c09bbb8d18c4caa9e8c41a108c0?null_safety=true |
|
// GIF Demo: See first comment below |
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
void main() { |
|
runApp(DemoApp()); |
|
} |
|
|
|
class DemoApp extends StatelessWidget { |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return MaterialApp( |
|
home: Scaffold( |
|
body: Center( |
|
child: Container( |
|
height: 48, |
|
width: 200, |
|
child: CoreButton(text: "Tap Me", onTap: () {}), |
|
), |
|
), |
|
), |
|
); |
|
} |
|
} |
|
|
|
class CoreButton extends StatefulWidget { |
|
final String text; |
|
final VoidCallback? onTap; |
|
|
|
const CoreButton({required this.text, this.onTap}); |
|
|
|
@override |
|
_CoreButtonState createState() => _CoreButtonState(); |
|
} |
|
|
|
class _CoreButtonState extends State<CoreButton> |
|
with SingleTickerProviderStateMixin { |
|
static const clickAnimationDurationMillis = 100; |
|
|
|
double _scaleTransformValue = 1; |
|
|
|
// needed for the "click" tap effect |
|
late final AnimationController animationController; |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
animationController = AnimationController( |
|
vsync: this, |
|
duration: const Duration(milliseconds: clickAnimationDurationMillis), |
|
lowerBound: 0.0, |
|
upperBound: 0.05, |
|
)..addListener(() { |
|
setState(() => _scaleTransformValue = 1 - animationController.value); |
|
}); |
|
} |
|
|
|
@override |
|
void dispose() { |
|
animationController.dispose(); |
|
super.dispose(); |
|
} |
|
|
|
void _shrinkButtonSize() { |
|
animationController.forward(); |
|
} |
|
|
|
void _restoreButtonSize() { |
|
Future.delayed( |
|
const Duration(milliseconds: clickAnimationDurationMillis), |
|
() => animationController.reverse(), |
|
); |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return GestureDetector( |
|
onTap: () { |
|
widget.onTap?.call(); |
|
_shrinkButtonSize(); |
|
_restoreButtonSize(); |
|
}, |
|
onTapDown: (_) => _shrinkButtonSize(), |
|
onTapCancel: _restoreButtonSize, |
|
child: Transform.scale( |
|
scale: _scaleTransformValue, |
|
child: Container( |
|
decoration: BoxDecoration( |
|
color: const Color(0xFF37003C), |
|
borderRadius: BorderRadius.circular(8), |
|
), |
|
child: Center( |
|
child: Text( |
|
widget.text, |
|
style: TextStyle( |
|
color: Colors.white, |
|
fontSize: 14, |
|
fontWeight: FontWeight.w700, |
|
), |
|
), |
|
), |
|
), |
|
), |
|
); |
|
} |
|
} |
Make it better: