Update: 2026-06-11 18:22:57

This commit is contained in:
Hamza-Ayed
2026-06-11 18:22:59 +03:00
parent c5170a88d2
commit 727068b668
629 changed files with 46050 additions and 46109 deletions

View File

@@ -6,6 +6,9 @@ import 'package:siro_rider/controller/auth/login_controller.dart';
import '../../constant/colors.dart';
import '../../controller/auth/otp_controller.dart';
import '../../controller/local/phone_intel/intl_phone_field.dart';
import '../../controller/functions/country_logic.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
// ─────────────────────────────────────────────────────────────────────────────
// SHARED DESIGN TOKENS
@@ -485,7 +488,7 @@ class _PhoneNumberScreenState extends State<PhoneNumberScreen> {
const BorderSide(color: Color(0xFFEF4444), width: 1.5),
),
),
initialCountryCode: 'SY',
initialCountryCode: CountryLogic.getCountryPrefix(box.read(BoxName.countryCode) ?? 'Syria'),
onChanged: (phone) {
_phoneController.text = phone.completeNumber;
},

View File

@@ -9,6 +9,9 @@ import 'package:get/get.dart';
import '../../controller/local/phone_intel/intl_phone_field.dart';
import '../../print.dart';
import '../widgets/mycircular.dart';
import '../../controller/functions/country_logic.dart';
import '../../../constant/box_name.dart';
import '../../../main.dart';
// import 'package:intl_phone_field/intl_phone_field.dart';
class SmsSignupEgypt extends StatelessWidget {
@@ -52,7 +55,7 @@ class SmsSignupEgypt extends StatelessWidget {
borderSide: BorderSide(),
),
),
initialCountryCode: 'EG',
initialCountryCode: CountryLogic.getCountryPrefix(box.read(BoxName.countryCode) ?? 'Syria'),
onChanged: (phone) {
// Properly concatenate country code and number
registerController.phoneController.text =

View File

@@ -1,3 +1,4 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:siro_rider/constant/colors.dart';
import 'package:siro_rider/constant/links.dart';
import 'package:siro_rider/constant/style.dart';
@@ -188,7 +189,7 @@ class ApplyOrderWidget extends StatelessWidget {
),
),
Text(
'SYP'.tr,
CurrencyHelper.currency,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,

View File

@@ -551,7 +551,7 @@ class Details extends StatelessWidget {
],
),
Text(
'Cost for passenger ${controller.totalPassenger.toStringAsFixed(2)} ',
'Cost for passenger ${double.parse(controller.totalPassenger.toString()).toStringAsFixed(2)} ',
style: AppStyle.title,
),
],

View File

