import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:vibration/vibration.dart'; import '../../constant/box_name.dart'; import '../../constant/colors.dart'; import '../../constant/style.dart'; import '../../main.dart'; class MyElevatedButton extends StatefulWidget { final String title; final VoidCallback onPressed; final Color kolor; final int vibrateDuration; final IconData? icon; final bool isLoading; final double? height; final double? fontSize; const MyElevatedButton({ Key? key, required this.title, required this.onPressed, this.kolor = AppColor.primaryColor, this.vibrateDuration = 50, // Shorter = crisper feedback this.icon, this.isLoading = false, this.height = 52, this.fontSize, }) : super(key: key); @override State createState() => _MyElevatedButtonState(); } class _MyElevatedButtonState extends State with SingleTickerProviderStateMixin { late final AnimationController _pressController; bool _isVibrateEnabled = true; @override void initState() { super.initState(); _pressController = AnimationController( vsync: this, duration: const Duration(milliseconds: 120), reverseDuration: const Duration(milliseconds: 180), ); _loadVibratePreference(); } Future _loadVibratePreference() async { // Reactive to preference changes if needed later setState(() { _isVibrateEnabled = box.read(BoxName.isvibrate) ?? true; }); } @override void dispose() { _pressController.dispose(); super.dispose(); } void _triggerHaptic() { if (!_isVibrateEnabled) return; // Unified approach: HapticFeedback works well on both platforms if (Platform.isIOS) { HapticFeedback.lightImpact(); } else if (Platform.isAndroid) { // Try native haptic first, fallback to Vibration package if needed HapticFeedback.mediumImpact(); // Optional stronger feedback: // Vibration.vibrate(duration: widget.vibrateDuration); } } void _handlePress() { if (widget.isLoading) return; _triggerHaptic(); _pressController.forward().then((_) => _pressController.reverse()); // Small delay ensures animation starts before callback Future.delayed(const Duration(milliseconds: 80), widget.onPressed); } @override Widget build(BuildContext context) { final isEnabled = !widget.isLoading && widget.onPressed != () {}; return AnimatedBuilder( animation: _pressController, builder: (context, child) { final scale = 1.0 - (_pressController.value * 0.03); return Transform.scale( scale: scale, child: Container( height: widget.height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), boxShadow: isEnabled ? [ BoxShadow( color: widget.kolor.withOpacity(0.25), blurRadius: 12, offset: const Offset(0, 4), ), ] : null, ), child: ElevatedButton( style: ButtonStyle( backgroundColor: WidgetStateProperty.resolveWith( (states) { if (!isEnabled) return widget.kolor.withOpacity(0.5); if (states.contains(WidgetState.pressed)) { return widget.kolor.withOpacity(0.92); } return widget.kolor; }, ), elevation: WidgetStateProperty.resolveWith( (states) => isEnabled ? (states.contains(WidgetState.pressed) ? 2 : 6) : 0, ), shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), padding: WidgetStateProperty.all( const EdgeInsets.symmetric(horizontal: 24, vertical: 14), ), ), onPressed: isEnabled ? _handlePress : null, child: _buildContent(), ), ), ); }, ); } Widget _buildContent() { if (widget.isLoading) { return SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation(AppColor.secondaryColor), ), ); } final textStyle = AppStyle.title.copyWith( color: AppColor.secondaryColor, fontSize: widget.fontSize, fontWeight: FontWeight.w600, letterSpacing: 0.3, ); if (widget.icon != null) { return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(widget.icon, size: 18, color: AppColor.secondaryColor), const SizedBox(width: 8), Text(widget.title, style: textStyle, textAlign: TextAlign.center), ], ); } return Text(widget.title, style: textStyle, textAlign: TextAlign.center); } }