Files
kulakpos_app/lib/Screens/subscription/purchase_premium_plan_screen.dart

906 lines
45 KiB
Dart
Raw Normal View History

2026-02-07 15:57:09 +07:00
import 'package:community_material_icon/community_material_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mobile_pos/Provider/profile_provider.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import '../../GlobalComponents/go_to_subscription-package_page_popup_widget.dart';
import '../../constant.dart';
import '../../http_client/custome_http_client.dart';
import '../../model/business_info_model.dart' as bInfo;
import '../Currency/Model/currency_model.dart';
import '../Currency/Provider/currency_provider.dart';
import '../Home/home.dart';
import '../../service/check_user_role_permission_provider.dart';
import '../payment getway/payment_getway_screen.dart';
import 'Model/subscription_plan_model.dart';
import 'Provider/subacription_plan_provider.dart';
import 'Repo/subscriptionPlanRepo.dart';
// class PurchasePremiumPlanScreenPrevious extends StatefulWidget {
// const PurchasePremiumPlanScreenPrevious({super.key, required this.isCameBack, this.isExpired, this.enrolledPlan, this.willExpire});
//
// final bool isCameBack;
// final bool? isExpired;
// final bInfo.EnrolledPlan? enrolledPlan;
// final String? willExpire;
//
// @override
// State<PurchasePremiumPlanScreen> createState() => _PurchasePremiumPlanScreenState();
// }
//
// class _PurchasePremiumPlanScreenState extends State<PurchasePremiumPlanScreen> {
// SubscriptionPlanModelNew? selectedPlan;
// bool isPlanExpiringIn7Days = false;
//
// List<String> imageList = [
// 'images/sp1.png',
// 'images/sp2.png',
// 'images/sp3.png',
// 'images/sp4.png',
// 'images/sp5.png',
// 'images/sp6.png',
// ];
//
// List<String> planDetailsImages = [
// 'images/plan_details_1.png',
// 'images/plan_details_2.png',
// 'images/plan_details_3.png',
// 'images/plan_details_4.png',
// 'images/plan_details_5.png',
// 'images/plan_details_6.png',
// ];
//
// @override
// void didChangeDependencies() {
// super.didChangeDependencies();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (widget.isExpired == true) {
// getUpgradeDialog();
// }
// });
// }
//
// CurrencyModel? getDefoultCurrency({required List<CurrencyModel> currencies}) {
// for (var element in currencies) {
// if (element.isDefault ?? false) {
// return element;
// }
// }
// return null;
// }
//
// // warning popup
// void getUpgradeDialog() {
// showDialog(
// context: context,
// builder: (BuildContext dialogContext) {
// return goToPackagePagePopup(context: dialogContext, enrolledPlan: widget.enrolledPlan);
// });
// }
//
// bool _isRefreshing = false; // Prevents multiple refresh calls
//
// Future<void> refreshData(WidgetRef ref) async {
// if (_isRefreshing) return; // Prevent duplicate refresh calls
// _isRefreshing = true;
//
// ref.refresh(businessInfoProvider);
// ref.refresh(subscriptionPlanProvider);
// ref.refresh(getExpireDateProvider(ref));
//
// await Future.delayed(const Duration(seconds: 1)); // Optional delay
// _isRefreshing = false;
// }
//
// @override
// void initState() {
// // selectedPlan = SubscriptionPlanModel(id: widget.enrolledPlan?.planId);
// if (widget.willExpire != null && DateTime.tryParse(widget.willExpire ?? '') != null) {
// DateTime expiryDate = DateTime.parse(widget.willExpire!);
// isPlanExpiringIn7Days = expiryDate.isBefore(DateTime.now().add(const Duration(days: 6)));
// }
//
// super.initState();
// }
//
// @override
// Widget build(BuildContext context) {
// List<String> planDetailsText = [
// lang.S.of(context).freeLifetimeUpdate,
// lang.S.of(context).android,
// lang.S.of(context).premiumCustomerSupport,
// lang.S.of(context).customInvoiceBranding,
// lang.S.of(context).unlimitedUsage,
// lang.S.of(context).freeDataBackup,
// ];
// List<String> titleListData = [
// lang.S.of(context).freeLifetimeUpdate,
// lang.S.of(context).android,
// lang.S.of(context).premiumCustomerSupport,
// lang.S.of(context).customInvoiceBranding,
// lang.S.of(context).unlimitedUsage,
// lang.S.of(context).freeDataBackup,
// ];
//
// return Consumer(builder: (context, ref, __) {
// final subscriptionPlanData = ref.watch(subscriptionPlanProvider);
// final businessInfo = ref.watch(businessInfoProvider);
// final currencyData = ref.watch(currencyProvider);
// return Scaffold(
// backgroundColor: kWhite,
// body: PopScope(
// canPop: widget.isExpired != true,
// child: RefreshIndicator(
// onRefresh: () => refreshData(ref),
// child: SingleChildScrollView(
// physics: const AlwaysScrollableScrollPhysics(),
// child: SafeArea(
// child: Padding(
// padding: const EdgeInsets.all(20.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// lang.S.of(context).purchasePremium,
// style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
// ),
// GestureDetector(
// onTap: widget.isExpired != true
// ? () {
// if (widget.isCameBack) {
// Navigator.pop(context);
// } else {
// Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(builder: (context) => const Home()),
// (Route<dynamic> route) => false,
// );
// }
// }
// : () => Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(builder: (context) => const Home()),
// (Route<dynamic> route) => false,
// ),
// // ScaffoldMessenger.of(context).showSnackBar(
// // const SnackBar(
// // backgroundColor: Colors.red,
// // content: Text('Please update your plan'),
// // ),
// // ),
//
// child: Icon(
// Icons.cancel_outlined,
// color: widget.isExpired != true ? Colors.grey : Colors.black,
// ),
// )
// ],
// ),
// const SizedBox(height: 20),
// ListView.builder(
// itemCount: imageList.length,
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// itemBuilder: (_, i) {
// return Padding(
// padding: const EdgeInsets.only(bottom: 15),
// child: GestureDetector(
// onTap: () {
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return Dialog(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// const SizedBox(height: 20),
// Row(
// mainAxisSize: MainAxisSize.max,
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// GestureDetector(
// child: const Icon(Icons.cancel),
// onTap: () {
// Navigator.pop(context);
// },
// ),
// const SizedBox(width: 20),
// ],
// ),
// const SizedBox(height: 20),
// Image(
// height: 200,
// width: 200,
// image: AssetImage(planDetailsImages[i]),
// ),
// const SizedBox(height: 20),
// Text(
// planDetailsText[i],
// textAlign: TextAlign.center,
// style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
// ),
// const SizedBox(height: 15),
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: Text(lang.S.of(context).loremIpsumDolor,
// //'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Natoque aliquet et, cur eget. Tellus sapien odio aliq.',
// textAlign: TextAlign.center,
// style: const TextStyle(fontSize: 16)),
// ),
// const SizedBox(height: 20),
// ],
// ),
// );
// },
// );
// },
// child: Container(
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(6), color: kWhite, boxShadow: [
// BoxShadow(color: const Color(0xff0C1A4B).withOpacity(0.24), blurRadius: 1),
// BoxShadow(color: const Color(0xff473232).withOpacity(0.05), offset: const Offset(0, 3), blurRadius: 8, spreadRadius: -1)
// ]),
// child: ListTile(
// visualDensity: const VisualDensity(horizontal: -4),
// contentPadding: const EdgeInsets.only(left: 8, right: 10),
// leading: SizedBox(
// height: 40,
// width: 40,
// child: Image(
// image: AssetImage(imageList[i]),
// ),
// ),
// title: Text(
// titleListData[i],
// style: const TextStyle(fontSize: 16),
// ),
// trailing: const Icon(
// FeatherIcons.alertCircle,
// color: kGreyTextColor,
// size: 20,
// ),
// ),
// ),
// ),
// );
// }),
// const SizedBox(height: 10),
// Text(
// lang.S.of(context).buyPremium,
// style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
// ),
//
// ///_______Plans_List______________________________________________________________
// subscriptionPlanData.when(data: (data) {
// return SizedBox(
// height: (context.width() / 2.5) + 18,
// child: ListView.builder(
// physics: const ClampingScrollPhysics(),
// shrinkWrap: true,
// scrollDirection: Axis.horizontal,
// itemCount: data.length,
// itemBuilder: (BuildContext context, int index) {
// return GestureDetector(
// onTap: () {
// setState(() {
// selectedPlan = data[index];
// });
// },
// child: (data[index].offerPrice != null && (data[index].offerPrice ?? 0) > 0)
// ? Padding(
// padding: const EdgeInsets.only(right: 10),
// child: SizedBox(
// height: (context.width() / 3) + 18,
// child: Stack(
// alignment: Alignment.center,
// children: [
// Padding(
// padding: const EdgeInsets.only(bottom: 20, top: 20),
// child: Container(
// // height: (context.width() / 3) - 20,
// width: (context.width() / 3) - 20,
// decoration: BoxDecoration(
// color: data[index].id == selectedPlan?.id ? kPremiumPlanColor2.withOpacity(0.1) : Colors.white,
// borderRadius: const BorderRadius.all(
// Radius.circular(10),
// ),
// border: Border.all(
// width: 1,
// color: data[index].id == selectedPlan?.id ? kPremiumPlanColor2 : kPremiumPlanColor,
// ),
// ),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// data[index].subscriptionName ?? '',
// style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
// ),
// Text(
// '${data[index].duration} days',
// textAlign: TextAlign.center,
// style: const TextStyle(
// fontSize: 13,
// ),
// ),
// Text(
// '${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${data[index].offerPrice}',
// style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: kPremiumPlanColor2),
// ),
// Text(
// '${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${data[index].subscriptionPrice}',
// style: const TextStyle(decoration: TextDecoration.lineThrough, fontSize: 14, color: Colors.grey),
// ),
// ],
// ),
// ),
// ),
// Positioned(
// top: 8,
// left: 0,
// child: Container(
// height: 25,
// width: 70,
// decoration: const BoxDecoration(
// color: kPremiumPlanColor2,
// borderRadius: BorderRadius.only(
// topLeft: Radius.circular(10),
// bottomRight: Radius.circular(10),
// ),
// ),
// child: Center(
// child: Text(
// // 'Save ${(100 - (((data[index].offerPrice ?? 0) * 100) / (data[index].subscriptionPrice ?? 0))).round().toString()}%',
// '${lang.S.of(context).save} ${(100 - (((data[index].offerPrice ?? 0) * 100) / (data[index].subscriptionPrice ?? 0))).round().toString()}%',
// style: const TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// )
// : Padding(
// padding: const EdgeInsets.only(bottom: 20, top: 20, right: 10),
// child: Container(
// width: (context.width() / 3) - 20,
// decoration: BoxDecoration(
// color: data[index].id == selectedPlan?.id ? kPremiumPlanColor2.withOpacity(0.1) : Colors.white,
// borderRadius: const BorderRadius.all(
// Radius.circular(10),
// ),
// border: Border.all(width: 1, color: data[index].id == selectedPlan?.id ? kPremiumPlanColor2 : kPremiumPlanColor),
// ),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// data[index].subscriptionName ?? '',
// style: const TextStyle(fontSize: 16),
// ),
// Text(
// //'${data[index].duration} days',
// '${data[index].duration} ${lang.S.of(context).days}',
// textAlign: TextAlign.center,
// style: const TextStyle(
// fontSize: 13,
// ),
// ),
// const SizedBox(height: 12),
// Text(
// '${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${data[index].subscriptionPrice.toString()}',
// style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: kPremiumPlanColor),
// )
// ],
// ),
// ),
// ),
// );
// },
// ),
// );
// }, error: (Object error, StackTrace? stackTrace) {
// return Text(error.toString());
// }, loading: () {
// return const Center(child: CircularProgressIndicator());
// }),
// const SizedBox(height: 20),
// Visibility(
// visible: (selectedPlan != null &&
// (widget.enrolledPlan?.planId != selectedPlan?.id || isPlanExpiringIn7Days) &&
// ((widget.enrolledPlan?.duration ?? 0) < (selectedPlan?.duration ?? 0)) &&
// (selectedPlan?.offerPrice != null ? selectedPlan!.offerPrice! > 0 : (selectedPlan?.subscriptionPrice ?? 0) > 0)),
// child: GestureDetector(
// onTap: () async {
// if (selectedPlan != null) {
// bool success = await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => PaymentScreen(
// planId: selectedPlan?.id.toString() ?? '',
// businessId: businessInfo.value?.id.toString() ?? '',
// ),
// ));
//
// if (success) {
// ref.refresh(businessInfoProvider);
// ref.refresh(getExpireDateProvider(ref));
// widget.isExpired == false;
// EasyLoading.showSuccess(
// lang.S.of(context).successfullyPaid,
// // 'successfully paid'
// );
// Navigator.push(context, MaterialPageRoute(builder: (context) => const Home()));
// } else {
// EasyLoading.showError(
// lang.S.of(context).field,
// // 'Field'
// );
// }
// }
// },
// child: Container(
// height: 50,
// decoration: const BoxDecoration(
// color: kMainColor,
// borderRadius: BorderRadius.all(Radius.circular(10)),
// ),
// child: Center(
// child: Text(
// lang.S.of(context).payForSubscribe,
// style: const TextStyle(fontSize: 18, color: Colors.white),
// ),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// ),
// );
// });
// }
// }
class PurchasePremiumPlanScreen extends ConsumerStatefulWidget {
const PurchasePremiumPlanScreen({
super.key,
required this.isCameBack,
this.isExpired,
this.enrolledPlan,
this.willExpire,
2026-02-08 10:31:54 +07:00
this.initiallyShowDialog = true,
2026-02-07 15:57:09 +07:00
});
final bool isCameBack;
final bool? isExpired;
final bInfo.EnrolledPlan? enrolledPlan;
final String? willExpire;
2026-02-08 10:31:54 +07:00
final bool initiallyShowDialog;
2026-02-07 15:57:09 +07:00
@override
ConsumerState<PurchasePremiumPlanScreen> createState() => _SubscriptionPlanScreenState();
}
class _SubscriptionPlanScreenState extends ConsumerState<PurchasePremiumPlanScreen> {
SubscriptionPlanModelNew? selectedPlan;
bool _isLoading = false;
bool isPlanExpiringIn7Days = false;
bool _isRefreshing = false;
int? ineligibleIndex;
SubscriptionPlanRepo subscriptionRepo = SubscriptionPlanRepo();
Widget _buildFeatureItem(String featureKey, dynamic featureValue) {
final isActive = featureValue is List && featureValue.length > 1 && featureValue[1] == "1";
final featureText = featureValue is List ? featureValue[0].toString() : featureKey;
return Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 8),
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
boxShadow: [
BoxShadow(
color: Color(0xff473232).withValues(alpha: 0.05), blurRadius: 8, offset: Offset(0, 3), spreadRadius: -1),
BoxShadow(
color: Color(0xff0C1A4B).withValues(alpha: 0.024), blurRadius: 1, offset: Offset(0, 0), spreadRadius: 0)
],
),
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0),
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
leading: Icon(
isActive ? Icons.check_circle : CommunityMaterialIcons.close_circle,
color: isActive ? Colors.green : Colors.red,
),
title: Text(
featureText,
style: TextStyle(
color: kGreyTextColor,
),
),
),
);
}
CurrencyModel? getDefoultCurrency({required List<CurrencyModel> currencies}) {
for (var element in currencies) {
if (element.isDefault ?? false) {
return element;
}
}
return null;
}
int calculateDiscountPercent(double originalPrice, double offerPrice) {
return ((1 - (offerPrice / originalPrice)) * 100).round();
}
@override
void initState() {
super.initState();
if (widget.willExpire != null && DateTime.tryParse(widget.willExpire ?? '') != null) {
DateTime expiryDate = DateTime.parse(widget.willExpire!);
isPlanExpiringIn7Days = expiryDate.isBefore(DateTime.now().add(const Duration(days: 6)));
}
// Fetch plans and select initial plan
subscriptionRepo.fetchAllPlans().then((plans) {
if (plans.isNotEmpty) {
final currentPlanId = widget.enrolledPlan?.planId;
final matchedPlan = plans.firstWhere(
(plan) => plan.id == currentPlanId,
orElse: () => plans.first,
);
setState(() {
selectedPlan = matchedPlan;
});
}
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addPostFrameCallback((_) {
2026-02-08 10:31:54 +07:00
if (widget.isExpired == true && widget.initiallyShowDialog) {
2026-02-07 15:57:09 +07:00
getUpgradeDialog();
}
});
}
void getUpgradeDialog() {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return goToPackagePagePopup(
context: dialogContext,
enrolledPlan: widget.enrolledPlan,
);
},
);
}
Future<void> refreshData(WidgetRef ref) async {
if (_isRefreshing) return;
_isRefreshing = true;
ref.refresh(businessInfoProvider);
ref.refresh(subscriptionPlanProvider);
ref.refresh(getExpireDateProvider(ref));
await Future.delayed(const Duration(seconds: 1));
_isRefreshing = false;
}
bool showIneligibleMessage = false;
@override
@override
@override
Widget build(BuildContext context) {
final businessInfo = ref.watch(businessInfoProvider);
final currencyData = ref.watch(currencyProvider);
final theme = Theme.of(context);
final permissionService = PermissionService(ref);
return SafeArea(
child: Scaffold(
backgroundColor: kWhite,
bottomNavigationBar: selectedPlan == null
? const SizedBox.shrink()
: Container(
padding: const EdgeInsets.all(16),
child: SizedBox(
height: 50,
width: double.infinity,
child: ElevatedButton(
onPressed: () async {
if (!permissionService.hasPermission(Permit.subscriptionsRead.value)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
content: Text(lang.S.of(context).youDoNotHavePermissionToCreatePurchase),
),
);
return;
}
final plan = selectedPlan!;
final isCurrentPlan = plan.id == widget.enrolledPlan?.planId;
final isUpgradeEligible = (widget.enrolledPlan?.planId != plan.id || isPlanExpiringIn7Days) &&
((widget.enrolledPlan?.duration ?? 0) < (plan.duration ?? 0));
if ((plan.subscriptionPrice ?? 0) <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(lang.S.of(context).thisPlanIsNotAvailableToPurchase)),
);
return;
}
if (isUpgradeEligible || isCurrentPlan) {
final success = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PaymentScreen(
planId: plan.id.toString(),
businessId: businessInfo.value?.data?.id.toString() ?? '',
),
),
);
if (success == true) {
ref.refresh(businessInfoProvider);
ref.refresh(getExpireDateProvider(ref));
EasyLoading.showSuccess(lang.S.of(context).successfullyPaid);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const Home()),
);
} else {
EasyLoading.showError(lang.S.of(context).field);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(lang.S.of(context).thisPlanIsEligibleForUpgrade)),
);
}
},
style: ElevatedButton.styleFrom(
backgroundColor: kMainColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
selectedPlan?.id == widget.enrolledPlan?.planId
? lang.S.of(context).extendPlan
: lang.S.of(context).buyNow,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
),
body: FutureBuilder<List<SubscriptionPlanModelNew>>(
future: subscriptionRepo.fetchAllPlans(),
builder: (context, snapshot) {
if (snapshot.hasError) return Center(child: Text('Error: ${snapshot.error}'));
if (!snapshot.hasData) return const Center(child: CircularProgressIndicator());
final plans = snapshot.data!;
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Features
if (selectedPlan != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lang.S.of(context).purchasePremium,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: kTitleColor),
),
GestureDetector(
onTap: widget.isExpired != true
? () {
if (widget.isCameBack) {
Navigator.pop(context);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const Home()),
(Route<dynamic> route) => false,
);
}
}
: () => Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const Home()),
(Route<dynamic> route) => false,
),
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(
// backgroundColor: Colors.red,
// content: Text('Please update your plan'),
// ),
// ),
child: Icon(
Icons.close,
color: widget.isExpired != true ? Colors.grey : Colors.black,
),
)
],
),
const SizedBox(height: 8),
...selectedPlan!.features.entries.map((entry) => _buildFeatureItem(entry.key, entry.value)),
const SizedBox(height: 16),
],
),
Text(
lang.S.of(context).outPremiumPlan,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w700,
fontSize: 18,
),
),
SizedBox(height: 10),
// Horizontal Plan List
SizedBox(
height: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: plans.length,
itemBuilder: (context, index) {
final plan = plans[index];
final isSelected = selectedPlan?.id == plan.id;
final hasOffer = plan.offerPrice != null && plan.offerPrice! > 0;
final discountPercent =
hasOffer ? calculateDiscountPercent(plan.subscriptionPrice, plan.offerPrice!) : null;
return GestureDetector(
onTap: () => setState(() => selectedPlan = plan),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
margin: const EdgeInsets.only(right: 16),
width: 115,
child: Stack(
clipBehavior: Clip.none,
children: [
// Main card container (single instance now)
Container(
height: 145,
width: 115,
decoration: BoxDecoration(
color: isSelected
? const Color(0xffFEF0F1).withOpacity(0.2)
: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected ? kMainColor : const Color(0xffEAECF0),
),
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
plan.subscriptionName,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 18,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'${plan.duration} ${lang.S.of(context).days}',
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
),
),
const SizedBox(height: 12),
if (hasOffer)
Column(
children: [
Text(
'${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${plan.offerPrice}',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: isSelected ? kMainColor : kTitleColor,
),
),
Text(
'${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${plan.subscriptionPrice}',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
decoration: TextDecoration.lineThrough,
color: Colors.grey,
),
),
const SizedBox(height: 4),
],
)
else
Text(
'${getDefoultCurrency(currencies: currencyData.value ?? [])?.symbol ?? ''}${plan.subscriptionPrice}',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: isSelected ? kMainColor : kTitleColor,
),
),
],
),
),
// Offer banner
if (hasOffer)
Positioned(
top: -8,
left: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: const BoxDecoration(
color: kMainColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: Text(
'${lang.S.of(context).save} $discountPercent%',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
),
),
);
},
),
),
],
),
),
);
},
),
),
);
}
}