import 'package:flutter/material.dart'; import 'dart:io'; import 'package:youmazgestion/Models/produit.dart'; class ProductCard extends StatefulWidget { final Product product; final void Function(Product, int) onAddToCart; const ProductCard({ Key? key, required this.product, required this.onAddToCart, }) : super(key: key); @override State createState() => _ProductCardState(); } class _ProductCardState extends State with TickerProviderStateMixin { int selectedQuantity = 1; late AnimationController _scaleController; late AnimationController _fadeController; late Animation _scaleAnimation; late Animation _fadeAnimation; @override void initState() { super.initState(); // Animations pour les interactions _scaleController = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); _fadeController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, )..forward(); _scaleAnimation = Tween( begin: 1.0, end: 0.95, ).animate(CurvedAnimation( parent: _scaleController, curve: Curves.easeInOut, )); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _fadeController, curve: Curves.easeOut, )); } @override void dispose() { _scaleController.dispose(); _fadeController.dispose(); super.dispose(); } void _onTapDown(TapDownDetails details) { _scaleController.forward(); } void _onTapUp(TapUpDetails details) { _scaleController.reverse(); } void _onTapCancel() { _scaleController.reverse(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _fadeAnimation, child: AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( margin: const EdgeInsets.all(8), height: 280, child: Material( elevation: 8, shadowColor: Colors.black.withOpacity(0.2), borderRadius: BorderRadius.circular(20), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.white, Colors.grey.shade50, ], ), ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Stack( children: [ Positioned.fill( child: Container( decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(20), ), child: widget.product.image != null ? Image.file( File(widget.product.image!), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return _buildPlaceholderImage(); }, ) : _buildPlaceholderImage(), ), ), Positioned.fill( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.transparent, Colors.black.withOpacity(0.3), Colors.black.withOpacity(0.7), ], stops: const [0.0, 0.4, 0.7, 1.0], ), ), ), ), if (widget.product.isStockDefined()) Positioned( top: 12, right: 12, child: Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: Colors.green.withOpacity(0.9), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.green.withOpacity(0.3), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.check_circle, color: Colors.white, size: 12, ), const SizedBox(width: 4), const Text( 'En stock', style: TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ], ), ), ), Positioned( left: 0, right: 0, bottom: 0, child: Container( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.product.name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, color: Colors.white, shadows: [ Shadow( offset: Offset(1, 1), blurRadius: 3, color: Colors.black54, ), ], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '${widget.product.price.toStringAsFixed(2)} MGA', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 13, color: Colors.white, shadows: [ Shadow( offset: Offset(1, 1), blurRadius: 3, color: Colors.black54, ), ], ), ), ], ), ), const SizedBox(height: 12), Row( children: [ Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.95), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildQuantityButton( icon: Icons.remove, onPressed: selectedQuantity > 1 ? () { setState(() { selectedQuantity--; }); } : null, ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), child: Text( '$selectedQuantity', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, ), ), ), _buildQuantityButton( icon: Icons.add, onPressed: selectedQuantity < 100 ? () { setState(() { selectedQuantity++; }); } : null, ), ], ), ), const SizedBox(width: 8), Expanded( child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTapDown: _onTapDown, onTapUp: _onTapUp, onTapCancel: _onTapCancel, onTap: () { widget.onAddToCart(widget.product, selectedQuantity); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon( Icons.shopping_cart, color: Colors.white, ), const SizedBox(width: 8), Expanded( child: Text( '${widget.product.name} (x$selectedQuantity) ajouté au panier', overflow: TextOverflow.ellipsis, ), ), ], ), backgroundColor: Colors.green, duration: const Duration(seconds: 1), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); }, child: Container( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 12, ), decoration: BoxDecoration( gradient: const LinearGradient( colors: [ Color.fromARGB(255, 4, 54, 95), Color.fromARGB(255, 6, 80, 140), ], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: const Color.fromARGB(255, 4, 54, 95).withOpacity(0.3), blurRadius: 6, offset: const Offset(0, 3), ), ], ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.add_shopping_cart, color: Colors.white, size: 16, ), const SizedBox(width: 6), const Flexible( child: Text( 'Ajouter', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12, ), overflow: TextOverflow.ellipsis, ), ), ], ), ), ), ), ), ], ), ], ), ), ), ], ), ), ), ), ), ); }, ), ); } Widget _buildPlaceholderImage() { return Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(20), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.image_outlined, size: 40, color: Colors.grey.shade400, ), const SizedBox(height: 8), Text( 'Image non disponible', style: TextStyle( color: Colors.grey.shade500, fontSize: 12, ), ), ], ), ); } Widget _buildQuantityButton({ required IconData icon, required VoidCallback? onPressed, }) { return Material( color: Colors.transparent, child: InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(15), child: Container( padding: const EdgeInsets.all(6), child: Icon( icon, size: 16, color: onPressed != null ? const Color.fromARGB(255, 4, 54, 95) : Colors.grey, ), ), ), ); } }