login-page/lib/widgets/product_card.dart
2025-05-17 11:13:16 +03:00

182 lines
5.3 KiB
Dart

import 'package:flutter/material.dart';
import '../models/product_model.dart';
import '../services/cart_service.dart'; // Added
/// An improved ProductCard widget that works with the enhanced theme system
/// - More efficient layout with better organization
/// - Uses semantic theme properties for consistent styling
/// - Responsive design with flexible sizing
class ProductCard extends StatelessWidget {
final Product product;
// final VoidCallback? onAddToCart; // Removed
const ProductCard({
super.key,
required this.product,
// this.onAddToCart, // Removed
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildProductImage(context),
Expanded(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: _buildProductDetails(context),
),
),
],
),
);
}
Widget _buildProductImage(BuildContext context) {
return AspectRatio(
aspectRatio: 3 / 2, // Adjusted for potentially taller cards. Original 16/10 or 3/2
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
product.imageUrl,
fit: BoxFit.cover,
loadingBuilder: _imageLoadingBuilder,
errorBuilder: _imageErrorBuilder,
),
Positioned(
top: 8,
right: 8,
child: _buildCategoryBadge(context),
),
],
),
);
}
Widget _buildCategoryBadge(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: colorScheme.secondaryContainer.withOpacity(0.85),
borderRadius: BorderRadius.circular(4),
),
child: Text(
product.category,
style: theme.textTheme.labelSmall?.copyWith(
color: Color(0xFFFFFFFF),
fontWeight: FontWeight.w500,
),
),
);
}
Widget _buildProductDetails(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: Theme.of(context).textTheme.titleMedium,
maxLines: 1, // Changed to 1 for consistency, can be 2 if design allows
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
Text(
product.description,
style: Theme.of(context).textTheme.bodySmall,
maxLines: 2, // Can be adjusted
overflow: TextOverflow.ellipsis,
),
const Spacer(),
const SizedBox(height: 14),
_buildPriceAndAction(context),
],
);
}
Widget _buildPriceAndAction(BuildContext context) {
final theme = Theme.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Ensures spacing
children: [
Text(
'\$${product.price.toStringAsFixed(2)}',
style: theme.textTheme.titleMedium?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
// const Spacer(), // Removed as MainAxisAlignment.spaceBetween handles it
_buildAddToCartButton(context),
],
);
}
Widget _buildAddToCartButton(BuildContext context) {
return IconButton(
onPressed: () => _handleAddToCart(context),
icon: const Icon(Icons.add_shopping_cart_outlined),
style: IconButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.1),
foregroundColor: Theme.of(context).colorScheme.primary,
),
tooltip: 'Add to cart',
);
}
void _handleAddToCart(BuildContext context) {
final cartService = CartService(); // Get singleton instance
cartService.addItem(product);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${product.name} added to cart'),
duration: const Duration(seconds: 2),
action: SnackBarAction(
label: 'VIEW CART',
onPressed: () {
Navigator.pushNamed(context, '/cart');
},
),
),
);
}
Widget _imageLoadingBuilder(
BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
strokeWidth: 2.0,
color: Theme.of(context).colorScheme.primary,
),
);
}
Widget _imageErrorBuilder(
BuildContext context, Object exception, StackTrace? stackTrace) {
final theme = Theme.of(context);
return Container(
color: theme.colorScheme.surfaceVariant, // Or surface.withOpacity(0.1)
child: Center(
child: Icon(
Icons.broken_image_outlined,
size: 40,
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.6),
),
),
);
}
}