// lib/presentation/widgets/reserved_list_item_card.dart import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:proxibuy/core/gen/assets.gen.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:proxibuy/core/config/app_colors.dart'; import 'package:proxibuy/data/models/offer_model.dart'; import 'package:slide_countdown/slide_countdown.dart'; class ReservedListItemCard extends StatefulWidget { final OfferModel offer; const ReservedListItemCard({super.key, required this.offer}); @override State createState() => _ReservedListItemCardState(); } class _ReservedListItemCardState extends State { bool _isExpanded = false; Timer? _timer; Duration _remaining = Duration.zero; @override void initState() { super.initState(); _calculateRemainingTime(); _timer = Timer.periodic(const Duration(seconds: 1), (_) { _calculateRemainingTime(); }); } void _calculateRemainingTime() { final now = DateTime.now(); if (widget.offer.expiryTime.isAfter(now)) { if (mounted) { setState(() { _remaining = widget.offer.expiryTime.difference(now); }); } } else { if (mounted) { setState(() => _remaining = Duration.zero); } _timer?.cancel(); } } @override void dispose() { _timer?.cancel(); super.dispose(); } // ignore: unused_element String _formatDuration(Duration duration) { if (duration.inSeconds <= 0) return "پایان یافته"; final hours = duration.inHours.toString().padLeft(2, '0'); final minutes = (duration.inMinutes % 60).toString().padLeft(2, '0'); final seconds = (duration.inSeconds % 60).toString().padLeft(2, '0'); return '$hours:$minutes:$seconds'; } @override Widget build(BuildContext context) { // ویجت اصلی به Column تغییر کرده است return Column( children: [ // بخش اول: کارت اصلی که فقط شامل اطلاعات محصول است Card( color: Colors.white, shape: RoundedRectangleBorder( side: BorderSide(color: Colors.grey.shade300, width: 1), borderRadius: BorderRadius.circular(20), ), elevation: 0, clipBehavior: Clip.antiAlias, margin: EdgeInsets.zero, child: _buildOfferPrimaryDetails(), // فقط اطلاعات اصلی داخل کارت ), _buildActionsRow(), // بخش سوم: پنل باز شونده QR کد _buildExpansionPanel(), ], ); } // این متد فقط اطلاعات اصلی داخل کارت را می‌سازد Widget _buildOfferPrimaryDetails() { return Padding( padding: const EdgeInsets.fromLTRB(15, 25, 15, 25), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: widget.offer.coverImageUrl, width: 90, height: 90, fit: BoxFit.cover, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ SvgPicture.asset(Assets.icons.shop.path), SizedBox(width: 8), Text( widget.offer.storeName, style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 16, ), ), ], ), const SizedBox(height: 4), Row( children: [ SvgPicture.asset(Assets.icons.shoppingCart.path), SizedBox(width: 8), Text( widget.offer.title, style: TextStyle(color: AppColors.hint, fontSize: 14), ), ], ), const SizedBox(height: 8), Row( children: [ SvgPicture.asset(Assets.icons.location.path), SizedBox(width: 8), // برای جلوگیری از سرریز شدن متن، از Flexible استفاده می‌کنیم Flexible( child: Text( "${widget.offer.address} (${widget.offer.distanceInMeters} متر تا تخفیف)", overflow: TextOverflow.ellipsis, maxLines: 1, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.normal, color: AppColors.hint, ), ), ), ], ), ], ), ), ], ), ); } Widget _buildActionsRow() { return Padding( padding: const EdgeInsets.fromLTRB(12, 8, 12, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (_remaining > Duration.zero) Column( children: [ Localizations.override( context: context, locale: const Locale('en'), child: SlideCountdown( duration: _remaining, slideDirection: SlideDirection.up, separator: ':', style: const TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: AppColors.countdown, ), separatorStyle: const TextStyle( fontSize: 20, color: AppColors.countdown, ), decoration: const BoxDecoration(color: Colors.white), shouldShowDays: (d) => d.inDays > 0, shouldShowHours: (d) => d.inHours > 0, shouldShowMinutes: (d) => d.inSeconds > 0, ), ), const SizedBox(height: 4), _buildTimerLabels(_remaining), ], ), SizedBox(width: 10), TextButton( onPressed: () => setState(() => _isExpanded = !_isExpanded), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( _isExpanded ? 'بستن' : 'اطلاعات بیشتر', style: TextStyle(color: AppColors.active), ), const SizedBox(width: 12), AnimatedRotation( turns: _isExpanded ? 0.5 : 0, duration: const Duration(milliseconds: 300), child: SvgPicture.asset( Assets.icons.arrowDown.path, height: 20, ), ), ], ), ), ], ), ); } Widget _buildTimerLabels(Duration duration) { const double columnWidth = 40; const labelStyle = TextStyle(fontSize: 10, color: AppColors.selectedImg); List labels = []; if (duration.inDays > 0) { labels = [ SizedBox( width: columnWidth, child: Center(child: Text("ثانیه", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("دقیقه", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("ساعت", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("روز", style: labelStyle)), ), ]; } else if (duration.inHours > 0) { labels = [ SizedBox( width: columnWidth, child: Center(child: Text("ثانیه", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("دقیقه", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("ساعت", style: labelStyle)), ), ]; } else if (duration.inSeconds > 0) { labels = [ SizedBox( width: columnWidth, child: Center(child: Text("ثانیه", style: labelStyle)), ), SizedBox( width: columnWidth, child: Center(child: Text("دقیقه", style: labelStyle)), ), ]; } return Row(mainAxisAlignment: MainAxisAlignment.center, children: labels); } Widget _buildExpansionPanel() { return AnimatedCrossFade( firstChild: Container(), secondChild: Container( padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), width: double.infinity, child: Center( child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 9, ), decoration: BoxDecoration( color: AppColors.singleOfferType, borderRadius: BorderRadius.circular(20), ), child: Text( "تخفیف ${widget.offer.discountType}", style: const TextStyle( color: Colors.white, fontWeight: FontWeight.normal, fontSize: 17, ), ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( '(${widget.offer.discount})', style: const TextStyle( fontSize: 14, color: AppColors.singleOfferType, fontWeight: FontWeight.normal, ), ), const SizedBox(width: 8), Text( widget.offer.originalPrice.toStringAsFixed(0), style: TextStyle( fontSize: 14, color: Colors.grey.shade600, decoration: TextDecoration.lineThrough, ), ), ], ), const SizedBox(height: 1), Text( '${widget.offer.finalPrice.toStringAsFixed(0)} تومان', style: const TextStyle( color: AppColors.singleOfferType, fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), ], ), SizedBox( height: 20, ), Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Color.fromARGB(255, 246, 246, 246), borderRadius: BorderRadius.circular(16), ), child: Column( children: [ Padding( padding: const EdgeInsets.all(15.0), child: QrImageView( data: widget.offer.qrCodeData, version: QrVersions.auto, size: 280.0, ), ), SizedBox(height: 10), Text( widget.offer.qrCodeData, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), ], ), ), ], ), ), ), crossFadeState: _isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: const Duration(milliseconds: 300), ); } }