first commit
This commit is contained in:
29
lib/Screens/subscription/Model/payment_credential_model.dart
Normal file
29
lib/Screens/subscription/Model/payment_credential_model.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
class PaymentCredentialModel {
|
||||
PaymentCredentialModel({
|
||||
required this.shurjopayserverUrl,
|
||||
required this.merchantuserName,
|
||||
required this.merchantPassword,
|
||||
required this.merchantkeyPrefix,
|
||||
});
|
||||
|
||||
PaymentCredentialModel.fromJson(dynamic json) {
|
||||
shurjopayserverUrl = json['SHURJOPAY_SERVER_URL'];
|
||||
merchantuserName = json['MERCHANT_USERNAME'];
|
||||
merchantPassword = json['MERCHANT_PASSWORD'];
|
||||
merchantkeyPrefix = json['MERCHANT_KEY_PREFIX'];
|
||||
}
|
||||
|
||||
late String shurjopayserverUrl;
|
||||
late String merchantuserName;
|
||||
late String merchantPassword;
|
||||
late String merchantkeyPrefix;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['SHURJOPAY_SERVER_URL'] = shurjopayserverUrl;
|
||||
map['MERCHANT_USERNAME'] = merchantuserName;
|
||||
map['MERCHANT_PASSWORD'] = merchantPassword;
|
||||
map['MERCHANT_KEY_PREFIX'] = merchantkeyPrefix;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
77
lib/Screens/subscription/Model/subscription_plan_model.dart
Normal file
77
lib/Screens/subscription/Model/subscription_plan_model.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
class SubscriptionPlanModel {
|
||||
SubscriptionPlanModel({
|
||||
this.id,
|
||||
this.subscriptionName,
|
||||
this.duration,
|
||||
this.offerPrice,
|
||||
this.subscriptionPrice,
|
||||
this.status,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
SubscriptionPlanModel.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
subscriptionName = json['subscriptionName'];
|
||||
duration = json['duration'];
|
||||
offerPrice = json['offerPrice'];
|
||||
subscriptionPrice = json['subscriptionPrice'];
|
||||
status = json['status'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
}
|
||||
|
||||
num? id;
|
||||
String? subscriptionName;
|
||||
num? duration;
|
||||
num? offerPrice;
|
||||
num? subscriptionPrice;
|
||||
num? status;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['subscriptionName'] = subscriptionName;
|
||||
map['duration'] = duration;
|
||||
map['offerPrice'] = offerPrice;
|
||||
map['subscriptionPrice'] = subscriptionPrice;
|
||||
map['status'] = status;
|
||||
map['created_at'] = createdAt;
|
||||
map['updated_at'] = updatedAt;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionPlanModelNew {
|
||||
final int id;
|
||||
final String subscriptionName;
|
||||
final int duration;
|
||||
final double? offerPrice;
|
||||
final double subscriptionPrice;
|
||||
final int status;
|
||||
final Map<String, dynamic> features;
|
||||
|
||||
SubscriptionPlanModelNew({
|
||||
required this.id,
|
||||
required this.subscriptionName,
|
||||
required this.duration,
|
||||
this.offerPrice,
|
||||
required this.subscriptionPrice,
|
||||
required this.status,
|
||||
required this.features,
|
||||
});
|
||||
|
||||
factory SubscriptionPlanModelNew.fromJson(Map<String, dynamic> json) {
|
||||
return SubscriptionPlanModelNew(
|
||||
id: json['id'],
|
||||
subscriptionName: json['subscriptionName'],
|
||||
duration: json['duration'],
|
||||
offerPrice: json['offerPrice']?.toDouble(),
|
||||
subscriptionPrice: json['subscriptionPrice'].toDouble(),
|
||||
status: json['status'],
|
||||
features: json['features'] is Map ? Map<String, dynamic>.from(json['features']) : {},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../Model/subscription_plan_model.dart';
|
||||
import '../Repo/subscriptionPlanRepo.dart';
|
||||
|
||||
SubscriptionPlanRepo subscriptionRepo = SubscriptionPlanRepo();
|
||||
final subscriptionPlanProvider = FutureProvider.autoDispose<List<SubscriptionPlanModelNew>>((ref) => subscriptionRepo.fetchAllPlans());
|
||||
106
lib/Screens/subscription/Repo/subscriptionPlanRepo.dart
Normal file
106
lib/Screens/subscription/Repo/subscriptionPlanRepo.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
// ignore_for_file: file_names, unused_element, unused_local_variable
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mobile_pos/Provider/profile_provider.dart';
|
||||
|
||||
import '../../../Const/api_config.dart';
|
||||
import '../../../Repository/constant_functions.dart';
|
||||
import '../../../http_client/customer_http_client_get.dart';
|
||||
import '../Model/payment_credential_model.dart';
|
||||
import '../Model/subscription_plan_model.dart';
|
||||
|
||||
class SubscriptionPlanRepo {
|
||||
final _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
final Random _rnd = Random();
|
||||
|
||||
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
|
||||
|
||||
Future<List<SubscriptionPlanModelNew>> fetchAllPlans() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/plans');
|
||||
|
||||
final response = await clientGet.get(url: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final partyList = parsedData['data'] as List<dynamic>;
|
||||
return partyList.map((category) => SubscriptionPlanModelNew.fromJson(category)).toList();
|
||||
// Parse into Party objects
|
||||
} else {
|
||||
throw Exception('Failed to fetch Products');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SubscriptionPlanModel>> fetchAllPlansPrevious() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/plans');
|
||||
|
||||
final response = await clientGet.get(url: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final partyList = parsedData['data'] as List<dynamic>;
|
||||
return partyList.map((category) => SubscriptionPlanModel.fromJson(category)).toList();
|
||||
// Parse into Party objects
|
||||
} else {
|
||||
throw Exception('Failed to fetch Products');
|
||||
}
|
||||
}
|
||||
|
||||
Future<PaymentCredentialModel> getPaymentCredential() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/gateways');
|
||||
final response = await clientGet.get(url: uri);
|
||||
print(response.statusCode);
|
||||
print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final data = parsedData['data'];
|
||||
return PaymentCredentialModel.fromJson(data);
|
||||
} else {
|
||||
throw Exception('Failed to fetch credential');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> subscribePlan({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required int planId,
|
||||
required String paymentMethod,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/subscribes');
|
||||
|
||||
var responseData = await http.post(uri, headers: {
|
||||
"Accept": 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
}, body: {
|
||||
'plan_id': planId.toString(),
|
||||
'subscriptionMethod': paymentMethod,
|
||||
});
|
||||
|
||||
try {
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
|
||||
if (responseData.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Subscribe successful!')));
|
||||
var data = ref.refresh(businessInfoProvider);
|
||||
ref.refresh(getExpireDateProvider(ref));
|
||||
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Subscribe creation failed: ${parsedData['message']}')));
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('An error occurred: $error')));
|
||||
}
|
||||
}
|
||||
}
|
||||
331
lib/Screens/subscription/package_screen.dart
Normal file
331
lib/Screens/subscription/package_screen.dart
Normal file
@@ -0,0 +1,331 @@
|
||||
import 'package:community_material_icon/community_material_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Provider/profile_provider.dart';
|
||||
import 'package:mobile_pos/Screens/subscription/purchase_premium_plan_screen.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../http_client/subscription_expire_provider.dart';
|
||||
import '../Home/home_screen.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'Model/subscription_plan_model.dart';
|
||||
import 'Provider/subacription_plan_provider.dart';
|
||||
|
||||
class PackageScreen extends StatefulWidget {
|
||||
const PackageScreen({super.key});
|
||||
|
||||
@override
|
||||
State<PackageScreen> createState() => _PackageScreenState();
|
||||
}
|
||||
|
||||
class _PackageScreenState extends State<PackageScreen> {
|
||||
Duration? remainTime;
|
||||
List<String>? initialPackageService;
|
||||
List<int>? mainPackageService;
|
||||
List<String> imageList = [
|
||||
'images/sales_2.png',
|
||||
'images/purchase_2.png',
|
||||
'images/due_collection_2.png',
|
||||
'images/parties_2.png',
|
||||
'images/product1.png',
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
bool _isRefreshing = false;
|
||||
|
||||
Future<void> refreshData(WidgetRef ref) async {
|
||||
if (_isRefreshing) return;
|
||||
_isRefreshing = true;
|
||||
|
||||
ref.refresh(businessInfoProvider);
|
||||
ref.refresh(getExpireDateProvider(ref));
|
||||
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
_isRefreshing = false;
|
||||
}
|
||||
|
||||
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: 0, 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<String> nameList = [
|
||||
lang.S.of(context).sales,
|
||||
lang.S.of(context).purchase,
|
||||
lang.S.of(context).dueCollection,
|
||||
lang.S.of(context).parties,
|
||||
lang.S.of(context).products,
|
||||
];
|
||||
final theme = Theme.of(context);
|
||||
return Consumer(builder: (context, ref, __) {
|
||||
final profileInfo = ref.watch(businessInfoProvider);
|
||||
final permissionService = PermissionService(ref);
|
||||
return profileInfo.when(
|
||||
data: (info) {
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
title: Text(
|
||||
lang.S.of(context).yourPack,
|
||||
// style: GoogleFonts.poppins(
|
||||
// color: Colors.black,
|
||||
// ),
|
||||
),
|
||||
centerTitle: true,
|
||||
iconTheme: const IconThemeData(color: Colors.black),
|
||||
elevation: 0.0,
|
||||
),
|
||||
bottomNavigationBar: Visibility(
|
||||
// visible: permissionService.hasPermission(Permit.subscriptionsRead.value),
|
||||
child: SizedBox(
|
||||
height: 115,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20),
|
||||
child: Text(
|
||||
lang.S.of(context).unlimitedUsagesOfOurPackage,
|
||||
//'Unlimited Usages of Our Package👇 ',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final subscriptionState = ref.read(subscriptionProvider);
|
||||
PurchasePremiumPlanScreen(
|
||||
isCameBack: true,
|
||||
enrolledPlan: subscriptionState.isExpired ? null : info.data?.enrolledPlan,
|
||||
willExpire: info.data?.willExpire,
|
||||
).launch(context);
|
||||
},
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: const BoxDecoration(
|
||||
color: kMainColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
lang.S.of(context).updateNow,
|
||||
style: const TextStyle(fontSize: 18, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => refreshData(ref),
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 80,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(color: kMainColor.withOpacity(0.1), borderRadius: const BorderRadius.all(Radius.circular(10))),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
info.data?.enrolledPlan != null
|
||||
? (info.data?.enrolledPlan?.price ?? 0) > 0
|
||||
? lang.S.of(context).premiumPlan
|
||||
: lang.S.of(context).freePlan
|
||||
: 'No active plan!',
|
||||
style: const TextStyle(fontSize: 18),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Flexible(
|
||||
child: info.data?.enrolledPlan?.plan != null
|
||||
? Text.rich(TextSpan(text: lang.S.of(context).youRUsing, children: [
|
||||
TextSpan(
|
||||
text: '${info.data?.enrolledPlan?.plan?.subscriptionName} Package',
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: kMainColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
))
|
||||
]))
|
||||
: const Text('You don’t have an active plan.'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 63,
|
||||
width: 63,
|
||||
decoration: const BoxDecoration(
|
||||
color: kMainColor,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(50),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text(
|
||||
getSubscriptionExpiring(expireDate: info.data?.willExpire, shortMSG: true),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.white),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
lang.S.of(context).packFeatures,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
// const SizedBox(height: 20),
|
||||
// ListView.builder(
|
||||
// itemCount: nameList.length,
|
||||
// shrinkWrap: true,
|
||||
// physics: const NeverScrollableScrollPhysics(),
|
||||
// itemBuilder: (_, i) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.only(bottom: 16),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {},
|
||||
// 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), spreadRadius: -1, blurRadius: 8)
|
||||
// ],
|
||||
// ),
|
||||
// child: ListTile(
|
||||
// visualDensity: const VisualDensity(vertical: -4),
|
||||
// horizontalTitleGap: 10,
|
||||
// contentPadding: const EdgeInsets.only(left: 6, top: 6, bottom: 6, right: 12),
|
||||
// leading: SizedBox(
|
||||
// height: 40,
|
||||
// width: 40,
|
||||
// child: Image(
|
||||
// image: AssetImage(imageList[i]),
|
||||
// ),
|
||||
// ),
|
||||
// title: Text(
|
||||
// nameList[i],
|
||||
// style: const TextStyle(fontSize: 16),
|
||||
// ),
|
||||
// trailing: Text(
|
||||
// lang.S.of(context).unlimited,
|
||||
// style: const TextStyle(color: Colors.grey),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
const SizedBox(height: 20),
|
||||
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!;
|
||||
final currentPlanId = info.data?.enrolledPlan?.planId;
|
||||
final currentPlan = plans.firstWhere(
|
||||
(plan) => plan.id == currentPlanId,
|
||||
);
|
||||
|
||||
if (currentPlan.id == null) {
|
||||
return const Center(child: Text("Current plan not found."));
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...currentPlan.features.entries.map(
|
||||
(entry) => _buildFeatureItem(entry.key, entry.value),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
error: (error, stackTrace) {
|
||||
return Text(error.toString());
|
||||
},
|
||||
loading: () {
|
||||
return const CircularProgressIndicator();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
903
lib/Screens/subscription/purchase_premium_plan_screen.dart
Normal file
903
lib/Screens/subscription/purchase_premium_plan_screen.dart
Normal file
@@ -0,0 +1,903 @@
|
||||
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,
|
||||
});
|
||||
final bool isCameBack;
|
||||
final bool? isExpired;
|
||||
final bInfo.EnrolledPlan? enrolledPlan;
|
||||
final String? willExpire;
|
||||
|
||||
@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((_) {
|
||||
if (widget.isExpired == true) {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user