bounceable 1.0.0
bounceable: ^1.0.0 copied to clipboard
A Flutter widget that provides a smooth, customizable bounce animation effect on tap. Features iOS-like Cupertino motion physics for natural, delightful interactions.
import 'package:flutter/material.dart';
import 'package:bounceable/bounceable.dart';
void main() {
runApp(const BounceableExampleApp());
}
class BounceableExampleApp extends StatelessWidget {
const BounceableExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bounceable Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const ExampleScreen(),
);
}
}
class ExampleScreen extends StatefulWidget {
const ExampleScreen({super.key});
@override
State<ExampleScreen> createState() => _ExampleScreenState();
}
class _ExampleScreenState extends State<ExampleScreen> {
int _tapCount = 0;
int _longPressCount = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bounceable Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Stats display
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
'$_tapCount',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const Text('Taps'),
],
),
Column(
children: [
Text(
'$_longPressCount',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const Text('Long Presses'),
],
),
],
),
),
const SizedBox(height: 32),
// Basic usage - Grow effect (default)
const Text(
'Basic Usage (Grow Effect)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Center(
child: Bounceable(
onTap: () {
setState(() => _tapCount++);
_showSnackBar(context, 'Basic tap!');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Text(
'Tap Me',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 32),
// Shrink effect
const Text(
'Shrink Effect (scaleFactorOnTap: 0.95)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Center(
child: Bounceable(
scaleFactorOnTap: 0.95,
onTap: () {
setState(() => _tapCount++);
_showSnackBar(context, 'Shrink effect!');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.orange.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Text(
'Shrink on Tap',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 32),
// Long press support
const Text(
'Long Press Support',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Center(
child: Bounceable(
onTap: () {
setState(() => _tapCount++);
_showSnackBar(context, 'Tapped!');
},
onLongPress: () {
setState(() => _longPressCount++);
_showSnackBar(context, 'Long pressed! (with haptic)');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.green.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Text(
'Tap or Long Press',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 32),
// Disabled haptic feedback
const Text(
'No Haptic Feedback',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Center(
child: Bounceable(
enableHapticFeedback: false,
onLongPress: () {
setState(() => _longPressCount++);
_showSnackBar(context, 'Long pressed! (no haptic)');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.purple.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Text(
'Long Press (Silent)',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 32),
// Custom scale factor
const Text(
'Custom Scale (1.15 = 15% larger)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Center(
child: Bounceable(
scaleFactorOnTap: 1.15,
onTap: () {
setState(() => _tapCount++);
_showSnackBar(context, 'Big bounce!');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.red.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Text(
'Big Bounce',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 32),
// Card example
const Text(
'Card Example',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Bounceable(
onTap: () {
setState(() => _tapCount++);
_showSnackBar(context, 'Card tapped!');
},
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.touch_app,
size: 32,
color: Colors.blue,
),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Interactive Card',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 4),
Text(
'Tap this card to see the bounce effect',
style: TextStyle(color: Colors.grey),
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
),
),
const SizedBox(height: 48),
],
),
),
);
}
void _showSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 1),
behavior: SnackBarBehavior.floating,
),
);
}
}