@@ -1,4 +1,6 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:flutter/material.dart';
import 'package:siro_rider/controller/home/map/map_engine_controller.dart';
import 'package:get/get.dart';
import 'package:siro_rider/constant/box_name.dart';
import 'package:siro_rider/constant/colors.dart';
@@ -245,7 +247,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
borderRadius: BorderRadius.circular(20),
),
child: Text(
'${_getPassengerPriceText(carTypes[controller.selectedIndex], controller)} ${'SYP'.tr}',
'${_getPassengerPriceText(carTypes[controller.selectedIndex], controller)} ${CurrencyHelper.currency}',
style: const TextStyle(
color: Colors.white,
fontSize: 13,
@@ -413,7 +415,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
),
child: FittedBox(
child: Text(
'${_getPassengerPriceText(carType, controller)} ${'SYP'.tr}',
'${_getPassengerPriceText(carType, controller)} ${CurrencyHelper.currency}',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
@@ -537,7 +539,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
const SizedBox(width: 10),
Expanded(
child: Text(
'${'You have a negative balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${'SYP'.tr}.',
'${'You have a negative balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${CurrencyHelper.currency}.',
style: TextStyle(
color: Colors.red.shade800,
fontWeight: FontWeight.w600,
@@ -557,7 +559,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
// ═══════════════════════════════════════════════════════════════════════════
String _getPassengerPriceText(
CarType carType, RideLifecycleController mapPassengerController) {
double rawPrice;
String rawPrice;
switch (carType.carType) {
case 'Comfort':
rawPrice = mapPassengerController.totalPassengerComfort;
@@ -587,9 +589,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
default:
return '...';
}
final int roundedPrice = rawPrice.round();
final formatter = NumberFormat.decimalPattern();
return formatter.format(roundedPrice);
return rawPrice;
}
// ═══════════════════════════════════════════════════════════════════════════
@@ -743,7 +743,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
borderRadius: BorderRadius.circular(20),
),
child: Text(
'${_getPassengerPriceText(carType, mapPassengerController)} ${'SYP'.tr}',
'${_getPassengerPriceText(carType, mapPassengerController)} ${CurrencyHelper.currency}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w800,
@@ -931,7 +931,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
}
}
double _getOriginalPrice(
String _getOriginalPrice(
CarType carType, RideLifecycleController mapPassengerController) {
switch (carType.carType) {
case 'Comfort':
@@ -947,15 +947,11 @@ class CarDetailsTypeToChoose extends StatelessWidget {
case 'Lady':
return mapPassengerController.totalPassengerLady;
default:
return 0.0;
return '0';
}
}
Widget _buildRayehGaiOption(
BuildContext context,
RideLifecycleController mapPassengerController,
String carTypeName,
double price) {
Widget _buildRayehGaiOption(BuildContext context, RideLifecycleController mapPassengerController, String carTypeName, String price) {
return GestureDetector(
onTap: () {
Navigator.of(context).pop();
@@ -969,7 +965,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: Column(children: [
Text(carTypeName.tr),
Text(price.toStringAsFixed(0))
Text(double.parse(price).toStringAsFixed(0))
])),
);
}
@@ -1031,7 +1027,7 @@ class BurcMoney extends StatelessWidget {
),
TextSpan(
text:
'${'You have a balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${box.read(BoxName.countryCode) == 'Egypt' ? 'LE'.tr : 'SYP'.tr} ${'due to a previous trip.'.tr}',
'${'You have a balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${box.read(BoxName.countryCode) == 'Egypt' ? 'LE'.tr : CurrencyHelper.currency} ${'due to a previous trip.'.tr}',
style: AppStyle.subtitle.copyWith(
color: Colors.white,
),

View File

@@ -1,3 +1,4 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_rider/constant/box_name.dart';
@@ -80,7 +81,7 @@ class CashConfirmPageShown extends StatelessWidget {
icon: Icons.account_balance_wallet_outlined,
title: '${AppInformation.appName} Balance'.tr,
subtitle:
'${'Balance:'.tr} ${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'SYP'.tr}',
'${'Balance:'.tr} ${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${CurrencyHelper.currency}',
isSelected: paymentCtrl.isWalletChecked,
selectedColor: selectedColor,
onTap: () =>
@@ -107,7 +108,7 @@ class CashConfirmPageShown extends StatelessWidget {
final bool isWalletSufficient = (double.tryParse(
box.read(BoxName.passengerWalletTotal) ?? '0') ??
0) >=
controller.totalPassenger;
double.parse(controller.totalPassenger.toString());
// إذا تم اختيار المحفظة والرصيد غير كافٍ
if (paymentCtrl.isWalletChecked && !isWalletSufficient) {

View File

@@ -1,3 +1,4 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
@@ -7,7 +8,7 @@ import 'package:intl/intl.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import 'package:siro_rider/controller/functions/country_logic.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
@@ -17,6 +18,7 @@ import '../../../controller/home/map/ride_state.dart';
import '../../../controller/profile/profile_controller.dart';
import '../../../main.dart';
import '../../../views/home/profile/complaint_page.dart';
import '../../../controller/functions/country_logic.dart';
class RideBeginPassenger extends StatelessWidget {
const RideBeginPassenger({super.key});
@@ -115,7 +117,8 @@ class RideBeginPassenger extends StatelessWidget {
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColor.primaryColor.withValues(alpha: 0.5), width: 1.5),
color: AppColor.primaryColor.withValues(alpha: 0.5),
width: 1.5),
),
child: CircleAvatar(
radius: 24,
@@ -207,7 +210,7 @@ class RideBeginPassenger extends StatelessWidget {
color: AppColor.primaryColor,
),
),
Text('SYP',
Text(CurrencyHelper.currency,
style: TextStyle(fontSize: 9, color: AppColor.grayColor)),
],
),
@@ -240,7 +243,8 @@ class RideBeginPassenger extends StatelessWidget {
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
} else {
makePhoneCall('112');
makePhoneCall(CountryLogic.getEmergencyNumber(
box.read(BoxName.countryCode) ?? 'Syria'));
}
},
),
@@ -255,13 +259,12 @@ class RideBeginPassenger extends StatelessWidget {
// لا يوجد رقم طوارئ — نعرض الديالوج لإدخاله
await uiController.shareTripWithFamily();
} else {
final formattedPhone = uiController.formatSyrianPhoneNumber(
phone.toString());
final formattedPhone =
CountryLogic.formatCurrentCountryPhone(phone.toString());
uiController.sendWhatsapp(formattedPhone);
}
},
),
_compactBtn(
icon: Icons.share,
label: 'Share'.tr,
@@ -287,12 +290,14 @@ class RideBeginPassenger extends StatelessWidget {
if (!audioCtx.isRecording) {
await audioCtx.startRecording(rideId: controller.rideId);
if (context.mounted) {
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
Toast.show(
context, 'Start Record'.tr, AppColor.greenColor);
}
} else {
await audioCtx.stopRecording();
if (context.mounted) {
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
Toast.show(
context, 'Record saved'.tr, AppColor.greenColor);
}
}
},

View File

@@ -1,7 +1,9 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:siro_rider/controller/functions/launch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../controller/functions/country_logic.dart';
// ... استيراد ملفاتك الأخرى ...
import '../../../constant/box_name.dart';
@@ -38,7 +40,7 @@ class RideFromStartApp extends StatelessWidget {
final driverName = rideData['driverName'] ?? 'Captain'.tr;
final driverRate = controller.driverRate;
final carType = rideData['carType'] ?? 'Car'.tr;
final carModel = controller.model ?? '';
final carModel = controller.model;
final arrivalTime = box.read(BoxName.arrivalTime) ?? '--:--';
// تحديد البيانات للعرض
@@ -177,7 +179,7 @@ class RideFromStartApp extends StatelessWidget {
"Distance".tr),
_buildVerticalDivider(),
_buildInfoColumn(
Icons.attach_money, "$displayPrice SYP", "Price".tr),
Icons.attach_money, "$displayPrice ${CurrencyHelper.currency}", "Price".tr),
],
),
),
@@ -213,7 +215,7 @@ class RideFromStartApp extends StatelessWidget {
flex: 1,
child: ElevatedButton.icon(
onPressed: () =>
makePhoneCall(box.read(BoxName.sosPhonePassenger)),
makePhoneCall(CountryLogic.getEmergencyNumber(box.read(BoxName.countryCode) ?? 'Syria')),
icon: const Icon(Icons.sos, color: Colors.white),
label: Text("SOS".tr,
style: const TextStyle(color: Colors.white)),

View File

@@ -14,6 +14,7 @@ import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map/ride_lifecycle_controller.dart';
import '../../../controller/home/map/ui_interactions_controller.dart';
import '../../../controller/functions/country_logic.dart';
class VipRideBeginPassenger extends StatelessWidget {
const VipRideBeginPassenger({
@@ -196,7 +197,7 @@ class VipRideBeginPassenger extends StatelessWidget {
profileController.prfoileData['sosPhone']);
}
} else {
makePhoneCall('122');
makePhoneCall(CountryLogic.getEmergencyNumber(box.read(BoxName.countryCode) ?? 'Syria'));
// box.read(BoxName.sosPhonePassenger));
}
},

View File

@@ -1,3 +1,4 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -153,7 +154,7 @@ class PassengerWallet extends StatelessWidget {
.copyWith(color: Colors.white.withOpacity(0.7)),
),
Text(
'${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'SYP'.tr}',
'${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${CurrencyHelper.currency}',
style: AppStyle.headTitle2.copyWith(
color: Colors.white,
fontSize: 28,

View File

@@ -1,6 +1,7 @@
import 'package:siro_rider/constant/currency.dart';
import 'package:siro_rider/print.dart';
import 'package:siro_rider/constant/style.dart';
import 'package:siro_rider/controller/functions/encrypt_decrypt.dart';
// import 'package:siro_rider/controller/functions/encrypt_decrypt.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -42,7 +43,7 @@ class PassengerWalletDialog extends StatelessWidget {
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '10000 ${'LE'.tr}'
: '10 ${'SYP'.tr}',
: '10 ${CurrencyHelper.currency}',
),
),
CupertinoActionSheetAction(
@@ -55,7 +56,7 @@ class PassengerWalletDialog extends StatelessWidget {
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '20000 ${'LE'.tr} = 2050 ${'LE'.tr}'
: '20 ${'SYP'.tr}',
: '20 ${CurrencyHelper.currency}',
),
),
CupertinoActionSheetAction(
@@ -68,7 +69,7 @@ class PassengerWalletDialog extends StatelessWidget {
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '40000 ${'LE'.tr} = 4150 ${'LE'.tr}'
: '40 ${'SYP'.tr}',
: '40 ${CurrencyHelper.currency}',
),
),
CupertinoActionSheetAction(
@@ -81,7 +82,7 @@ class PassengerWalletDialog extends StatelessWidget {
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '100000 ${'LE'.tr} = 11000 ${'LE'.tr}'
: '50 ${'SYP'.tr}',
: '50 ${CurrencyHelper.currency}',
),
),
],
@@ -130,10 +131,10 @@ void showPaymentBottomSheet(BuildContext context) {
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
return PopScope(
canPop: true,
onPopInvokedWithResult: (didPop, result) async {
Get.back();
return false;
},
child: Container(
padding: const EdgeInsets.all(16.0),
@@ -154,7 +155,7 @@ void showPaymentBottomSheet(BuildContext context) {
controller: controller,
amount: 500,
bonusAmount: 30,
currency: 'SYP'.tr,
currency: CurrencyHelper.currency,
),
const SizedBox(height: 8.0),
@@ -163,7 +164,7 @@ void showPaymentBottomSheet(BuildContext context) {
controller: controller,
amount: 1000,
bonusAmount: 70,
currency: 'SYP'.tr,
currency: CurrencyHelper.currency,
),
const SizedBox(height: 8.0),
@@ -172,7 +173,7 @@ void showPaymentBottomSheet(BuildContext context) {
controller: controller,
amount: 2000,
bonusAmount: 180,
currency: 'SYP'.tr,
currency: CurrencyHelper.currency,
),
const SizedBox(height: 8.0),
@@ -181,7 +182,7 @@ void showPaymentBottomSheet(BuildContext context) {
controller: controller,
amount: 5000,
bonusAmount: 700,
currency: 'SYP'.tr,
currency: CurrencyHelper.currency,
),
const SizedBox(height: 16.0),
@@ -266,7 +267,7 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
// controller.payWithMTNWallet(
// context,
// controller.selectedAmount.toString(),
// 'SYP',
// CurrencyHelper.currency,
// );
// await controller.getPassengerWallet();
// controller.isLoading = false;
@@ -331,148 +332,215 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
// Get.back();
// Get.defaultDialog(
// barrierDismissible: false,
// title: 'Insert Wallet phone number'.tr,
// content: Form(
// key: controller.formKey,
// child: MyTextForm(
// controller: controller.walletphoneController,
// label: 'Insert Wallet phone number'.tr,
// hint: '963941234567',
// type: TextInputType.phone)),
// confirm: MyElevatedButton(
// title: 'OK'.tr,
// onPressed: () async {
// Get.back();
// if (controller.formKey.currentState!.validate()) {
// if (controller.selectedAmount != 0) {
// controller.isLoading = true;
// controller.update();
// box.write(BoxName.phoneWallet,
// (controller.walletphoneController.text));
// Get.back();
// await controller.payWithMTNWallet(
// context,
// controller.selectedAmount.toString(),
// 'SYP',
// );
// await controller.getPassengerWallet();
// controller.isLoading = false;
// controller.update();
// } else {
// Toast.show(
// context,
// '⚠️ You need to choose an amount!'.tr,
// AppColor.redColor,
// );
// }
// }
// }));
// },
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Pay by MTN Wallet'.tr,
// style: AppStyle.title,
// ),
// const SizedBox(width: 10),
// Image.asset(
// 'assets/images/cashMTN.png',
// width: 70,
// height: 70,
// fit: BoxFit.contain,
// ),
// ],
// ),
// )),
if (box.read(BoxName.countryCode) == 'Syria')
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963941234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (controller.formKey.currentState!.validate()) {
if (controller.selectedAmount != 0) {
controller.isLoading = true;
controller.update();
box.write(BoxName.phoneWallet,
(controller.walletphoneController.text));
await controller.payWithMTNWallet(
context,
controller.selectedAmount.toString(),
CurrencyHelper.currency,
);
await controller.getPassengerWallet();
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963941234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (controller.formKey.currentState!.validate()) {
box.write(BoxName.phoneWallet,
controller.walletphoneController.text);
await controller.payWithSyriaTelWallet(
controller.selectedAmount.toString(), 'SYP');
}
}));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Syriatel Wallet'.tr,
controller.isLoading = false;
controller.update();
} else {
Toast.show(
context,
'⚠️ You need to choose an amount!'.tr,
AppColor.redColor,
);
}
}
}));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by MTN Wallet'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/cashMTN.png',
width: 70,
height: 70,
fit: BoxFit.contain,
),
],
),
)),
if (box.read(BoxName.countryCode) == 'Jordan')
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '962791234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (controller.formKey.currentState!.validate()) {
if (controller.selectedAmount != 0) {
controller.isLoading = true;
controller.update();
box.write(BoxName.phoneWallet,
(controller.walletphoneController.text));
await controller.payWithClickWallet(
context,
controller.selectedAmount.toString(),
CurrencyHelper.currency,
);
await controller.getPassengerWallet();
controller.isLoading = false;
controller.update();
} else {
Toast.show(
context,
'⚠️ You need to choose an amount!'.tr,
AppColor.redColor,
);
}
}
}));
},
child: Container(
margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.transparent,
),
child: ListTile(
title: Text(
'Pay by Cliq'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/syriatel.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
GestureDetector(
onTap: () async {
// التحقق بالبصمة قبل أي شيء
bool isAuthSupported =
await LocalAuthentication().isDeviceSupported();
trailing: const Icon(Icons.payment, size: 40, color: Colors.green),
),
)), if (box.read(BoxName.countryCode) == 'Syria')
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963941234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (controller.formKey.currentState!.validate()) {
box.write(BoxName.phoneWallet,
controller.walletphoneController.text);
await controller.payWithSyriaTelWallet(
controller.selectedAmount.toString(),
CurrencyHelper.currency);
}
}));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Syriatel Wallet'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/syriatel.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
if (box.read(BoxName.countryCode) == 'Syria')
GestureDetector(
onTap: () async {
// التحقق بالبصمة قبل أي شيء
bool isAuthSupported =
await LocalAuthentication().isDeviceSupported();
if (isAuthSupported) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (isAuthSupported) {
bool didAuthenticate =
await LocalAuthentication().authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (!didAuthenticate) {
Log.print("❌ User did not authenticate with biometrics");
return;
if (!didAuthenticate) {
Log.print("❌ User did not authenticate with biometrics");
return;
}
}
}
// الانتقال مباشرة لإنشاء الفاتورة بعد النجاح بالبصمة
Get.to(() => PaymentScreenSmsProvider(
amount: double.parse(controller.selectedAmount.toString())));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Sham Cash'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/shamCash.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
// الانتقال مباشرة لإنشاء الفاتورة بعد النجاح بالبصمة
Get.to(() => PaymentScreenSmsProvider(
amount:
double.parse(controller.selectedAmount.toString())));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Sham Cash'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/shamCash.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
@@ -483,4 +551,4 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
),
),
);
}
}

