/// Widget: Promotion Slider /// /// Horizontal scrolling list of promotional banners. /// Displays promotion images, titles, and descriptions. library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:worker/core/router/app_router.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/home/domain/entities/promotion.dart'; /// Promotion Slider Widget /// /// Displays a horizontal scrollable list of promotion cards. /// Each card shows an image, title, and brief description. class PromotionSlider extends StatelessWidget { const PromotionSlider({ super.key, required this.promotions, this.onPromotionTap, }); /// List of promotions to display final List promotions; /// Callback when a promotion is tapped final void Function(Promotion promotion)? onPromotionTap; @override Widget build(BuildContext context) { if (promotions.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.only(bottom: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text( 'Chương trình ưu đãi', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Color(0xFF212121), // --text-dark ), ), ), const SizedBox(height: 12), SizedBox( height: 210, // 140px image + 54px text area child: ListView.builder( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: promotions.length, itemBuilder: (context, index) { return _PromotionCard( promotion: promotions[index], onTap: () { if (onPromotionTap != null) { onPromotionTap!(promotions[index]); } else { // Navigate to promotion detail page context.pushNamed( RouteNames.promotionDetail, extra: promotions[index], ); } }, ); }, ), ), ], ), ); } } /// Individual Promotion Card class _PromotionCard extends StatelessWidget { const _PromotionCard({required this.promotion, this.onTap}); final Promotion promotion; final VoidCallback? onTap; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( width: 280, margin: const EdgeInsets.only(right: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Promotion Image ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), child: CachedNetworkImage( imageUrl: promotion.imageUrl, height: 140, width: 280, fit: BoxFit.cover, placeholder: (context, url) => Container( height: 140, color: AppColors.grey100, child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 140, color: AppColors.grey100, child: const Icon( Icons.image_not_supported, size: 48, color: AppColors.grey500, ), ), ), ), // Promotion Info Container( padding: const EdgeInsets.all(12), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical( bottom: Radius.circular(12), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( promotion.title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF212121), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( promotion.description, style: const TextStyle( fontSize: 12, color: Color(0xFF666666), // --text-muted ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), ); } }