diff --git a/android/app/build.gradle b/android/app/build.gradle index d93fa6f..c61fdff 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -148,8 +148,8 @@ android { // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = 23 targetSdk = flutter.targetSdkVersion - versionCode = 97 - versionName = '1.5.97' + versionCode = 98 + versionName = '1.5.98' multiDexEnabled =true // manifestPlaceholders can be specified here if needed diff --git a/ios/Podfile.lock b/ios/Podfile.lock index caf939e..7975ed8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -28,7 +28,7 @@ PODS: - Firebase/Messaging (= 11.0.0) - firebase_core - Flutter - - FirebaseAppCheckInterop (11.2.0) + - FirebaseAppCheckInterop (11.5.0) - FirebaseAuth (11.0.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) @@ -38,16 +38,16 @@ PODS: - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (~> 3.4) - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.2.0) + - FirebaseAuthInterop (11.5.0) - FirebaseCore (11.0.0): - FirebaseCoreInternal (~> 11.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.2.0): + - FirebaseCoreExtension (11.4.1): - FirebaseCore (~> 11.0) - - FirebaseCoreInternal (11.2.0): + - FirebaseCoreInternal (11.5.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" - - FirebaseInstallations (11.2.0): + - FirebaseInstallations (11.4.0): - FirebaseCore (~> 11.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) @@ -356,13 +356,13 @@ SPEC CHECKSUMS: firebase_auth: 16ac5db3d064db837ecd845080d7e18e4be7c66d firebase_core: ceec591a66629daaee82d3321551692c4a871493 firebase_messaging: 15d8b557010f3bb7b98d0302e1c7c8fbcd244425 - FirebaseAppCheckInterop: ea21450529cf0ebd132788dd8916a0269abc684f + FirebaseAppCheckInterop: d265d9f4484e7ec1c591086408840fdd383d1213 FirebaseAuth: d5cf28be74d7e82257f6a3f717509eff70d3cf4a - FirebaseAuthInterop: 47c09558af5d1b31f16fb352387c72d4804f4a24 + FirebaseAuthInterop: 1219bee9b23e6ebe84c256a0d95adab53d11c331 FirebaseCore: 3cf438f431f18c12cdf2aaf64434648b63f7e383 - FirebaseCoreExtension: cda74ddfb001224bd8fd1d6e74698b4ed07803de - FirebaseCoreInternal: 0c569513412da9f3b31bd0b340013bbee8f295c5 - FirebaseInstallations: 771177d89d6c451dc6e50085ec82e2fc77ed0a4a + FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e + FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604 + FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414 FirebaseMessaging: d2d1d9c62c46dd2db49a952f7deb5b16ad2c9742 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0f46ccb..382ae34 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -467,6 +467,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -594,6 +595,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -651,6 +653,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index bef9833..44be063 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -41,11 +41,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 69 + 70 CFBundleSignature ???? CFBundleVersion - 4.3.69 + 4.3.70 NSHumanReadableCopyright FirebaseAppDelegateProxyEnabled diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 803a4bb..d823265 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -171,6 +171,7 @@ class AppLink { //-----------------mishwari------------------ static String addMishwari = "$ride/mishwari/add.php"; + static String cancelMishwari = "$ride/mishwari/cancel.php"; static String getMishwari = "$ride/mishwari/get.php"; //-----------------DriverOrder------------------ diff --git a/lib/controller/firebase/local_notification.dart b/lib/controller/firebase/local_notification.dart index e471e04..73ba3ff 100644 --- a/lib/controller/firebase/local_notification.dart +++ b/lib/controller/firebase/local_notification.dart @@ -113,6 +113,104 @@ class NotificationController extends GetxController { print('Notifications scheduled successfully for the next 7 days'); } + void scheduleNotificationsForTimeSelected( + String title, String message, String tone, DateTime timeSelected) async { + final AndroidNotificationDetails android = AndroidNotificationDetails( + 'high_importance_channel', + 'High Importance Notifications', + importance: Importance.max, + priority: Priority.high, + sound: RawResourceAndroidNotificationSound(tone), + ); + + const DarwinNotificationDetails ios = DarwinNotificationDetails( + sound: 'default', + presentAlert: true, + presentBadge: true, + presentSound: true, + ); + + final NotificationDetails details = + NotificationDetails(android: android, iOS: ios); + + // Check for the exact alarm permission on Android 12 and above + if (Platform.isAndroid) { + if (await Permission.scheduleExactAlarm.isDenied) { + if (await Permission.scheduleExactAlarm.request().isGranted) { + print('SCHEDULE_EXACT_ALARM permission granted'); + } else { + print('SCHEDULE_EXACT_ALARM permission denied'); + return; + } + } + } + + // Schedule notifications for 10 and 30 minutes before the timeSelected + await _scheduleNotificationForTimeVIP( + timeSelected.subtract(const Duration(minutes: 10)), // 10 minutes before + title, + message, + details, + 1, // Unique ID for 10-minute before notification + ); + + await _scheduleNotificationForTimeVIP( + timeSelected.subtract(const Duration(minutes: 30)), // 30 minutes before + title, + message, + details, + 2, // Unique ID for 30-minute before notification + ); + + print('Notifications scheduled successfully for the time selected'); + } + + Future _scheduleNotificationForTimeVIP( + DateTime scheduledDate, + String title, + String message, + NotificationDetails details, + int notificationId, + ) async { + // Initialize and set Cairo timezone + tz.initializeTimeZones(); + var cairoLocation = tz.getLocation('Africa/Cairo'); + + final now = tz.TZDateTime.now(cairoLocation); + + // Convert to Cairo time + tz.TZDateTime scheduledTZDateTime = + tz.TZDateTime.from(scheduledDate, cairoLocation); + + // Check if 10 minutes before the scheduled time is in the past + if (scheduledTZDateTime + .subtract(const Duration(minutes: 10)) + .isBefore(now)) { + // If the 10 minutes before the scheduled time is in the past, don't schedule + print( + 'Scheduled time minus 10 minutes is in the past. Skipping notification.'); + return; // Skip this notification + } + + print('Current time (Cairo): $now'); + print('Scheduling notification for: $scheduledTZDateTime'); + + await _flutterLocalNotificationsPlugin.zonedSchedule( + notificationId, // Unique ID for each notification + title, + message, + scheduledTZDateTime, + details, + androidScheduleMode: AndroidScheduleMode.exact, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + matchDateTimeComponents: + null, // Don't repeat automatically; we handle manually + ); + + print('Notification scheduled successfully for: $scheduledTZDateTime'); + } + Future _scheduleNotificationForTime( int dayOffset, int hour, @@ -138,7 +236,7 @@ class NotificationController extends GetxController { // If the scheduled time is in the past, move it to the next day if (scheduledDate.isBefore(now)) { - scheduledDate = scheduledDate.add(Duration(days: 1)); + scheduledDate = scheduledDate.add(const Duration(days: 1)); } print('Current time (Cairo): $now'); diff --git a/lib/controller/home/map_passenger_controller.dart b/lib/controller/home/map_passenger_controller.dart index f17c4f5..1a79e5b 100644 --- a/lib/controller/home/map_passenger_controller.dart +++ b/lib/controller/home/map_passenger_controller.dart @@ -5,6 +5,7 @@ import 'dart:math' as math; import 'dart:ui'; import 'package:SEFER/constant/univeries_polygon.dart'; import 'package:SEFER/controller/firebase/local_notification.dart'; +import 'package:SEFER/views/widgets/mysnakbar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_confetti/flutter_confetti.dart'; import 'package:vector_math/vector_math.dart' show radians, degrees; @@ -42,6 +43,7 @@ import '../functions/crud.dart'; import '../functions/launch.dart'; import '../functions/secure_storage.dart'; import '../payment/payment_controller.dart'; +import 'vip_waitting_page.dart'; class MapPassengerController extends GetxController { bool isLoading = true; @@ -3069,20 +3071,41 @@ class MapPassengerController extends GetxController { } Future getPlaces() async { + var languageCode; + +// Check if `placeDestinationController.text` contains English characters + if (RegExp(r'[a-zA-Z]').hasMatch(placeDestinationController.text)) { + languageCode = 'en'; + } else { + languageCode = 'ar'; + } var url = // '${AppLink.googleMapsLink}place/nearbysearch/json?location=${mylocation.longitude}&radius=25000&language=ar&keyword=&key=${placeController.text}${AK.mapAPIKEY}'; - '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${placeDestinationController.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=50000&language=ar&key=${AK.mapAPIKEY.toString()}'; - + '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${placeDestinationController.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}'; + print(url); var response = await CRUD().getGoogleApi(link: url, payload: {}); + Log.print('response: ${response}'); placesDestination = response['results']; update(); } Future getPlacesStart() async { + var languageCode = wayPoint0Controller.text; + + // Regular expression to check for English alphabet characters + final englishRegex = RegExp(r'[a-zA-Z]'); + + // Check if text contains English characters + if (englishRegex.hasMatch(languageCode)) { + languageCode = 'en'; + } else { + languageCode = 'ar'; + } + var url = // '${AppLink.googleMapsLink}place/nearbysearch/json?location=${mylocation.longitude}&radius=25000&language=ar&keyword=&key=${placeController.text}${AK.mapAPIKEY}'; - '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${placeStartController.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=50000&language=ar&key=${AK.mapAPIKEY.toString()}'; + '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${placeStartController.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}'; var response = await CRUD().getGoogleApi(link: url, payload: {}); @@ -3091,15 +3114,45 @@ class MapPassengerController extends GetxController { } Future getPlacesListsWayPoint(int index) async { + var languageCode = wayPoint0Controller.text; + + // Regular expression to check for English alphabet characters + final englishRegex = RegExp(r'[a-zA-Z]'); + + // Check if text contains English characters + if (englishRegex.hasMatch(languageCode)) { + languageCode = 'en'; + } else { + languageCode = 'ar'; + } + var url = - '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${wayPoint0Controller.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=50000&language=ar&key=${AK.mapAPIKEY.toString()}'; + '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${wayPoint0Controller.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}'; - var response = await CRUD().getGoogleApi(link: url, payload: {}); + try { + var response = await CRUD().getGoogleApi(link: url, payload: {}); - wayPoint0 = response['results']; - placeListResponseAll[index] = response['results']; - update(); + if (response != null && response['results'] != null) { + wayPoint0 = response['results']; + placeListResponseAll[index] = response['results']; + update(); + } else { + print('Error: Invalid response from Google Places API'); + } + } catch (e) { + print('Error fetching places: $e'); + } } + // Future getPlacesListsWayPoint(int index) async { + // var url = + // '${AppLink.googleMapsLink}place/nearbysearch/json?keyword=${wayPoint0Controller.text}&location=${passengerLocation.latitude},${passengerLocation.longitude}&radius=80000&language=${}&key=${AK.mapAPIKEY.toString()}'; + + // var response = await CRUD().getGoogleApi(link: url, payload: {}); + + // wayPoint0 = response['results']; + // placeListResponseAll[index] = response['results']; + // update(); + // } LatLng fromString(String location) { List parts = location.split(','); @@ -3677,7 +3730,7 @@ class MapPassengerController extends GetxController { isLoading = false; update(); var url = - ('${AppLink.googleMapsLink}directions/json?&language=ar&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}'); + ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang) ?? 'ar'}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}'); var response = await CRUD().getGoogleApi(link: url, payload: {}); data = response['routes'][0]['legs']; @@ -4488,15 +4541,51 @@ class MapPassengerController extends GetxController { } List driversForMishwari = []; + Future selectDriverAndCarForMishwariTrip() async { - var res = await CRUD() - .get(link: AppLink.selectDriverAndCarForMishwariTrip, payload: {}); - if (res != 'failure') { - var d = jsonDecode(res); - driversForMishwari = d['message']; - update(); - } else { - return 'No driver available now try later time\nthanks for using our app' + // Calculate the bounds for 20km + double latitudeOffset = 0.1795; // 20km range in latitude + double longitudeOffset = 0.2074; // 20km range in longitude + + // Calculate bounding box based on passenger's location + double southwestLat = passengerLocation.latitude - latitudeOffset; + double northeastLat = passengerLocation.latitude + latitudeOffset; + double southwestLon = passengerLocation.longitude - longitudeOffset; + double northeastLon = passengerLocation.longitude + longitudeOffset; + + // Create the payload with calculated bounds + var payload = { + 'southwestLat': southwestLat.toString(), + 'northeastLat': northeastLat.toString(), + 'southwestLon': southwestLon.toString(), + 'northeastLon': northeastLon.toString(), + }; + + try { + // Fetch data from the API + var res = await CRUD().get( + link: AppLink.selectDriverAndCarForMishwariTrip, payload: payload); + + if (res != 'failure') { + // Check if response is valid JSON + try { + var d = jsonDecode(res); + driversForMishwari = d['message']; + Log.print('driversForMishwari: ${driversForMishwari}'); + update(); + } catch (e) { + // Handle invalid JSON format + print("Error decoding JSON: $e"); + return 'Server returned invalid data. Please try again later.'; + } + } else { + return 'No driver available now, try again later. Thanks for using our app.' + .tr; + } + } catch (e) { + // Handle network or other exceptions + print("Error fetching data: $e"); + return 'There was an issue connecting to the server. Please try again later.' .tr; } } @@ -4519,22 +4608,23 @@ class MapPassengerController extends GetxController { // changeCashConfirmPageShown(); } + var driverIdVip = ''; Future saveTripData( Map driver, DateTime tripDateTime) async { try { // Prepare trip data Map tripData = { - 'id': driver['id'].toString(), // Ensure the id is a string + 'id': driver['driver_id'].toString(), // Ensure the id is a string 'phone': driver['phone'], 'gender': driver['gender'], 'name': driver['NAME'], 'name_english': driver['name_english'], 'address': driver['address'], - 'religion': driver['religion'], + 'religion': driver['religion'] ?? 'UnKnown', 'age': driver['age'].toString(), // Convert age to String - 'education': driver['education'], - 'license_type': driver['license_type'], - 'national_number': driver['national_number'], + 'education': driver['education'] ?? 'UnKnown', + 'license_type': driver['license_type'] ?? 'UnKnown', + 'national_number': driver['national_number'] ?? 'UnKnown', 'car_plate': driver['car_plate'], 'make': driver['make'], 'model': driver['model'], @@ -4546,12 +4636,12 @@ class MapPassengerController extends GetxController { 'token': driver['token'], 'rating': driver['rating'].toString(), // Convert rating to String 'countRide': - driver['countRide'].toString(), // Convert countRide to String + driver['ride_count'].toString(), // Convert countRide to String 'passengerId': box.read(BoxName.passengerID), 'timeSelected': tripDateTime.toIso8601String(), 'status': 'pending', }; - // Log.print('tripData: $tripData'); + Log.print('tripData: $tripData'); // Send data to server var response = @@ -4560,33 +4650,52 @@ class MapPassengerController extends GetxController { if (response != 'failure') { // Trip saved successfully - Get.snackbar('Success'.tr, 'Trip booked successfully'.tr); + // Get.snackbar('Success'.tr, 'Trip booked successfully'.tr); var id = response['message'].toString(); if (AppLink.endPoint != AppLink.seferCairoServer) { await CRUD().post( link: "${AppLink.endPoint}/ride/mishwari/add.php", payload: tripData); } + driverIdVip = driver['driver_id']; + + DateTime timeSelected = DateTime.parse(tripDateTime.toIso8601String()); + Get.find().scheduleNotificationsForTimeSelected( + "Your trip is scheduled".tr, + "Don't forget your ride!".tr, + "tone1", + timeSelected); // Optionally, set up local notification or send a push notification - // await setLocalNotification(tripDateTime); - await FirebaseMessagesController().sendNotificationToDriverMAP( - 'OrderVIP', - rideId.toString(), - driver['token'].toString(), - [ - id, - driver['id'], - passengerLocation.latitude.toString(), - passengerLocation.longitude.toString(), - box.read(BoxName.name).toString(), - box.read(BoxName.passengerID).toString(), - box.read(BoxName.phone).toString(), - box.read(BoxName.email).toString(), - box.read(BoxName.passengerPhotoUrl).toString(), - box.read(BoxName.tokenFCM).toString(), - driver['token'].toString(), - ], - 'order.wav'); + + // await FirebaseMessagesController().sendNotificationToDriverMAP( + // 'OrderVIP', + // rideId.toString(), + // driver['token'].toString(), + // [ + // id, + // driver['id'], + // passengerLocation.latitude.toString(), + // passengerLocation.longitude.toString(), + // box.read(BoxName.name).toString(), + // box.read(BoxName.passengerID).toString(), + // box.read(BoxName.phone).toString(), + // box.read(BoxName.email).toString(), + // box.read(BoxName.passengerPhotoUrl).toString(), + // box.read(BoxName.tokenFCM).toString(), + // driver['token'].toString(), + // ], + // 'order.wav'); + if (response['message'] == "Trip updated successfully") { + mySnackbarSuccess("Trip updated successfully".tr); + // FirebaseMessagesController().sendNotificationToDriverMAP( + // 'Order VIP Canceld'.tr, + // 'Passenger cancel order'.tr, + // token, + // [], + // 'cancel.wav', + // ); + } + Get.to(() => const VipWaittingPage()); } else { throw Exception('Failed to save trip'); } @@ -4598,6 +4707,22 @@ class MapPassengerController extends GetxController { } } + cancelVip(String token, tripId) async { + // FirebaseMessagesController().sendNotificationToDriverMAP( + // 'Order VIP Canceld'.tr, + // 'Passenger cancel order'.tr, + // token, + // [], + // 'cancel.wav', + // ); + var res = await CRUD() + .post(link: AppLink.cancelMishwari, payload: {'id': tripId}); + if (res != 'failur') { + Get.back(); + mySnackbarSuccess('You canceled VIP trip'.tr); + } + } + initilizeGetStorage() async { if (box.read(BoxName.addWork) == null) { box.write(BoxName.addWork, 'addWork'); diff --git a/lib/controller/home/vip_waitting_page.dart b/lib/controller/home/vip_waitting_page.dart new file mode 100644 index 0000000..3f5a096 --- /dev/null +++ b/lib/controller/home/vip_waitting_page.dart @@ -0,0 +1,190 @@ +import 'dart:convert'; + +import 'package:SEFER/constant/colors.dart'; +import 'package:SEFER/constant/style.dart'; +import 'package:SEFER/controller/home/map_passenger_controller.dart'; +import 'package:SEFER/views/widgets/elevated_btn.dart'; +import 'package:SEFER/views/widgets/mycircular.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_font_icons/flutter_font_icons.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +import '../../constant/links.dart'; +import '../functions/crud.dart'; + +class VipWaittingPage extends StatelessWidget { + const VipWaittingPage({super.key}); + + @override + Widget build(BuildContext context) { + Get.put(VipOrderController()); + return Scaffold( + appBar: AppBar( + title: Text("Waiting VIP".tr), + ), + body: GetBuilder(builder: (vipOrderController) { + var data = vipOrderController.tripData[0]; + + // Function to get the localized status string + String getLocalizedStatus(String status) { + switch (status) { + case 'pending': + return 'pending'.tr; + case 'accepted': + return 'accepted'.tr; + case 'rejected': + return 'rejected'.tr; + default: + return 'unknown'.tr; // Fallback for unexpected statuses + } + } + +// Function to get the appropriate status color + Color getStatusColor(String status) { + switch (status) { + case 'pending': + return Colors.yellow; + case 'accepted': + return Colors.green; + case 'rejected': + return Colors.red; + default: + return Colors.grey; // Default color for unknown statuses + } + } + + return vipOrderController.isLoading + ? const MyCircularProgressIndicator() + : Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + margin: const EdgeInsets.all(16), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${'Driver Name:'.tr} ${data['name']}", + style: AppStyle.title, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${'Car Plate:'.tr} ${data['car_plate']}", + style: AppStyle.title, + ), + Text( + "${'Car Make:'.tr} ${data['make']}", + style: AppStyle.title, + ), + Text( + "${'Car Model:'.tr} ${data['model']}", + style: AppStyle.title, + ), + Text( + "${"Car Color:".tr} ${data['color']}", + style: AppStyle.title, + ), + ], + ), + SizedBox( + width: 100, + height: 100, + child: Icon(Fontisto.car, + size: 80, + color: Color(int.parse(data['color_hex'] + .replaceFirst('#', '0xff'))))), + ], + ), + // Text( + // "${'Driver Phone:'.tr} ${data['phone']}", + // style: AppStyle.title, + // ), + const SizedBox(height: 12), + const Divider(), + const SizedBox(height: 12), + + Container( + color: getStatusColor( + data['status']), // Correctly assigns a Color + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "${'Trip Status:'.tr} ${getLocalizedStatus(data['status'])}", // Uses the String function + style: const TextStyle( + fontSize: 16, + ), + ), + ), + ), + Text( + "${'Scheduled Time:'.tr} ${DateFormat('yyyy-MM-dd hh:mm a').format(DateTime.parse(data['timeSelected']))}", + style: const TextStyle(fontSize: 16), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + MyElevatedButton( + title: "Cancel Trip".tr, + kolor: AppColor.redColor, + onPressed: () { + Get.find().cancelVip( + data['token'].toString(), + data['id'].toString(), + ); + }, + ), + // MyElevatedButton( + // title: "Accept Trip".tr, + // kolor: AppColor.greenColor, + // onPressed: () { + // // Add your cancel trip logic here + // }, + // ), + ], + ), + ], + ), + ), + ); + }), + ); + } +} + +class VipOrderController extends GetxController { + bool isLoading = false; + final arguments = Get.arguments; + late String body; + List tripData = []; + + fetchOrder() async { + isLoading = true; + update(); + var res = await CRUD().get(link: AppLink.getMishwari, payload: { + 'driverId': Get.find().driverIdVip.toString(), + }); + isLoading = false; + update(); + if (res != 'failure') { + tripData = jsonDecode(res)['message']; + update(); + } + } + + @override + void onInit() async { + fetchOrder(); + super.onInit(); + } +} diff --git a/lib/controller/local/translations.dart b/lib/controller/local/translations.dart index 2f75fa8..3d96535 100644 --- a/lib/controller/local/translations.dart +++ b/lib/controller/local/translations.dart @@ -4,6 +4,32 @@ class MyTranslation extends Translations { @override Map> get keys => { "ar": { + "Driver Name:": "اسم السائق:", + "Car Plate:": "رقم اللوحة:", + "Order Cancelled": "تم إلغاء الطلب", + 'You canceled VIP trip': "ألغيت الرحلة", + "Passenger cancelled order": "الراكب قام بإلغاء الطلب", + "Your trip is scheduled": "رحلتك مجدولة", + "Don't forget your ride!": "لا تنسَ رحلتك!", + "Trip updated successfully": "تم تحديث الرحلة بنجاح", + "Car Make:": "ماركة السيارة:", + "Car Model:": "طراز السيارة:", "Car Color:": "لون السيارة:", + "Driver Phone:": "رقم هاتف السائق:", + 'Pre-booking': 'احجز مسبقًا', "Waiting VIP": "انتظار VIP", + "Driver List": "قائمة السائقين", "Confirm Trip": "تأكيد الرحلة", + "Select date and time of trip": "حدد تاريخ ووقت الرحلة", + "Date and Time Picker": "اختيار التاريخ والوقت", + "Trip Status:": "حالة الرحلة:", "pending": "قيد الانتظار", + "accepted": "تم القبول", + "rejected": "تم الرفض", + "Scheduled Time:": "الوقت المحدد:", + "No drivers available": "لا يوجد سائقين متاحين", + "Please try again in a few moments": + "يرجى المحاولة مرة أخرى بعد قليل", + "Unknown Driver": "سائق غير معروف", + "rides": "الرحلات", + "The reason is": "السبب هو", + "User does not have a wallet #1652": "المستخدم ليس لديه محفظة ", "Price of trip": "سعر الرحلة", "For Speed and Delivery trips, the price is calculated dynamically. For Comfort trips, the price is based on time and distance": "بالنسبة لرحلات السرعة والتوصيل، يتم حساب السعر ديناميكياً. بالنسبة لرحلات الراحة، يتم حساب السعر بناءً على الوقت والمسافة", diff --git a/lib/controller/payment/payment_controller.dart b/lib/controller/payment/payment_controller.dart index 4aa3bd7..44b1eb7 100644 --- a/lib/controller/payment/payment_controller.dart +++ b/lib/controller/payment/payment_controller.dart @@ -19,6 +19,7 @@ import '../../constant/colors.dart'; import '../../constant/info.dart'; import '../../constant/links.dart'; import '../../main.dart'; +import '../../print.dart'; import '../functions/crud.dart'; import '../functions/toast.dart'; import 'paymob/paymob_wallet.dart'; @@ -697,7 +698,7 @@ class PaymentController extends GetxController { billingData: PaymobBillingDataWallet(), onPayment: (PaymobResponseWallet response) {}, ); - + // Log.print('response.message!: ${response!.responseCode!}'); // if (response!.success == true && response.responseCode == '200') { if (response!.responseCode.toString() == '200' && response.success == true) { diff --git a/lib/views/home/map_widget.dart/car_details_widget_to_go.dart b/lib/views/home/map_widget.dart/car_details_widget_to_go.dart index 52d5a4c..eb9840e 100644 --- a/lib/views/home/map_widget.dart/car_details_widget_to_go.dart +++ b/lib/views/home/map_widget.dart/car_details_widget_to_go.dart @@ -190,7 +190,8 @@ class CarDetailsTypeToChoose extends StatelessWidget { .totalPassengerRayehGai .toStringAsFixed( 1) - : '50', + : 'Pre-booking' + .tr, style: AppStyle.title.copyWith(fontSize: 20), ), diff --git a/lib/views/home/map_widget.dart/left_main_menu_icons.dart b/lib/views/home/map_widget.dart/left_main_menu_icons.dart index 1d9caad..bb77e2d 100644 --- a/lib/views/home/map_widget.dart/left_main_menu_icons.dart +++ b/lib/views/home/map_widget.dart/left_main_menu_icons.dart @@ -1,17 +1,11 @@ -import 'dart:math'; - -import 'package:SEFER/views/auth/login_page.dart'; -import 'package:SEFER/views/auth/sms_verfy_page.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; -import '../../../constant/box_name.dart'; import '../../../constant/colors.dart'; -import '../../../constant/notification.dart'; -import '../../../controller/firebase/local_notification.dart'; +import '../../../controller/functions/crud.dart'; import '../../../controller/functions/tts.dart'; import '../../../controller/home/map_passenger_controller.dart'; -import '../../../main.dart'; +import '../../../controller/home/vip_waitting_page.dart'; GetBuilder leftMainMenuIcons() { final textToSpeechController = Get.put(TextToSpeechController()); @@ -88,135 +82,23 @@ GetBuilder leftMainMenuIcons() { const SizedBox( width: 5, ), - // AnimatedContainer( - // duration: const Duration(microseconds: 200), - // width: controller.widthMapTypeAndTraffic, - // decoration: BoxDecoration( - // color: AppColor.secondaryColor, - // border: Border.all(), - // borderRadius: BorderRadius.circular(15)), - // child: IconButton( - // onPressed: () async { - // Get.to(SmsSignupEgypt()); - // }, - // icon: const Icon( - // Icons.voice_chat, - // size: 29, - // ), - // ), - // ), - // AnimatedContainer( - // duration: const Duration(microseconds: 200), - // width: controller.widthMapTypeAndTraffic, - // decoration: BoxDecoration( - // color: AppColor.secondaryColor, - // border: Border.all(), - // borderRadius: BorderRadius.circular(15)), - // child: IconButton( - // onPressed: () async { - // Get.to(SmsSignupEgypt()); - // List d = [ - // "30.003028,31.2419628", - // "30.0955661,31.2665336", - // "160.00", - // "25.92", - // "1488", - // "16.93", - // "114243034311436865474", - // "113172279072358305645", - // "hamza ayed", - // "rlMbi4Hc8L1STMPE99iPKqK4Gddwv8r9qZOCadsz9qTEJZ6KLEE9ruTJI6N8dKfK4CXez5pme5WIs14-1QGo29s07fQOniZgIlJV5XFL3yqzPRSUmn3", - // "+201023248456", - // "1 min", - // "1 m", - // "false", - // "QwUMoyUtZ0J3oR6yXKUavrB_gBl9npUZe-qZtax-Raq4QBbdKv0AmtLKm0BfBd6N_592HBv4CVa41ii4122W3hr-BCUKKzJhzZcK8m0YjbWbtpvgJRD8uD_nuMk9", - // "0", - // "238", - // "false", - // "114243034311436865474", - // "1488", - // "startEnd", - // "30.049307749732176,31.274291574954987", - // "", - // "", - // "", - // "", - // "17.73", - // "0", - // "hamzaayedflutter@gmail.com", - // "الفسطاط، حي مصر القديمة، مصر", - // " الزاوية الحمراء، محافظة القاهرة، مصر", - // "Speed", - // "8", - // "5.00" - // ]; - - // FirebaseMessagesController() - // .sendNotificationToAnyWithoutData( - // 'Cancel'.tr, - // "How much longer will you be?".tr, - // 'fKBBB4_1R0q18-byySHUeG:APA91bHk2RmjjMt6eKr7KQnqh4CK02yW3H5E8g_beVcQFgiCG50j9KCtSU1O8PtvS_gA5xuJLhaorDV9AeslcyLFJFf302tICKMiKgsDP5pWkF5WXNw0-4NsoD-BnJxf0-Do9Vs1Zbpq', - // // d, - // 'ding.wav', - // ); - - // Get.to(SmsSignupEgypt()); - // Log.print( - // 'getUpdatedRideForDriverApply: ${Get.find().driverToken}'); - // Get.find() - // .firstTimeRunToGetCoupon('SEFER25', '1 WEEEK', '25%'); - // }, - // icon: const Icon( - // Icons.chat, - // size: 29, - // ), - // ), - // ), - // // AnimatedContainer( - // duration: const Duration(microseconds: 200), - // width: controller.widthMapTypeAndTraffic, - // decoration: BoxDecoration( - // color: AppColor.secondaryColor, - // border: Border.all(), - // borderRadius: BorderRadius.circular(15)), - // child: IconButton( - // onPressed: () async { - // await CRUD().allMethodForAI( - // 'name,fullName,address,idNumber,cardId,dob', - // AppLink.uploadEgypt, - // 'idFront'); - // - // // await ImageController().choosImage( - // // 'https://api.sefer.live/sefer/uploadEgypt.php', - // // 'FrontId'); - // AC credentials = AC(); - // String apiKey = 'zjujl_qvo_fwjfgjlXrXlBl'; - // String convertedStringN = credentials.c( - // credentials.c(credentials.c(apiKey, cs), cC), cn); - - // String retrievedStringS = credentials.r( - // credentials.r(credentials.r(convertedStringN, cn), cC), - // cs); - // // - // if (retrievedStringS == apiKey) { - // print('convertedStringN --- $convertedStringN'); - // print('retrievedStringS ---$retrievedStringS'); - // print('same'); - // } - // - // // await Get.find() - // // .payWithPayMob(context, '1100', 'EGP'); - // // Initiates a payment with a card using the FlutterPaymob instance - // - // }, - // icon: const Icon( - // // Get.put(AudioRecorderController()).isRecording - // Icons.start, - // size: 29, - // ), - // ), - // ), + AnimatedContainer( + duration: const Duration(microseconds: 200), + width: controller.widthMapTypeAndTraffic, + decoration: BoxDecoration( + color: AppColor.secondaryColor, + border: Border.all(), + borderRadius: BorderRadius.circular(15)), + child: IconButton( + onPressed: () async { + Get.to(() => const VipWaittingPage()); + }, + icon: const Icon( + Icons.voice_chat, + size: 29, + ), + ), + ), ], ); })), diff --git a/lib/views/home/map_widget.dart/select_driver_mishwari.dart b/lib/views/home/map_widget.dart/select_driver_mishwari.dart index adda2c2..dfaee4b 100644 --- a/lib/views/home/map_widget.dart/select_driver_mishwari.dart +++ b/lib/views/home/map_widget.dart/select_driver_mishwari.dart @@ -8,6 +8,7 @@ import 'package:get/get.dart'; import '../../../constant/api_key.dart'; import '../../../constant/links.dart'; +import '../../../print.dart'; class CupertinoDriverListWidget extends StatelessWidget { MapPassengerController mapPassengerController = @@ -16,170 +17,207 @@ class CupertinoDriverListWidget extends StatelessWidget { Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( - middle: Text('Driver List'.tr), + middle: Text('Driver List'.tr), // Ensure text is properly localized ), child: SafeArea( - child: ListView.separated( - itemCount: mapPassengerController.driversForMishwari.length, - separatorBuilder: (context, index) => const Divider(height: 1), - itemBuilder: (context, index) { - var driver = mapPassengerController.driversForMishwari[index]; - return Container( - decoration: AppStyle.boxDecoration1, - child: CupertinoListTile( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - leading: CircleAvatar( - radius: 25, - backgroundImage: NetworkImage( - '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg', - ), - child: Builder( - builder: (context) { - return Image.network( - '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg', - fit: BoxFit.cover, - loadingBuilder: (BuildContext context, Widget child, - ImageChunkEvent? loadingProgress) { - if (loadingProgress == null) { - return child; // Image is loaded - } else { - return Center( - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - (loadingProgress.expectedTotalBytes ?? 1) - : null, - ), - ); - } - }, - errorBuilder: (BuildContext context, Object error, - StackTrace? stackTrace) { - return const Icon( - Icons.person, // Icon to show when image fails to load - size: 25, // Adjust the size as needed - color: AppColor.blueColor, // Color for the error icon - ); - }, - ); - }, - ), - ), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${driver['NAME'].toString().split(' ')[0]} ${driver['NAME'].toString().split(' ')[1]}', - style: const TextStyle(fontWeight: FontWeight.bold), + child: mapPassengerController.driversForMishwari.isEmpty + ? Center( + child: Text( + 'No drivers available at the moment. Please try again later.' + .tr, + style: const TextStyle( + fontSize: 18, // Adjust the size as needed + fontWeight: FontWeight.w600, + color: CupertinoColors.inactiveGray, // Customize color + ), + textAlign: TextAlign.center, // Center-align the text ), - Text('${'Age'.tr}: ${driver['age'].toString()}'), - Row( - children: [ - const Icon(CupertinoIcons.star_fill, - size: 16, color: CupertinoColors.systemYellow), - const SizedBox(width: 4), - Text(driver['rating']?.toStringAsFixed(1) ?? 'N/A'.tr), - const SizedBox(width: 8), - Text('${'Rides'.tr}: ${driver['countRide']}'), - ], - ), - ], - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), - Text('${'Plate'.tr}: ${driver['car_plate']}'), - ], - ), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text('${'Education'.tr}: ${driver['education']}'), - // ], - // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox( - // width: Get.width * .3, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${'Color'.tr}: ${driver['color']}'), - const SizedBox(width: 8), - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: driver['color_hex'].toString() == 'null' - ? Colors.amber - : hexToColor( - driver['color_hex'].toString()), - borderRadius: BorderRadius.circular(4), - border: Border.all(), - ), - ), - ], + ) + : ListView.separated( + itemCount: mapPassengerController.driversForMishwari.length, + separatorBuilder: (context, index) => + const Divider(height: 1), + itemBuilder: (context, index) { + var driver = + mapPassengerController.driversForMishwari[index]; + return Container( + decoration: AppStyle.boxDecoration1, + child: CupertinoListTile( + padding: const EdgeInsets.symmetric( + vertical: 4, horizontal: 8), + leading: CircleAvatar( + radius: 25, + backgroundImage: NetworkImage( + '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg', + ), + child: Builder( + builder: (context) { + return Image.network( + '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg', + fit: BoxFit.cover, + loadingBuilder: (BuildContext context, + Widget child, + ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) { + return child; // Image is loaded + } else { + return Center( + child: CircularProgressIndicator( + value: loadingProgress + .expectedTotalBytes != + null + ? loadingProgress + .cumulativeBytesLoaded / + (loadingProgress + .expectedTotalBytes ?? + 1) + : null, + ), + ); + } + }, + errorBuilder: (BuildContext context, + Object error, StackTrace? stackTrace) { + return const Icon( + Icons + .person, // Icon to show when image fails to load + size: 25, // Adjust the size as needed + color: AppColor + .blueColor, // Color for the error icon + ); + }, + ); + }, + ), ), - ), - ], - ), - ], - ), - onTap: () { - // Handle driver selection - Get.defaultDialog( - title: '${'Selected driver'.tr}: ${driver['NAME']}', - content: Column( - children: [ - Column( + title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), - Text('${'Plate'.tr}: ${driver['car_plate']}'), + '${driver['NAME'].toString().split(' ')[0]} ${driver['NAME'].toString().split(' ')[1]}', + style: + const TextStyle(fontWeight: FontWeight.bold), + ), + Text('${'Age'.tr}: ${driver['age'].toString()}'), Row( - mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('${'Color'.tr}: ${driver['color']}'), + const Icon(CupertinoIcons.star_fill, + size: 16, + color: CupertinoColors.systemYellow), + const SizedBox(width: 4), + Text(driver['rating']?.toStringAsFixed(1) ?? + 'N/A'.tr), const SizedBox(width: 8), - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: - driver['color_hex'].toString() == 'null' - ? Colors.amber - : hexToColor( - driver['color_hex'].toString()), - borderRadius: BorderRadius.circular(4), - border: Border.all(), + Text('${'Rides'.tr}: ${driver['ride_count']}'), + ], + ), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), + Text('${'Plate'.tr}: ${driver['car_plate']}'), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + // width: Get.width * .3, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text('${'Color'.tr}: ${driver['color']}'), + const SizedBox(width: 8), + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: driver['color_hex'] + .toString() == + 'null' + ? Colors.amber + : hexToColor(driver['color_hex'] + .toString()), + borderRadius: + BorderRadius.circular(4), + border: Border.all(), + ), + ), + ], ), ), ], ), ], ), - ], - ), - confirm: MyElevatedButton( - title: 'OK'.tr, - onPressed: () { - Get.back(); - showDateTimePickerDialog(driver); - })); - print('${'Selected driver'.tr}: ${driver['NAME']}'); - // Get.back(); // Close the dialog - }, - ), - ); - }, - )), + onTap: () { + Log.print(' driver["id"]: ${driver['driver_id']}'); + Get.find().driverIdVip = + driver['driver_id']; + + // Handle driver selection + Get.defaultDialog( + title: + '${'Selected driver'.tr}: ${driver['NAME']}', + content: Column( + children: [ + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), + Text( + '${'Plate'.tr}: ${driver['car_plate']}'), + Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + '${'Color'.tr}: ${driver['color']}'), + const SizedBox(width: 8), + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: driver['color_hex'] + .toString() == + 'null' + ? Colors.amber + : hexToColor( + driver['color_hex'] + .toString()), + borderRadius: + BorderRadius.circular(4), + border: Border.all(), + ), + ), + ], + ), + ], + ), + ], + ), + confirm: MyElevatedButton( + title: 'OK'.tr, + onPressed: () { + Get.back(); + showDateTimePickerDialog(driver); + })); + print('${'Selected driver'.tr}: ${driver['NAME']}'); + // Get.back(); // Close the dialog + }, + ), + ); + }, + )), ); } @@ -233,7 +271,7 @@ class CupertinoDriverListWidget extends StatelessWidget { Get.defaultDialog( barrierDismissible: false, - title: 'select date and time of trip'.tr, + title: "Select date and time of trip".tr, content: SizedBox( // height: 400, // Adjust height as needed width: double.maxFinite, @@ -253,6 +291,13 @@ class CupertinoDriverListWidget extends StatelessWidget { await mapPassengerController.saveTripData(driver, selectedDateTime); }, ), + cancel: MyElevatedButton( + kolor: AppColor.redColor, + title: 'Cancel'.tr, + onPressed: () { + Get.back(); + }, + ), ); } } @@ -263,10 +308,10 @@ class DateTimePickerWidget extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar( + navigationBar: CupertinoNavigationBar( transitionBetweenRoutes: false, automaticallyImplyLeading: false, - middle: Text('Date and Time Picker'), + middle: Text('Date and Time Picker'.tr), ), child: SafeArea( child: Column( diff --git a/lib/views/home/my_wallet/passenger_wallet_dialoge.dart b/lib/views/home/my_wallet/passenger_wallet_dialoge.dart index 2cd8009..89b9933 100644 --- a/lib/views/home/my_wallet/passenger_wallet_dialoge.dart +++ b/lib/views/home/my_wallet/passenger_wallet_dialoge.dart @@ -2,10 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/colors.dart'; -import 'package:SEFER/constant/style.dart'; import 'package:SEFER/controller/functions/toast.dart'; import 'package:SEFER/controller/payment/payment_controller.dart'; -import 'package:SEFER/views/widgets/elevated_btn.dart'; import '../../../main.dart'; @@ -138,6 +136,8 @@ void showPaymentOptions(BuildContext context, PaymentController controller) { child: Text('💰 Pay with Wallet'.tr), onPressed: () { if (controller.selectedAmount != 0) { + controller.isLoading = true; + controller.update(); controller.payWithPayMobWallet( context, controller.selectedAmount.toString(), @@ -148,6 +148,8 @@ void showPaymentOptions(BuildContext context, PaymentController controller) { await controller.getPassengerWallet(); }, ); + controller.isLoading = false; + controller.update(); } else { Toast.show(context, '⚠️ You need to choose an amount!'.tr, AppColor.redColor);