SButton
A highly customizable Flutter button widget with rich animations, visual effects, and interaction modes. Perfect for creating engaging user interfaces with splash effects, bounce animations, haptic feedback, and more.
Demo

π Features
-
Rich Animations
- Bounce animation with configurable scale
- Splash effects with custom colors and opacity
- Smooth color transitions and overlays
- Delayed initialization support
-
Multiple Interaction Modes
- Single tap with offset detection
- Double tap support
- Long press with start/end callbacks
- Customizable hit test behavior
-
Visual Customization
- Circle or rectangle button shapes
- Custom border radius
- Splashcolor and selected state overlays
- Loading state with customizable loading widget
- Error state handling with error builder
-
Enhanced User Feedback
- Bubble label tooltips (platform-aware)
- Haptic feedback (multiple feedback types)
- Tooltip messages
- Active/inactive state management
-
Advanced Features
- Comprehensive state management
- Custom animation controls
- Flexible child widget support
- Error handling and recovery
- Built with Material Design principles
π¦ Installation
Add this to your pubspec.yaml:
dependencies:
s_button: ^1.1.0
Then run:
flutter pub get
π Quick Start
Basic Usage
import 'package:s_button/s_button.dart';
SButton(
onTap: (offset) {
print('Button tapped at: $offset');
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'Tap Me',
style: TextStyle(color: Colors.white),
),
),
)
With Splash Effect
SButton(
onTap: (offset) => print('Tapped'),
splashColor: Colors.blue.withOpacity(0.3),
splashOpacity: 0.5,
shouldBounce: true,
bounceScale: 0.95,
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Click Me'),
),
)
Circle Button
SButton(
isCircleButton: true,
onTap: (offset) => print('Circular button tapped'),
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.green,
),
child: const Icon(Icons.add, color: Colors.white),
),
)
π Comprehensive Usage
Advanced Interaction Handling
SButton(
onTap: (offset) {
print('Single tap at: ${offset.dx}, ${offset.dy}');
},
onDoubleTap: (offset) {
print('Double tapped at: $offset');
},
onLongPressStart: (details) {
print('Long press started');
},
onLongPressEnd: (details) {
print('Long press ended');
},
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Multiple Interactions'),
),
)
With Bubble Label (Tooltip)
SButton(
onTap: (offset) => print('Tapped'),
bubbleLabelContent: BubbleLabelContent(
child: const Text('This is a helpful tooltip!'),
shouldActivateOnLongPressOnAllPlatforms: false,
),
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Icon(Icons.info),
),
)
Loading State
SButton(
isLoading: isLoading,
onTap: (offset) async {
// Handle loading state
},
loadingWidget: const CircularProgressIndicator(),
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Loading...'),
),
)
With Error Handling
SButton(
onTap: (offset) {
try {
// Perform operation
} catch (e) {
widget.onError?.call(e);
}
},
onError: (error) {
print('Error occurred: $error');
},
errorBuilder: (context, error) {
return Container(
padding: const EdgeInsets.all(8.0),
color: Colors.red,
child: Text('Error: $error'),
);
},
child: const Text('Operation'),
)
Haptic Feedback
SButton(
onTap: (offset) => print('Tapped with feedback'),
enableHapticFeedback: true,
hapticFeedbackType: HapticFeedbackType.mediumImpact,
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Haptic Button'),
),
)
Full Example with All Features
SButton(
// Styling
splashColor: Colors.blue,
splashOpacity: 0.4,
selectedColor: Colors.blue.withOpacity(0.2),
borderRadius: BorderRadius.circular(12), // Clips child and applies to splash/overlay
// Animations
shouldBounce: true,
bounceScale: 0.97,
delay: const Duration(milliseconds: 200),
// Interactions
onTap: (offset) => _handleTap(offset),
onDoubleTap: (offset) => _handleDoubleTap(offset),
onLongPressStart: (details) => _handleLongPressStart(details),
onLongPressEnd: (details) => _handleLongPressEnd(details),
// States
isActive: true,
isLoading: isLoading,
isCircleButton: false,
// Feedback
enableHapticFeedback: true,
hapticFeedbackType: HapticFeedbackType.lightImpact,
tooltipMessage: 'Press to perform action',
bubbleLabelContent: BubbleLabelContent(
child: const Text('Long press for more info'),
),
// Error handling
onError: (error) => _handleError(error),
errorBuilder: (context, error) => _buildErrorWidget(context, error),
// Custom loading widget
loadingWidget: const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
),
// Behavior
hitTestBehavior: HitTestBehavior.opaque,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'Advanced Button',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
)
π¨ Customization
All Available Properties
SButton(
// Required
required Widget child,
// Shape & Border Radius
BorderRadius? borderRadius, // Clips child and applies to splash/selected overlay
bool isCircleButton = false,
AlignmentGeometry? alignment,
// Splash Effects
Color? splashColor,
double? splashOpacity,
// Animation
bool shouldBounce = true,
double bounceScale = 0.98,
Duration? delay,
// Interactions
void Function(Offset)? onTap,
void Function(Offset)? onDoubleTap,
void Function(LongPressStartDetails)? onLongPressStart,
void Function(LongPressEndDetails)? onLongPressEnd,
// State
bool isActive = true,
bool isLoading = false,
Color? selectedColor,
// Feedback
bool enableHapticFeedback = true,
HapticFeedbackType hapticFeedbackType = HapticFeedbackType.lightImpact,
String? tooltipMessage,
BubbleLabelContent? bubbleLabelContent,
// Loading & Error
Widget? loadingWidget,
Function(Object)? onError,
Widget Function(BuildContext, Object)? errorBuilder,
// Behavior
HitTestBehavior hitTestBehavior = HitTestBehavior.opaque,
bool ignoreChildWidgetOnTap = false,
)
πΌοΈ Examples
Check the example/ folder for a complete Flutter application demonstrating:
- Basic button usage
- Advanced interactions
- Custom styling
- Loading and error states
- Multiple button variations
π§ͺ Testing
The package includes comprehensive unit tests. Run tests with:
flutter test
π± Platform Support
- β iOS
- β Android
- β Web
- β macOS
- β Windows
- β Linux
π Dependencies
flutter_animate- For advanced animationssoundsliced_tween_animation_builder- Custom tween animationsbubble_label- Bubble label tooltipsticker_free_circular_progress_indicator- Loading indicatorss_ink_button- Underlying ink button effectss_disabled- Disabled state managementsoundsliced_dart_extensions- Utility extensions
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
π Support
For issues, questions, or suggestions:
- GitHub Issues: s_button issues
- Email: contact@soundsliced.com
π― Roadmap
Web-specific optimizationsAdditional haptic feedback patternsMore bubble label customization optionsAnimation preset libraryAccessibility improvements
Made with β€οΈ by SoundSliced