View File

@@ -0,0 +1,208 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
class PaymentScreenCliq extends StatefulWidget {
final double amount;
final String invoiceNumber;
final String cliqAlias;
const PaymentScreenCliq({
Key? key,
required this.amount,
required this.invoiceNumber,
required this.cliqAlias,
}) : super(key: key);
@override
State<PaymentScreenCliq> createState() => _PaymentScreenCliqState();
}
class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTickerProviderStateMixin {
Timer? _pollingTimer;
String _status = 'waiting'; // waiting, uploading, verifying, success, error
final TextEditingController _proofController = TextEditingController();
late AnimationController _blinkController;
late Animation<Color?> _colorAnimation;
late Animation<double> _shadowAnimation;
@override
void initState() {
super.initState();
_blinkController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this)..repeat(reverse: true);
_colorAnimation = ColorTween(begin: Colors.red.shade700, end: Colors.red.shade100).animate(_blinkController);
_shadowAnimation = Tween<double>(begin: 2.0, end: 15.0).animate(CurvedAnimation(parent: _blinkController, curve: Curves.easeInOut));
_startPolling();
}
@override
void dispose() {
_pollingTimer?.cancel();
_blinkController.dispose();
_proofController.dispose();
super.dispose();
}
void _startPolling() {
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
if (_status == 'success' || _status == 'verifying') return;
try {
final res = await CRUD().postWallet(link: AppLink.checkCliqStatus, payload: {'invoice_number': widget.invoiceNumber});
if (res != 'failure' && res['status'] == 'success' && res['invoice_status'] == 'completed') {
timer.cancel();
if (mounted) setState(() => _status = 'success');
}
} catch (_) {}
});
}
Future<void> _submitProof() async {
if (_proofController.text.trim().isEmpty) {
Get.snackbar('Error'.tr, 'Please paste the transfer message'.tr, backgroundColor: Colors.red);
return;
}
setState(() => _status = 'verifying');
try {
final res = await CRUD().postWallet(link: AppLink.uploadCliqProof, payload: {
'invoice_number': widget.invoiceNumber,
'proof_text': _proofController.text.trim(),
});
if (res != 'failure' && res['status'] == 'success') {
if (mounted) setState(() => _status = 'success');
} else {
if (mounted) setState(() => _status = 'error');
Get.defaultDialog(title: 'Failed'.tr, content: Text(res['message']?.toString() ?? 'Verification failed'));
}
} catch (e) {
if (mounted) setState(() => _status = 'error');
Get.defaultDialog(title: 'Error'.tr, content: Text('Error uploading proof'.tr));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(title: const Text("Cliq Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: _status == 'success' ? _buildSuccessUI() : _buildWaitingUI(),
),
),
);
}
Widget _buildWaitingUI() {
final currencyFormat = NumberFormat.decimalPattern('ar_SY');
return SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.blue.shade800, Colors.blue.shade600]), borderRadius: BorderRadius.circular(16)),
child: Column(
children: [
const Text("المبلغ المطلوب", style: TextStyle(color: Colors.white70, fontSize: 14)),
const SizedBox(height: 5),
Text("${currencyFormat.format(widget.amount)} ل.س", style: const TextStyle(color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold)),
],
),
),
const SizedBox(height: 25),
AnimatedBuilder(
animation: _blinkController,
builder: (context, child) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: _colorAnimation.value ?? Colors.red, width: 3.0), boxShadow: [BoxShadow(color: (_colorAnimation.value ?? Colors.red).withOpacity(0.4), blurRadius: _shadowAnimation.value, spreadRadius: 2)]),
child: Column(
children: [
const Text("يرجى تحويل المبلغ إلى الاسم المستعار التالي (Alias):", textAlign: TextAlign.center, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: widget.cliqAlias));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text("تم نسخ الاسم ✅", textAlign: TextAlign.center), backgroundColor: Colors.green.shade600));
},
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.shade200)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(widget.cliqAlias, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 2.0)),
const Icon(Icons.copy, color: Colors.blue),
],
),
),
),
],
),
);
},
),
const SizedBox(height: 30),
const Text("بعد إتمام التحويل، يرجى نسخ رسالة البنك النصية ولصقها هنا للتحقق التلقائي:", textAlign: TextAlign.center, style: TextStyle(fontSize: 14)),
const SizedBox(height: 10),
TextField(
controller: _proofController,
maxLines: 4,
decoration: InputDecoration(
hintText: "قم بلصق نص رسالة التحويل هنا...",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.white,
),
),
const SizedBox(height: 15),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue.shade800, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
onPressed: _status == 'verifying' ? null : _submitProof,
child: _status == 'verifying'
? const CircularProgressIndicator(color: Colors.white)
: const Text("تحقق من الدفع", style: TextStyle(fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold)),
),
),
const SizedBox(height: 20),
const Text("جاري فحص الفاتورة تلقائياً كل 5 ثوانٍ...", style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
);
}
Widget _buildSuccessUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.verified_rounded, color: Colors.green, size: 100),
const SizedBox(height: 20),
const Text("تم الدفع بنجاح!", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 40),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16)),
onPressed: () { Get.back(); Get.back(); },
child: const Text("متابعة", style: TextStyle(fontSize: 18)),
),
),
],
);
}
}

View File

@@ -0,0 +1,208 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
class PaymentScreenMtn extends StatefulWidget {
final double amount;
final String invoiceNumber;
final String mtnNumber;
const PaymentScreenMtn({
Key? key,
required this.amount,
required this.invoiceNumber,
required this.mtnNumber,
}) : super(key: key);
@override
State<PaymentScreenMtn> createState() => _PaymentScreenMtnState();
}
class _PaymentScreenMtnState extends State<PaymentScreenMtn> with SingleTickerProviderStateMixin {
Timer? _pollingTimer;
String _status = 'waiting'; // waiting, uploading, verifying, success, error
final TextEditingController _proofController = TextEditingController();
late AnimationController _blinkController;
late Animation<Color?> _colorAnimation;
late Animation<double> _shadowAnimation;
@override
void initState() {
super.initState();
_blinkController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this)..repeat(reverse: true);
_colorAnimation = ColorTween(begin: Colors.red.shade700, end: Colors.red.shade100).animate(_blinkController);
_shadowAnimation = Tween<double>(begin: 2.0, end: 15.0).animate(CurvedAnimation(parent: _blinkController, curve: Curves.easeInOut));
_startPolling();
}
@override
void dispose() {
_pollingTimer?.cancel();
_blinkController.dispose();
_proofController.dispose();
super.dispose();
}
void _startPolling() {
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
if (_status == 'success' || _status == 'verifying') return;
try {
final res = await CRUD().postWallet(link: AppLink.checkMtnStatus, payload: {'invoice_number': widget.invoiceNumber});
if (res != 'failure' && res['status'] == 'success' && res['invoice_status'] == 'completed') {
timer.cancel();
if (mounted) setState(() => _status = 'success');
}
} catch (_) {}
});
}
Future<void> _submitProof() async {
if (_proofController.text.trim().isEmpty) {
Get.snackbar('Error'.tr, 'Please paste the transfer message'.tr, backgroundColor: Colors.red);
return;
}
setState(() => _status = 'verifying');
try {
final res = await CRUD().postWallet(link: AppLink.uploadMtnProof, payload: {
'invoice_number': widget.invoiceNumber,
'proof_text': _proofController.text.trim(),
});
if (res != 'failure' && res['status'] == 'success') {
if (mounted) setState(() => _status = 'success');
} else {
if (mounted) setState(() => _status = 'error');
Get.defaultDialog(title: 'Failed'.tr, content: Text(res['message']?.toString() ?? 'Verification failed'));
}
} catch (e) {
if (mounted) setState(() => _status = 'error');
Get.defaultDialog(title: 'Error'.tr, content: Text('Error uploading proof'.tr));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(title: const Text("MTN Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: _status == 'success' ? _buildSuccessUI() : _buildWaitingUI(),
),
),
);
}
Widget _buildWaitingUI() {
final currencyFormat = NumberFormat.decimalPattern('ar_SY');
return SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.blue.shade800, Colors.blue.shade600]), borderRadius: BorderRadius.circular(16)),
child: Column(
children: [
const Text("المبلغ المطلوب", style: TextStyle(color: Colors.white70, fontSize: 14)),
const SizedBox(height: 5),
Text("${currencyFormat.format(widget.amount)} ل.س", style: const TextStyle(color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold)),
],
),
),
const SizedBox(height: 25),
AnimatedBuilder(
animation: _blinkController,
builder: (context, child) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: _colorAnimation.value ?? Colors.red, width: 3.0), boxShadow: [BoxShadow(color: (_colorAnimation.value ?? Colors.red).withOpacity(0.4), blurRadius: _shadowAnimation.value, spreadRadius: 2)]),
child: Column(
children: [
const Text("يرجى تحويل المبلغ إلى الرقم التالي:", textAlign: TextAlign.center, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: widget.mtnNumber));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text("تم نسخ الرقم ✅", textAlign: TextAlign.center), backgroundColor: Colors.green.shade600));
},
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.shade200)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(widget.mtnNumber, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 2.0)),
const Icon(Icons.copy, color: Colors.blue),
],
),
),
),
],
),
);
},
),
const SizedBox(height: 30),
const Text("بعد إتمام التحويل، يرجى نسخ رسالة البنك النصية ولصقها هنا للتحقق التلقائي:", textAlign: TextAlign.center, style: TextStyle(fontSize: 14)),
const SizedBox(height: 10),
TextField(
controller: _proofController,
maxLines: 4,
decoration: InputDecoration(
hintText: "قم بلصق نص رسالة التحويل هنا...",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.white,
),
),
const SizedBox(height: 15),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue.shade800, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
onPressed: _status == 'verifying' ? null : _submitProof,
child: _status == 'verifying'
? const CircularProgressIndicator(color: Colors.white)
: const Text("تحقق من الدفع", style: TextStyle(fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold)),
),
),
const SizedBox(height: 20),
const Text("جاري فحص الفاتورة تلقائياً كل 5 ثوانٍ...", style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
);
}
Widget _buildSuccessUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.verified_rounded, color: Colors.green, size: 100),
const SizedBox(height: 20),
const Text("تم الدفع بنجاح!", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 40),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16)),
onPressed: () { Get.back(); Get.back(); },
child: const Text("متابعة", style: TextStyle(fontSize: 18)),
),
),
],
);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:siro_rider/constant/currency.dart';
import 'dart:math' as math;
import 'dart:typed_data';
@@ -191,7 +192,7 @@ class _HistoryCard extends StatelessWidget {
Text('Total Price'.tr,
style: AppStyle.title.copyWith(fontSize: 15)),
Text(
'${ride['price']} ${'SYP'.tr}',
'${ride['price']} ${CurrencyHelper.currency}',
style: AppStyle.headTitle.copyWith(
fontSize: 20, color: AppColor.primaryColor),
),
@@ -559,7 +560,7 @@ class _RideDetailSheetState extends State<_RideDetailSheet> {
color:
AppColor.writeColor.withOpacity(0.55))),
const SizedBox(height: 2),
Text('${ride['price']} ${'SYP'.tr}',
Text('${ride['price']} ${CurrencyHelper.currency}',
style: AppStyle.headTitle.copyWith(
fontSize: 22, color: AppColor.primaryColor)),
],