proxibuy/lib/presentation/widgets/reserved_list_item_card.dart

387 lines
12 KiB
Dart

// 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<ReservedListItemCard> createState() => _ReservedListItemCardState();
}
class _ReservedListItemCardState extends State<ReservedListItemCard> {
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) {
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(),
_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(
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<Widget> 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),
);
}
}