Skip to content

Instantly share code, notes, and snippets.

@riscait
Last active July 31, 2024 08:14
Show Gist options
  • Save riscait/0faacafc83c8544605eca8e0f95ec659 to your computer and use it in GitHub Desktop.
Save riscait/0faacafc83c8544605eca8e0f95ec659 to your computer and use it in GitHub Desktop.
FavoriteButton with Animations
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FavoriteButton with Animations',
home: const MyHomePage(title: 'FavoriteButton with Animations'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
super.key,
required this.title,
});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FavoriteButton(
onPressed: (){},
),
),
);
}
}
class FavoriteButton extends StatefulWidget {
const FavoriteButton({
required this.onPressed,
this.backgroundSize = 80,
this.iconSize = 26,
this.backgroundColor = const Color(0xFF303030),
this.splashColor = const Color(0xFFE24459),
this.iconColor = const Color(0xFFFFFFFF),
this.bounceDuration = const Duration(milliseconds: 600),
this.fillDuration = const Duration(milliseconds: 400),
this.isLarge = false,
super.key,
});
final VoidCallback? onPressed;
final Color backgroundColor;
final Color splashColor;
final Color iconColor;
final double backgroundSize;
final double iconSize;
final bool isLarge;
final Duration bounceDuration;
final Duration fillDuration;
@override
State<FavoriteButton> createState() => _FavoriteButtonState();
}
class _FavoriteButtonState extends State<FavoriteButton>
with SingleTickerProviderStateMixin {
bool _isFavorite = false;
late AnimationController _iconController;
late Animation<double> _iconWidthAnimation;
late Animation<double> _iconHeightAnimation;
@override
void initState() {
super.initState();
_iconController = AnimationController(
duration: widget.bounceDuration,
vsync: this,
);
_iconWidthAnimation = TweenSequence<double>([
TweenSequenceItem(tween: Tween(begin: 1, end: 0.85), weight: 1),
TweenSequenceItem(tween: Tween(begin: 0.85, end: 1.15), weight: 1),
TweenSequenceItem(tween: Tween(begin: 1.15, end: 0.85), weight: 1),
TweenSequenceItem(tween: Tween(begin: 0.85, end: 1), weight: 1),
]).animate(
CurvedAnimation(
parent: _iconController,
curve: Curves.ease,
),
);
_iconHeightAnimation = TweenSequence<double>([
TweenSequenceItem(tween: Tween(begin: 1, end: 1.15), weight: 1),
TweenSequenceItem(tween: Tween(begin: 1.15, end: 0.85), weight: 1),
TweenSequenceItem(tween: Tween(begin: 0.85, end: 1.15), weight: 1),
TweenSequenceItem(tween: Tween(begin: 1.15, end: 1), weight: 1),
]).animate(
CurvedAnimation(
parent: _iconController,
curve: Curves.ease,
),
);
}
@override
void dispose() {
_iconController.dispose();
super.dispose();
}
void _onTap() {
widget.onPressed?.call();
_toggleFavorite();
}
void _toggleFavorite() {
setState(() {
_isFavorite = !_isFavorite;
if (_isFavorite) {
_iconController.forward(from: 0);
} else {
_iconController.reverse();
}
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _onTap,
child: AnimatedScale(
duration: const Duration(milliseconds: 300),
scale: widget.isLarge ? 1.5 : 1.0,
child: Stack(
alignment: Alignment.center,
children: [
// Grey background.
Container(
width: widget.backgroundSize,
height: widget.backgroundSize,
decoration: ShapeDecoration(
shape: const OvalBorder(),
color: widget.backgroundColor,
),
),
// Pink background.
AnimatedScale(
scale: _isFavorite ? 1.0 : 0,
duration: widget.fillDuration,
curve: Curves.ease,
child: Container(
width: widget.backgroundSize,
height: widget.backgroundSize,
decoration: ShapeDecoration(
shape: const OvalBorder(),
color: widget.splashColor,
),
),
),
// Heart icon.
AnimatedBuilder(
animation: _iconController,
child: Icon(
_isFavorite ? Icons.favorite : Icons.favorite_border,
color: Colors.white,
size: widget.iconSize,
),
builder: (context, child) {
return Transform(
transform: Matrix4.identity()
..scale(
_iconWidthAnimation.value,
_iconHeightAnimation.value,
),
alignment: Alignment.center,
child: child,
);
},
),
],
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment