387 lines
12 KiB
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),
|
|
);
|
|
}
|
|
} |