Files
intaleq/lib/controller/home/map_passenger_controller.dart
Hamza-Ayed 7595be8067 25-09-22/1
2025-09-22 17:28:19 +03:00

5972 lines
210 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' show Random, atan2, cos, max, min, pi, pow, sin, sqrt;
import 'dart:math' as math;
import 'dart:ui';
import 'dart:convert';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:http/http.dart' as http;
import 'package:Intaleq/constant/univeries_polygon.dart';
import 'package:Intaleq/controller/firebase/local_notification.dart';
import 'package:Intaleq/controller/functions/encrypt_decrypt.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_confetti/flutter_confetti.dart';
import 'package:vector_math/vector_math.dart' show radians, degrees;
import 'package:Intaleq/controller/functions/tts.dart';
import 'package:Intaleq/views/home/map_page_passenger.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
import 'package:intl/intl.dart';
import 'package:location/location.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
import 'package:Intaleq/views/home/map_widget.dart/form_serch_multiy_point.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../constant/table_names.dart';
import '../../main.dart';
import '../../models/model/locations.dart';
import '../../models/model/painter_copoun.dart';
import '../../print.dart';
import '../../views/home/map_widget.dart/cancel_raide_page.dart';
import '../../views/home/map_widget.dart/car_details_widget_to_go.dart';
import '../../views/home/map_widget.dart/searching_captain_window.dart';
import '../../views/home/map_widget.dart/select_driver_mishwari.dart';
import '../../views/widgets/elevated_btn.dart';
import '../../views/widgets/error_snakbar.dart';
import '../../views/widgets/mydialoug.dart';
import '../firebase/firbase_messge.dart';
import '../functions/audio_record1.dart';
import '../functions/crud.dart';
import '../functions/launch.dart';
import '../functions/package_info.dart';
import '../functions/secure_storage.dart';
import '../payment/payment_controller.dart';
import 'deep_link_controller.dart';
import 'device_tier.dart';
import 'vip_waitting_page.dart';
class MapPassengerController extends GetxController {
bool isLoading = true;
TextEditingController placeDestinationController = TextEditingController();
TextEditingController increasFeeFromPassenger = TextEditingController();
TextEditingController placeStartController = TextEditingController();
TextEditingController wayPoint0Controller = TextEditingController();
TextEditingController wayPoint1Controller = TextEditingController();
TextEditingController wayPoint2Controller = TextEditingController();
TextEditingController wayPoint3Controller = TextEditingController();
TextEditingController wayPoint4Controller = TextEditingController();
TextEditingController sosPhonePassengerProfile = TextEditingController();
TextEditingController whatsAppLocationText = TextEditingController();
TextEditingController messageToDriver = TextEditingController();
final sosFormKey = GlobalKey<FormState>();
final promoFormKey = GlobalKey<FormState>();
final messagesFormKey = GlobalKey<FormState>();
final increaseFeeFormKey = GlobalKey<FormState>();
List data = [];
List<LatLng> bounds = [];
List placesStart = [];
List<String> driversToken = [];
LatLng previousLocationOfDrivers = const LatLng(0, 0);
double angleDegrees = 0;
LatLng currentLocationOfDrivers = const LatLng(0, 0);
List<TextEditingController> allTextEditingPlaces = [];
List placesDestination = [];
List wayPoint0 = [];
List wayPoint1 = [];
List wayPoint2 = [];
List wayPoint3 = [];
List wayPoint4 = [];
final firebaseMessagesController =
Get.isRegistered<FirebaseMessagesController>()
? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController());
List<List<dynamic>> placeListResponseAll = [];
List<Widget> placeListResponse = [
formSearchPlaces(0),
formSearchPlaces(1),
formSearchPlaces(2),
formSearchPlaces(3),
];
LatLngBounds? boundsdata;
List<Marker> markers = [];
List<Polyline> polyLines = [];
late LatLng passengerLocation = const LatLng(32, 34);
late LatLng newMyLocation = const LatLng(32.115295, 36.064773);
late LatLng newStartPointLocation = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation0 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation1 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation2 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation3 = const LatLng(32.115295, 36.064773);
late LatLng newPointLocation4 = const LatLng(32.115295, 36.064773);
late LatLng myDestination;
List<LatLng> polylineCoordinates = [];
List<LatLng> polylineCoordinates0 = [];
List<LatLng> polylineCoordinates1 = [];
List<LatLng> polylineCoordinates2 = [];
List<LatLng> polylineCoordinates3 = [];
List<LatLng> polylineCoordinates4 = [];
List<List<LatLng>> polylineCoordinatesPointsAll = [];
List carsLocationByPassenger = [];
List<LatLng> driverCarsLocationToPassengerAfterApplied = [];
BitmapDescriptor markerIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor tripIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor startIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor endIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor motoIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor ladyIcon = BitmapDescriptor.defaultMarker;
double height = 150;
DateTime currentTime = DateTime.now();
final location = Location();
late LocationData currentLocation;
double heightMenu = 0;
double widthMenu = 0;
double heightPickerContainer = 90;
double heightPointsPageForRider = 0;
double mainBottomMenuMapHeight = Get.height * .2;
double wayPointSheetHeight = 0;
String stringRemainingTimeToPassenger = '';
String stringRemainingTimeDriverWaitPassenger5Minute = '';
bool isDriverInPassengerWay = false;
bool isDriverArrivePassenger = false;
bool startLocationFromMap = false;
bool isAnotherOreder = false;
bool isWhatsAppOrder = false;
bool passengerStartLocationFromMap = false;
bool workLocationFromMap = false;
bool homeLocationFromMap = false;
bool isPassengerRideLocationWidget = false;
bool startLocationFromMap0 = false;
bool startLocationFromMap1 = false;
bool startLocationFromMap2 = false;
bool startLocationFromMap3 = false;
bool startLocationFromMap4 = false;
List startLocationFromMapAll = [];
double latePrice = 0;
double fuelPrice = 0;
double heavyPrice = 0;
double naturePrice = 0;
bool heightMenuBool = false;
String statusRide = 'wait';
String statusRideVip = 'wait';
bool statusRideFromStart = false;
bool isPickerShown = false;
bool isPointsPageForRider = false;
bool isBottomSheetShown = false;
bool mapType = false;
bool mapTrafficON = false;
bool isCancelRidePageShown = false;
bool isCashConfirmPageShown = false;
bool isPaymentMethodPageShown = false;
bool isRideFinished = false;
bool rideConfirm = false;
bool isMarkersShown = false;
bool isMainBottomMenuMap = true;
late Timer markerReloadingTimer2;
late Timer markerReloadingTimer1;
late int durationToPassenger = 0;
bool isWayPointSheet = false;
bool isWayPointStopsSheet = false;
bool isWayPointStopsSheetUtilGetMap = false;
double heightBottomSheetShown = 0;
double cashConfirmPageShown = 250;
late String driverId = '';
late String gender = '';
double widthMapTypeAndTraffic = 50;
double paymentPageShown = Get.height * .6;
late LatLng southwest;
late LatLng northeast;
List<CarLocationModel> carLocationsModels = <CarLocationModel>[];
var dataCarsLocationByPassenger;
var datadriverCarsLocationToPassengerAfterApplied;
CarLocation? nearestCar;
late Timer markerReloadingTimer;
bool shouldFetch = true; // Flag to determine if fetch should be executed
int selectedPassengerCount = 1;
double progress = 0;
double progressTimerToPassengerFromDriverAfterApplied = 0;
double progressTimerDriverWaitPassenger5Minute = 0;
int durationTimer = 9;
int durationToRide = 0;
int remainingTime = 25;
int remainingTimeToPassengerFromDriverAfterApplied = 60;
int remainingTimeDriverWaitPassenger5Minute = 60;
int timeToPassengerFromDriverAfterApplied = 0;
Timer? timerToPassengerFromDriverAfterApplied;
bool rideTimerBegin = false;
double progressTimerRideBegin = 0;
int remainingTimeTimerRideBegin = 60;
String stringRemainingTimeRideBegin = '';
String hintTextStartPoint = 'Search for your Start point'.tr;
String hintTextwayPoint0 = 'Search for waypoint'.tr;
String hintTextwayPoint1 = 'Search for waypoint'.tr;
String hintTextwayPoint2 = 'Search for waypoint'.tr;
String hintTextwayPoint3 = 'Search for waypoint'.tr;
String hintTextwayPoint4 = 'Search for waypoint'.tr;
String currentLocationString = 'Current Location'.tr;
String currentLocationString0 = 'Current Location'.tr;
String currentLocationString1 = 'Add Location 1'.tr;
String currentLocationString2 = 'Add Location 2'.tr;
String currentLocationString3 = 'Add Location 3'.tr;
String currentLocationString4 = 'Add Location 4'.tr;
String placesCoordinate0 = ''.tr;
String placesCoordinate1 = ''.tr;
String placesCoordinate2 = ''.tr;
String placesCoordinate3 = ''.tr;
String placesCoordinate4 = ''.tr;
List<String> currentLocationStringAll = [];
List<String> hintTextwayPointStringAll = [];
var placesCoordinate = <String>[];
String hintTextDestinationPoint = 'Select your destination'.tr;
late String rideId = 'yet';
bool noCarString = false;
bool isCashSelectedBeforeConfirmRide = false;
bool isPassengerChosen = false;
bool isSearchingWindow = false;
bool currentLocationToFormPlaces = false;
bool currentLocationToFormPlaces0 = false;
bool currentLocationToFormPlaces1 = false;
bool currentLocationToFormPlaces2 = false;
bool currentLocationToFormPlaces3 = false;
bool currentLocationToFormPlaces4 = false;
List currentLocationToFormPlacesAll = [];
late String driverToken = '';
int carsOrder = 0;
int wayPointIndex = 0;
late double kazan;
String? mapAPIKEY;
late double totalME = 0;
late double tax = 0;
late double totalPassenger = 0;
late double totalCostPassenger = 0;
late double totalPassengerComfort = 0;
late double totalPassengerComfortDiscount = 0;
late double totalPassengerElectricDiscount = 0;
late double totalPassengerLadyDiscount = 0;
late double totalPassengerSpeedDiscount = 0;
late double totalPassengerBalashDiscount = 0;
late double totalPassengerRaihGaiDiscount = 0;
late double totalPassengerScooter = 0;
late double totalPassengerVan = 0;
late double totalDriver = 0;
late double averageDuration = 0;
late double costDuration = 0;
late double costDistance = 0;
late double distance = 0;
late double duration = 0;
late Duration durationToAdd;
late DateTime newTime = DateTime.now();
int hours = 0;
int minutes = 0;
// --- إضافة جديدة: للوصول إلى وحدة التحكم بالروابط ---
final DeepLinkController _deepLinkController = Get.find();
// ------------------------------------------------
void onChangedPassengerCount(int newValue) {
selectedPassengerCount = newValue;
update();
}
void onChangedPassengersChoose() {
isPassengerChosen = true;
update();
}
void getCurrentLocationFormString() async {
currentLocationToFormPlaces = true;
currentLocationString = 'Waiting for your location'.tr;
await getLocation();
currentLocationString = passengerLocation.toString();
newStartPointLocation = passengerLocation;
update();
}
List<String> coordinatesWithoutEmpty = [];
void getMapPointsForAllMethods() async {
clearPolyline();
isMarkersShown = false;
isWayPointStopsSheetUtilGetMap = false;
isWayPointSheet = false;
durationToRide = 0;
distanceOfDestination = 0;
wayPointSheetHeight = 0;
remainingTime = 25;
haveSteps = true;
// Filter out empty value
coordinatesWithoutEmpty =
placesCoordinate.where((coord) => coord.isNotEmpty).toList();
latestPosition = LatLng(
double.parse(coordinatesWithoutEmpty.last.split(',')[0]),
double.parse(coordinatesWithoutEmpty.last.split(',')[1]));
for (var i = 0; i < coordinatesWithoutEmpty.length; i++) {
if ((i + 1) < coordinatesWithoutEmpty.length) {
await getMapPoints(
coordinatesWithoutEmpty[i].toString(),
coordinatesWithoutEmpty[i + 1].toString(),
i,
);
if (i == 0) {
startNameAddress = data[0]['start_address'];
}
if (i == coordinatesWithoutEmpty.length) {
endNameAddress = data[0]['end_address'];
}
}
}
// isWayPointStopsSheet = false;
if (haveSteps) {
String latestWaypoint =
placesCoordinate.lastWhere((coord) => coord.isNotEmpty);
latestPosition = LatLng(
double.parse(latestWaypoint.split(',')[0]),
double.parse(latestWaypoint.split(',')[1]),
);
}
updateCameraForDistanceAfterGetMap();
changeWayPointStopsSheet();
bottomSheet();
showBottomSheet1();
update();
}
void convertHintTextStartNewPlaces(int index) {
if (placesStart.isEmpty) {
hintTextStartPoint = 'Search for your Start point'.tr;
update();
} else {
var res = placesStart[index];
hintTextStartPoint = res['displayName']?['text'] ??
res['formattedAddress'] ??
'Unknown Place';
double? lat = res['location']?['latitude'];
double? lng = res['location']?['longitude'];
if (lat != null && lng != null) {
newStartPointLocation = LatLng(lat, lng);
}
update();
}
}
void convertHintTextPlaces(int index, var res) {
if (placeListResponseAll[index].isEmpty) {
placeListResponseAll[index] = res;
hintTextwayPointStringAll[index] = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPointStringAll[index] = res['name'];
currentLocationStringAll[index] = res['name'];
placesCoordinate[index] =
'${res['geometry']['location']['lat']},${res['geometry']['location']['lng']}';
placeListResponseAll[index] = [];
allTextEditingPlaces[index].clear();
// double lat = wayPoint0[index]['geometry']['location']['lat'];
// double lng = wayPoint0[index]['geometry']['location']['lng'];
// newPointLocation0 = LatLng(lat, lng);
update();
Get.back();
}
}
increaseFeeByPassengerAndReOrder() async {
if (increaseFeeFormKey.currentState!.validate()) {
if (double.parse(increasFeeFromPassenger.text) > totalPassenger) {
totalPassenger = double.parse(increasFeeFromPassenger.text);
Get.back();
if (rideId != 'yet') {
Log.print('rideId from increase: $rideId');
notifyAvailableDriversAgain();
await CRUD().post(link: AppLink.updateDriverOrder, payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'waiting'
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/update.php",
payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'waiting'
});
}
await CRUD().post(link: AppLink.updateRides, payload: {
"id": rideId.toString(), // Convert to String
"status": 'waiting'
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rides/update.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'waiting'
});
}
CRUD().post(link: AppLink.updateWaitingTrip, payload: {
"id": rideId.toString(), // Convert to String
"status": 'wait'
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'wait'
});
}
tick = 0;
}
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 4000);
// confirmRideForAllDriverAvailable();
increaseForSameRideAndDelay();
}
}
}
void convertHintTextPlaces1(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint1 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint1 = wayPoint1[index]['name'];
currentLocationString1 = wayPoint1[index]['name'];
double lat = wayPoint1[index]['geometry']['location']['lat'];
double lng = wayPoint1[index]['geometry']['location']['lng'];
newPointLocation1 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces2(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint2 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint2 = wayPoint2[index]['name'];
currentLocationString2 = wayPoint1[index]['name'];
double lat = wayPoint2[index]['geometry']['location']['lat'];
double lng = wayPoint2[index]['geometry']['location']['lng'];
newPointLocation2 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces3(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint3 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint3 = wayPoint3[index]['name'];
currentLocationString3 = wayPoint1[index]['name'];
double lat = wayPoint3[index]['geometry']['location']['lat'];
double lng = wayPoint3[index]['geometry']['location']['lng'];
newPointLocation3 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces4(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint4 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint4 = wayPoint4[index]['name'];
currentLocationString4 = wayPoint1[index]['name'];
double lat = wayPoint4[index]['geometry']['location']['lat'];
double lng = wayPoint4[index]['geometry']['location']['lng'];
newPointLocation4 = LatLng(lat, lng);
update();
}
}
void convertHintTextDestinationNewPlaces(int index) {
if (placesDestination.isEmpty) {
hintTextDestinationPoint = 'Search for your destination'.tr;
update();
} else {
var res = placesDestination[index];
// استخراج الاسم من displayName.text أو بديله
hintTextDestinationPoint = res['displayName']?['text'] ??
res['formattedAddress'] ??
'Unknown Place';
// استخراج الإحداثيات
double? lat = res['location']?['latitude'];
double? lng = res['location']?['longitude'];
if (lat != null && lng != null) {
newMyLocation = LatLng(lat, lng);
}
update();
}
}
void convertHintTextDestinationNewPlacesFromRecent(
List recentLocations, int index) {
hintTextDestinationPoint = recentLocations[index]['name'];
double lat = recentLocations[index]['latitude'];
double lng = recentLocations[index]['longitude'];
newMyLocation = LatLng(lat, lng);
update();
}
// final mainBottomMenuMap = GlobalKey<AnimatedContainer>();
void changeBottomSheetShown() {
isBottomSheetShown = !isBottomSheetShown;
heightBottomSheetShown = isBottomSheetShown == true ? 250 : 0;
update();
}
void changeCashConfirmPageShown() {
isCashConfirmPageShown = !isCashConfirmPageShown;
isCashSelectedBeforeConfirmRide = true;
cashConfirmPageShown = isCashConfirmPageShown == true ? 250 : 0;
// to get or sure picker point for origin //todo
// isPickerShown = true;
// clickPointPosition();
update();
}
void changePaymentMethodPageShown() {
isPaymentMethodPageShown = !isPaymentMethodPageShown;
paymentPageShown = isPaymentMethodPageShown == true ? Get.height * .6 : 0;
update();
}
void changeMapType() {
mapType = !mapType;
// heightButtomSheetShown = isButtomSheetShown == true ? 240 : 0;
update();
}
void changeMapTraffic() {
mapTrafficON = !mapTrafficON;
update();
}
void changeisAnotherOreder(bool val) {
isAnotherOreder = val;
update();
}
void changeIsWhatsAppOrder(bool val) {
isWhatsAppOrder = val;
update();
}
void sendSMS(String to) async {
// Get the driver's phone number.
String driverPhone =
(dataCarsLocationByPassenger['message'][carsOrder]['phone'].toString());
// Format the message.
String message =
'Hi! This is ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n I am using ${box.read(AppInformation.appName)} to ride with $passengerName as the driver. $passengerName \nis driving a $model\n with license plate $licensePlate.\n I am currently located at $passengerLocation.\n If you need to reach me, please contact the driver directly at\n\n $driverPhone.';
// Launch the URL to send the SMS.
launchCommunication('sms', to, message);
}
void sendWhatsapp(String to) async {
// Get the driver's phone number.
// String driverPhone = dataCarsLocationByPassenger['message'][carsOrder]['phone'].toString();
// Format the message.
String message =
'${'${'Hi! This is'.tr} ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n${' I am using'.tr}'} ${AppInformation.appName}${' to ride with'.tr} $passengerName${' as the driver.'.tr} $passengerName \n${'is driving a '.tr}$model\n${' with license plate '.tr}$licensePlate.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n $driverPhone.';
// Launch the URL to send the WhatsApp message.
launchCommunication('whatsapp', to, message);
}
void changeCancelRidePageShow() {
showCancelRideBottomSheet();
isCancelRidePageShown = !isCancelRidePageShown;
// : cancelRide();
update();
}
void getDrawerMenu() {
heightMenuBool = !heightMenuBool;
widthMapTypeAndTraffic = heightMenuBool == true ? 0 : 50;
heightMenu = heightMenuBool == true ? 80 : 0;
widthMenu = heightMenuBool == true ? 110 : 0;
update();
}
calcualateDistsanceInMetet(LatLng prev, current) async {
double distance2 = Geolocator.distanceBetween(
prev.latitude,
prev.longitude,
current.latitude,
current.longitude,
);
return distance2;
}
// bool isTimerFromDriverToPassengerAfterAppliedRunning = true;
// int beginRideInterval = 4; // Interval in seconds for getBeginRideFromDriver
// void startTimerFromDriverToPassengerAfterApplied() async {
// int secondsElapsed = 0;
// while (secondsElapsed <= timeToPassengerFromDriverAfterApplied &&
// isTimerFromDriverToPassengerAfterAppliedRunning) {
// await Future.delayed(const Duration(seconds: 1));
// secondsElapsed++;
// progressTimerToPassengerFromDriverAfterApplied =
// secondsElapsed / timeToPassengerFromDriverAfterApplied;
// remainingTimeToPassengerFromDriverAfterApplied =
// timeToPassengerFromDriverAfterApplied - secondsElapsed;
// if (remainingTimeToPassengerFromDriverAfterApplied < 59) {
// if (rideTimerBegin == false) {
// rideTimerBegin = true;
// }
// }
// // Call getBeginRideFromDriver every 4 seconds
// if (secondsElapsed % beginRideInterval == 0) {
// getBeginRideFromDriver();
// uploadPassengerLocation();
// }
// int minutes =
// (remainingTimeToPassengerFromDriverAfterApplied / 60).floor();
// int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60;
// stringRemainingTimeToPassenger =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// update();
// }
// }
StreamController<int> _timerStreamController = StreamController<int>();
Stream<int> get timerStream => _timerStreamController.stream;
bool isTimerFromDriverToPassengerAfterAppliedRunning = true;
bool isTimerRunning = false; // Flag to track if the timer is running
int beginRideInterval = 4; // Interval in seconds for getBeginRideFromDriver
void startTimerFromDriverToPassengerAfterApplied() {
if (isTimerRunning) return; // Prevent duplicate streams
isTimerRunning = true;
int secondsElapsed = 0;
// Start the stream
Timer.periodic(const Duration(seconds: 1), (timer) {
if (secondsElapsed > timeToPassengerFromDriverAfterApplied ||
!isTimerFromDriverToPassengerAfterAppliedRunning) {
timer.cancel();
isTimerRunning = false;
_timerStreamController.close(); // Close the stream when done
return;
}
secondsElapsed++;
_timerStreamController.add(secondsElapsed); // Emit elapsed time
// Calculate progress and remaining time
progressTimerToPassengerFromDriverAfterApplied =
secondsElapsed / timeToPassengerFromDriverAfterApplied;
remainingTimeToPassengerFromDriverAfterApplied =
timeToPassengerFromDriverAfterApplied - secondsElapsed;
// Update remaining time as string
int minutes =
(remainingTimeToPassengerFromDriverAfterApplied / 60).floor();
int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60;
stringRemainingTimeToPassenger =
'$minutes:${seconds.toString().padLeft(2, '0')}';
if (remainingTimeToPassengerFromDriverAfterApplied < 59 &&
!rideTimerBegin) {
rideTimerBegin = true;
}
// Call periodic functions
if (secondsElapsed % beginRideInterval == 0) {
getBeginRideFromDriver();
uploadPassengerLocation();
}
update(); // Notify listeners
});
}
// void startTimerFromDriverToPassengerAfterApplied() async {
// if (isTimerRunning) return; // Exit if timer is already running
// isTimerRunning = true; // Set the flag to true
// int secondsElapsed = 0;
// while (secondsElapsed <= timeToPassengerFromDriverAfterApplied &&
// isTimerFromDriverToPassengerAfterAppliedRunning) {
// await Future.delayed(const Duration(seconds: 1));
// secondsElapsed++;
// progressTimerToPassengerFromDriverAfterApplied =
// secondsElapsed / timeToPassengerFromDriverAfterApplied;
// remainingTimeToPassengerFromDriverAfterApplied =
// timeToPassengerFromDriverAfterApplied - secondsElapsed;
// if (remainingTimeToPassengerFromDriverAfterApplied < 59) {
// if (rideTimerBegin == false) {
// rideTimerBegin = true;
// }
// }
// // Call getBeginRideFromDriver every 4 seconds
// if (secondsElapsed % beginRideInterval == 0) {
// getBeginRideFromDriver();
// uploadPassengerLocation();
// }
// int minutes =
// (remainingTimeToPassengerFromDriverAfterApplied / 60).floor();
// int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60;
// stringRemainingTimeToPassenger =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// update();
// }
// isTimerRunning = false; // Reset the flag when timer completes
// }
// Remove the getBeginRideFromDriverForDuration function as it's no longer needed
// Function to stop the timer
void stopTimerFromDriverToPassengerAfterApplied() {
isTimerFromDriverToPassengerAfterAppliedRunning = false;
update();
}
void startTimerDriverWaitPassenger5Minute() async {
stopTimerFromDriverToPassengerAfterApplied();
isDriverArrivePassenger = true;
isDriverInPassengerWay = false;
timeToPassengerFromDriverAfterApplied = 0;
update();
for (int i = 0; i <= 300; i++) {
await Future.delayed(const Duration(seconds: 1));
progressTimerDriverWaitPassenger5Minute = i / 300;
remainingTimeDriverWaitPassenger5Minute = 300 - i;
int minutes = (remainingTimeDriverWaitPassenger5Minute / 60).floor();
int seconds = remainingTimeDriverWaitPassenger5Minute % 60;
stringRemainingTimeDriverWaitPassenger5Minute =
'$minutes:${seconds.toString().padLeft(2, '0')}';
update();
}
}
// Create a StreamController to manage the timer values
final timerController = StreamController<int>();
// Start the timer when the ride begins
void beginRideTimer() {
// Set up the timer to run every second
Timer.periodic(const Duration(seconds: 1), (timer) {
// Update the timer value and notify listeners
timerController.add(timer.tick);
update();
});
}
// Stop the timer when the ride ends
void stopRideTimer() {
timerController.close();
update();
}
late String arrivalTime = '';
void rideIsBeginPassengerTimer() async {
// Calculate arrival time considering current time and duration
DateTime now = DateTime.now();
DateTime arrivalTime1 = now.add(Duration(seconds: durationToRide));
arrivalTime = DateFormat('hh:mm').format(arrivalTime1);
box.write(BoxName.arrivalTime, arrivalTime);
for (int i = 0; i <= durationToRide; i++) {
await Future.delayed(const Duration(seconds: 1));
progressTimerRideBegin = i / durationToRide;
remainingTimeTimerRideBegin = durationToRide - i;
if (i == (durationToRide / 4).round() && (statusRide == 'Begin')) {
NotificationController().showNotification("Record Your Trip".tr,
"You can call or record audio during this trip.".tr, 'tone1');
}
bool sendSOS = false;
if (speed > 100 && sendSOS == false) {
NotificationController().showNotification(
"Warning: Speeding detected!".tr,
'You can call or record audio of this trip'.tr,
'tone1');
Get.defaultDialog(
barrierDismissible: false,
title: "Warning: Speeding detected!".tr,
titleStyle: AppStyle.title,
content: Text(
"We noticed the speed is exceeding 100 km/h. Please slow down for your safety. If you feel unsafe, you can share your trip details with a contact or call the police using the red SOS button."
.tr,
style: AppStyle.title,
),
confirm: MyElevatedButton(
title: "Share Trip Details".tr,
onPressed: () {
Get.back();
// Implement sharing trip details logic here
String message = "**Emergency SOS from Passenger:**\n";
// Get trip details from GetX or relevant provider
String origin = passengerLocation.toString();
String destination = myDestination.toString();
String driverName = passengerName;
String driverCarPlate = licensePlate;
// Add trip details to the message
message += "* ${'Origin'.tr}: $origin\n";
message += "* ${'Destination'.tr}: $destination\n";
message += "* ${'Driver Name'.tr}: $driverName\n";
message += "* ${'Driver Car Plate'.tr}: $driverCarPlate\n\n";
message += "* ${'Driver phone'.tr}:$driverPhone\n\n";
// Add any additional information you want to include (optional)
// - Example: current location (using GetX LocationController)
message +=
"${'Current Location'.tr}:https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude} \n";
// Append a call to action
message += "Please help! Contact me as soon as possible.".tr;
// Launch WhatsApp communication with the constructed message
launchCommunication(
'whatsapp', box.read(BoxName.sosPhonePassenger), message);
sendSOS = true;
},
kolor: AppColor.redColor,
),
cancel: MyElevatedButton(
title: "Cancel".tr,
onPressed: () {
Get.back();
},
kolor: AppColor.greenColor,
),
);
}
int minutes = (remainingTimeTimerRideBegin / 60).floor();
int seconds = remainingTimeTimerRideBegin % 60;
stringRemainingTimeRideBegin =
'$minutes:${seconds.toString().padLeft(2, '0')}';
update();
}
// rideTimerBegin = false;
// isRideFinished = true;
// update();
}
int progressTimerRideBeginVip = 0;
int elapsedTimeInSeconds = 0; // Timer starts from 0
String stringElapsedTimeRideBegin = '0:00';
String stringElapsedTimeRideBeginVip = '0:00';
bool rideInProgress = true; // To control when to stop the timer
void rideIsBeginPassengerTimerVIP() async {
rideInProgress = true; // Start the ride timer
bool sendSOS = false;
while (rideInProgress) {
await Future.delayed(const Duration(seconds: 1));
// Increment elapsed time
elapsedTimeInSeconds++;
// Update the time display
int minutes = (elapsedTimeInSeconds / 60).floor();
int seconds = elapsedTimeInSeconds % 60;
stringElapsedTimeRideBeginVip =
'$minutes:${seconds.toString().padLeft(2, '0')}';
// Check for speed and SOS conditions
if (speed > 100 && !sendSOS) {
Get.defaultDialog(
barrierDismissible: false,
title: "Warning: Speeding detected!".tr,
titleStyle: AppStyle.title,
content: Text(
"We noticed the speed is exceeding 100 km/h. Please slow down for your safety. If you feel unsafe, you can share your trip details with a contact or call the police using the red SOS button."
.tr,
style: AppStyle.title,
),
confirm: MyElevatedButton(
title: "Share Trip Details".tr,
onPressed: () {
Get.back();
// Implement sharing trip details logic here
String message = "**Emergency SOS from Passenger:**\n";
// Get trip details from GetX or relevant provider
String origin = passengerLocation.toString();
String destination = myDestination.toString();
String driverName = passengerName;
String driverCarPlate = licensePlate;
// Add trip details to the message
message += "* ${'Origin'.tr}: $origin\n";
message += "* ${'Destination'.tr}: $destination\n";
message += "* ${'Driver Name'.tr}: $driverName\n";
message += "* ${'Driver Car Plate'.tr}: $driverCarPlate\n\n";
message += "* ${'Driver Phone'.tr}: $driverPhone\n\n";
// Add current location
message +=
"${'Current Location'.tr}:https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude} \n";
// Append a call to action
message += "Please help! Contact me as soon as possible.".tr;
// Launch WhatsApp communication
launchCommunication(
'whatsapp', box.read(BoxName.sosPhonePassenger), message);
sendSOS = true;
},
kolor: AppColor.redColor,
),
cancel: MyElevatedButton(
title: "Cancel".tr,
onPressed: () {
Get.back();
},
kolor: AppColor.greenColor,
),
);
}
// Update the UI
update();
}
}
void tripFinishedFromDriver() {
isRideFinished = true;
rideTimerBegin = false;
statusRideVip = 'Finished';
box.write(BoxName.arrivalTime, '');
remainingTimeTimerRideBegin = 0;
box.write(BoxName.passengerWalletTotal, '0');
update();
if (box.read(BoxName.parentTripSelected) == true) {
firebaseMessagesController.sendNotificationToPassengerToken(
"Finish Monitor".tr,
"Finish Monitor".tr,
box.read(BoxName.tokenParent),
[],
'order1.wav',
);
box.write(BoxName.parentTripSelected, false);
box.remove(BoxName.tokenParent);
}
}
// bool isBeginRideFromDriver = false;
// void getBeginRideFromDriver() async {
// try {
// if (isBeginRideFromDriver) return; // Prevent duplicate streams
// isBeginRideFromDriver = true;
// var res = await CRUD()
// .get(link: AppLink.getRideStatusBegin, payload: {'ride_id': rideId});
// if (res != 'failure') {
// var decode = jsonDecode(res);
// // if (decode['data']['status'] != 'Apply') {
// if (decode['data']['status'] == 'Begin') {
// timeToPassengerFromDriverAfterApplied = 0;
// remainingTime = 0;
// remainingTimeToPassengerFromDriverAfterApplied = 0;
// remainingTimeDriverWaitPassenger5Minute = 0;
// rideTimerBegin = true;
// statusRide = 'Begin';
// isDriverInPassengerWay = false;
// isDriverArrivePassenger = false;
// update();
// // isCancelRidePageShown = true;
// rideIsBeginPassengerTimer();
// runWhenRideIsBegin();
// } else {}
// }
// } catch (e) {
// // Handle the error or perform any necessary actions
// }
// }
StreamController<String> _beginRideStreamController =
StreamController<String>.broadcast();
Stream<String> get beginRideStream => _beginRideStreamController.stream;
bool isBeginRideFromDriverRunning = false;
void getBeginRideFromDriver() {
if (isBeginRideFromDriverRunning) return; // Prevent duplicate streams
isBeginRideFromDriverRunning = true;
Timer.periodic(const Duration(seconds: 1), (timer) async {
try {
var res = await CRUD().get(
link: AppLink.getRideStatusBegin, payload: {'ride_id': rideId});
print(res);
print('1002');
if (res != 'failure') {
var decode = jsonDecode(res);
_beginRideStreamController
.add(decode['data']['status']); // Emit the status
if (decode['data']['status'] == 'Begin') {
// Stop the periodic check
timer.cancel();
isBeginRideFromDriverRunning = false;
timeToPassengerFromDriverAfterApplied = 0;
remainingTime = 0;
remainingTimeToPassengerFromDriverAfterApplied = 0;
remainingTimeDriverWaitPassenger5Minute = 0;
rideTimerBegin = true;
statusRide = 'Begin';
isDriverInPassengerWay = false;
isDriverArrivePassenger = false;
update();
// Trigger additional actions
rideIsBeginPassengerTimer();
runWhenRideIsBegin();
NotificationController().showNotification(
'Trip is begin'.tr,
'The trip has started! Feel free to contact emergency numbers, share your trip, or activate voice recording for the journey'
.tr,
'ding');
}
}
} catch (e) {
// Handle errors
_beginRideStreamController.addError(e);
}
});
}
// Call this method to listen to the stream
void listenToBeginRideStream() {
beginRideStream.listen((status) {
print("Ride status: $status");
// Perform additional actions based on the status
}, onError: (error) {
print("Error in Begin Ride Stream: $error");
});
}
begiVIPTripFromPassenger() async {
timeToPassengerFromDriverAfterApplied = 0;
remainingTime = 0;
isBottomSheetShown = false;
remainingTimeToPassengerFromDriverAfterApplied = 0;
remainingTimeDriverWaitPassenger5Minute = 0;
rideTimerBegin = true;
statusRideVip = 'Begin';
isDriverInPassengerWay = false;
isDriverArrivePassenger = false;
update();
// isCancelRidePageShown = true;
rideIsBeginPassengerTimerVIP();
runWhenRideIsBegin();
}
Map rideStatusFromStartApp = {};
getRideStatusFromStartApp() async {
try {
var res = await CRUD().get(
link: AppLink.getRideStatusFromStartApp,
payload: {'passenger_id': box.read(BoxName.passengerID)});
print(res);
print('1070');
if (res == 'failure') {
print(
"No rides found for the given passenger ID within the last hour.");
}
rideStatusFromStartApp = jsonDecode(res);
if (rideStatusFromStartApp['data']['status'] == 'Begin') {
statusRide = 'Begin';
driverId = rideStatusFromStartApp['data']['driver_id'];
passengerName = rideStatusFromStartApp['data']['driverName'];
driverRate = rideStatusFromStartApp['data']['rateDriver'].toString();
statusRideFromStart = true;
// DateTime endTime =
// DateTime.parse(rideStatusFromStartApp['data']['endtime']);
// DateTime rideTimeStart =
// DateTime.parse(rideStatusFromStartApp['data']['rideTimeStart']);
//
// // Calculate the new end time by adding the duration to the rideTimeStart
// DateTime newEndTime = rideTimeStart.add(
// Duration(seconds: endTime.difference(rideTimeStart).inSeconds));
//
// // Save the new end time in a variable
// var newEndTimeVariable = newEndTime.toString();
update();
Map<String, dynamic> tripData =
box.read(BoxName.tripData) as Map<String, dynamic>;
final points = decodePolyline(
tripData["routes"][0]["overview_polyline"]["points"]);
for (int i = 0; i < points.length; i++) {
double lat = points[i][0].toDouble();
double lng = points[i][1].toDouble();
polylineCoordinates.add(LatLng(lat, lng));
}
var polyline = Polyline(
polylineId: const PolylineId('begin trip'),
points: polylineCoordinates,
width: 10,
color: Colors.blue,
);
polyLines.add(polyline);
timeToPassengerFromDriverAfterApplied = 0;
remainingTime = 0;
remainingTimeToPassengerFromDriverAfterApplied = 0;
remainingTimeDriverWaitPassenger5Minute = 0;
rideTimerBegin = true;
isDriverInPassengerWay = false;
isDriverArrivePassenger = false;
// update();
// isCancelRidePageShown = true;
durationToAdd = tripData['routes'][0]['legs'][0]['duration']['value'];
rideIsBeginPassengerTimer();
runWhenRideIsBegin();
update();
}
} catch (e) {
// Handle the error or perform any necessary actions
}
}
void driverArrivePassenger() {
timeToPassengerFromDriverAfterApplied = 0;
remainingTime = 0;
// isCancelRidePageShown = true;
update();
rideIsBeginPassengerTimer();
// runWhenRideIsBegin();
}
void cancelTimerToPassengerFromDriverAfterApplied() {
timerToPassengerFromDriverAfterApplied?.cancel();
}
void clearPlacesDestination() {
placesDestination = [];
hintTextDestinationPoint = 'Search for your destination'.tr;
update();
}
void clearPlacesStart() {
placesStart = [];
hintTextStartPoint = 'Search for your Start point'.tr;
update();
}
void clearPlaces(int index) {
placeListResponseAll[index] = [];
hintTextwayPointStringAll[index] = 'Search for waypoint'.tr;
update();
}
void clearPlaces1() {
wayPoint1 = [];
hintTextwayPoint1 = 'Search for waypoint'.tr;
update();
}
void clearPlaces2() {
wayPoint2 = [];
hintTextwayPoint2 = 'Search for waypoint'.tr;
update();
}
void clearPlaces3() {
wayPoint3 = [];
hintTextwayPoint3 = 'Search for waypoint'.tr;
update();
}
void clearPlaces4() {
wayPoint4 = [];
hintTextwayPoint4 = 'Search for waypoint'.tr;
update();
}
int selectedReason = -1;
String? cancelNote;
void selectReason(int index, String note) {
selectedReason = index;
cancelNote = note;
update();
}
void getDialog(String title, String? midTitle, VoidCallback onPressed) {
final textToSpeechController = Get.find<TextToSpeechController>();
Get.defaultDialog(
title: title,
titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title,
content: Column(
children: [
IconButton(
onPressed: () async {
await textToSpeechController.speakText(title ?? midTitle!);
},
icon: const Icon(Icons.headphones)),
Text(
midTitle!,
style: AppStyle.title,
)
],
),
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: onPressed,
kolor: AppColor.greenColor,
),
cancel: MyElevatedButton(
title: 'Cancel',
kolor: AppColor.redColor,
onPressed: () {
Get.back();
}));
}
Map<String, double>? extractCoordinatesFromLink(String link) {
try {
// Extract the URL part from the link by finding the first occurrence of "http"
int urlStartIndex = link.indexOf(RegExp(r'https?://'));
if (urlStartIndex == -1) {
throw const FormatException('No URL found in the provided link.');
}
// Extract the URL and clean it
link = link.substring(urlStartIndex).trim();
Uri uri = Uri.parse(link);
// Common coordinate query parameters
List<String> coordinateParams = ['q', 'cp', 'll'];
// Try to extract coordinates from query parameters
for (var param in coordinateParams) {
String? value = uri.queryParameters[param];
if (value != null && (value.contains(',') || value.contains('~'))) {
List<String> coordinates =
value.contains(',') ? value.split(',') : value.split('~');
if (coordinates.length == 2) {
double? latitude = double.tryParse(coordinates[0].trim());
double? longitude = double.tryParse(coordinates[1].trim());
if (latitude != null && longitude != null) {
return {
'latitude': latitude,
'longitude': longitude,
};
}
}
}
}
// Try to extract coordinates from the path
List<String> pathSegments = uri.pathSegments;
for (var segment in pathSegments) {
if (segment.contains(',')) {
List<String> coordinates = segment.split(',');
if (coordinates.length == 2) {
double? latitude = double.tryParse(coordinates[0].trim());
double? longitude = double.tryParse(coordinates[1].trim());
if (latitude != null && longitude != null) {
return {
'latitude': latitude,
'longitude': longitude,
};
}
}
}
}
} catch (e) {
print('Error parsing location link: $e');
}
return null;
}
double latitudeWhatsApp = 0;
double longitudeWhatsApp = 0;
void handleWhatsAppLink(String link) {
Map<String, double>? coordinates = extractCoordinatesFromLink(link);
if (coordinates != null) {
latitudeWhatsApp = coordinates['latitude']!;
longitudeWhatsApp = coordinates['longitude']!;
print(
'Extracted coordinates: Lat: $latitudeWhatsApp, Long: $longitudeWhatsApp');
// Use these coordinates in your app as needed
} else {
print('Failed to extract coordinates from the link');
}
}
void goToWhatappLocation() async {
if (sosFormKey.currentState!.validate()) {
changeIsWhatsAppOrder(true);
Get.back();
handleWhatsAppLink(whatsAppLocationText.text);
myDestination = LatLng(latitudeWhatsApp, longitudeWhatsApp);
await mapController?.animateCamera(CameraUpdate.newLatLng(
LatLng(passengerLocation.latitude, passengerLocation.longitude)));
changeMainBottomMenuMap();
passengerStartLocationFromMap = true;
isPickerShown = true;
update();
}
}
int currentTimeSearchingCaptainWindow = 0;
late String driverPhone = '';
late String driverRate = '';
late String passengerName = '';
late String carColor = '';
late String colorHex = '';
late String carYear = '';
late String model = '';
late String make = '';
late String licensePlate = '';
String driverOrderStatus = 'yet';
bool isDriversTokensSend = false;
Set<String> notifiedDrivers = {};
addRideToNotificationDriverAvailable() async {
await CRUD().post(link: AppLink.addWaitingRide, payload: {
'id': rideId.toString(),
'start_location':
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
'end_location':
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"price": totalPassenger.toStringAsFixed(2),
'passenger_id': box.read(BoxName.passengerID).toString(),
'status': 'waiting',
'carType': box.read(BoxName.carType),
'passengerRate': passengerRate.toStringAsFixed(2),
'price_for_passenger': totalME.toStringAsFixed(2),
'distance': distance.toStringAsFixed(1),
'duration': duration.toStringAsFixed(1),
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(
link: '${AppLink.endPoint}/notificationCaptain/addWaitingRide.php',
payload: {
'id': rideId.toString(),
'start_location':
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
'end_location':
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"price": totalPassenger.toStringAsFixed(2),
'passenger_id': box.read(BoxName.passengerID).toString(),
'status': 'waiting',
'carType': box.read(BoxName.carType),
'passengerRate': passengerRate.toStringAsFixed(2),
'price_for_passenger': totalME.toStringAsFixed(2),
'distance': distance.toStringAsFixed(1),
'duration': duration.toStringAsFixed(0),
});
}
}
// Future<void> confirmRideForAllDriverAvailable1() async {
// // Try to fetch car locations up to 4 times with a 2-second delay
// bool driversFound = false;
// for (int attempt = 0; attempt < 8; attempt++) {
// await getCarsLocationByPassengerAndReloadMarker(
// box.read(BoxName.carType), attempt > 5 ? 4500 : 3000);
// // Check if dataCarsLocationByPassenger is valid and contains drivers
// if (dataCarsLocationByPassenger != 'failure' &&
// dataCarsLocationByPassenger != null &&
// dataCarsLocationByPassenger.containsKey('data') &&
// dataCarsLocationByPassenger['message'] != null) {
// driversFound = true;
// break; // Exit loop if drivers are found
// }
// // Wait 2 seconds before next attempt
// await Future.delayed(const Duration(seconds: 2));
// }
// // If no drivers were found after 4 attempts, show a dialog
// if (!driversFound) {
// Get.dialog(
// BackdropFilter(
// filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
// child: CupertinoAlertDialog(
// title: Text(
// "No Car or Driver Found in your area.".tr,
// style: AppStyle.title.copyWith(
// fontSize: 20,
// fontWeight: FontWeight.bold,
// ),
// ),
// content: Text(
// "No Car or Driver Found in your area.".tr,
// style: AppStyle.title.copyWith(fontSize: 16),
// ),
// actions: [
// CupertinoDialogAction(
// onPressed: () {
// Get.back();
// Get.offAll(() => const MapPagePassenger());
// },
// child: Text('OK'.tr,
// style: const TextStyle(color: AppColor.greenColor)),
// ),
// ],
// ),
// ),
// barrierDismissible: false,
// );
// return;
// }
// // Proceed with the rest of the function if drivers are found
// PaymentController paymentController = Get.find<PaymentController>();
// rideConfirm = true;
// shouldFetch = true;
// isBottomSheetShown = false;
// timeToPassengerFromDriverAfterApplied = 60;
// // Add ride to database
// await CRUD()
// .post(link: "${AppLink.IntaleqCairoServer}/ride/rides/add.php", payload: {
// "start_location":
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// "end_location":
// '${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
// "date": DateTime.now().toString(),
// "time": DateTime.now().toString(),
// "endtime": durationToAdd.toString(),
// "price": totalPassenger.toStringAsFixed(2),
// "passenger_id": box.read(BoxName.passengerID).toString(),
// "driver_id": dataCarsLocationByPassenger['message'][carsOrder]['driver_id']
// .toString(),
// "status": "waiting",
// 'carType': box.read(BoxName.carType),
// "price_for_driver": totalPassenger.toString(),
// "price_for_passenger": totalME.toString(),
// "distance": distance.toString(),
// "paymentMethod": paymentController.isWalletChecked.toString(),
// }).then((value) {
// if (value is String) {
// final parsedValue = jsonDecode(value);
// rideId = parsedValue['message'];
// } else if (value is Map) {
// rideId = value['message'];
// } else {
// Log.print('Unexpected response type: ${value.runtimeType}');
// }
// // Timer to notify drivers every 2 seconds for 5 iterations
// int iteration = 0;
// Timer.periodic(const Duration(seconds: 2), (timer) async {
// if (iteration >= 5) {
// timer.cancel();
// return;
// }
// iteration++;
// // Reload driver locations and notify available drivers
// await getCarsLocationByPassengerAndReloadMarker(
// box.read(BoxName.carType), 3000);
// if (dataCarsLocationByPassenger != null &&
// dataCarsLocationByPassenger.containsKey('data') &&
// dataCarsLocationByPassenger['message'] != null) {
// for (var driverData in dataCarsLocationByPassenger['message']) {
// String driverId = driverData['driver_id'].toString();
// if (!notifiedDrivers.contains(driverId)) {
// notifiedDrivers.add(driverId);
// List<String> body = [
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// '${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
// totalPassenger.toStringAsFixed(2),
// totalDriver.toStringAsFixed(2),
// durationToRide.toString(),
// distance.toStringAsFixed(2),
// driverId.toString(),
// box.read(BoxName.passengerID).toString(),
// box.read(BoxName.name).toString(),
// box.read(BoxName.tokenFCM).toString(),
// box.read(BoxName.phone).toString(),
// durationByPassenger.toString(),
// distanceByPassenger.toString(),
// paymentController.isWalletChecked.toString(),
// driverData['token'].toString(),
// durationToPassenger.toString(),
// rideId.toString(),
// rideTimerBegin.toString(),
// driverId.toString(),
// durationToRide.toString(),
// Get.find<WayPointController>().wayPoints.length > 1
// ? 'haveSteps'
// : 'startEnd',
// placesCoordinate[0],
// placesCoordinate[1],
// placesCoordinate[2],
// placesCoordinate[3],
// placesCoordinate[4],
// costForDriver.toStringAsFixed(2),
// (double.parse(box.read(BoxName.passengerWalletTotal)) < 0
// ? double.parse(box.read(BoxName.passengerWalletTotal))
// .toStringAsFixed(2)
// : '0'),
// box.read(BoxName.email).toString(),
// data[0]['start_address'],
// data[0]['end_address'],
// box.read(BoxName.carType),
// kazan.toStringAsFixed(0),
// passengerRate.toStringAsFixed(2),
// ];
// Log.print('body: ${body}');
// firebaseMessagesController.sendNotificationToDriverMAP(
// 'OrderSpeed',
// rideId,
// driverData['token'].toString(),
// body,
// 'order.wav',
// );
// }
// }
// }
// });
// });
// // If an additional endpoint is available, post data there as well
// if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
// CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
// "start_location":
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// "end_location":
// '${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
// "date": DateTime.now().toString(),
// "time": DateTime.now().toString(),
// "endtime": durationToAdd.toString(),
// "price": totalPassenger.toStringAsFixed(2),
// "passenger_id": box.read(BoxName.passengerID).toString(),
// "driver_id": dataCarsLocationByPassenger['message'][carsOrder]['driver_id']
// .toString(),
// "status": "waiting",
// 'carType': box.read(BoxName.carType),
// "price_for_driver": totalPassenger.toString(),
// "price_for_passenger": totalME.toString(),
// "distance": distance.toString(),
// "paymentMethod": paymentController.isWalletChecked.toString(),
// });
// }
// delayAndFetchRideStatusForAllDriverAvailable(rideId);
// update();
// }
increaseForSameRideAndDelay() async {
reSearchAfterCanceledFromDriver();
// bool driversFound = false;
// for (int attempt = 0; attempt < 8; attempt++) {
// await getCarsLocationByPassengerAndReloadMarker(
// box.read(BoxName.carType), 4500);
// // Check if dataCarsLocationByPassenger is valid and contains drivers
// if (dataCarsLocationByPassenger != 'failure' &&
// dataCarsLocationByPassenger != null &&
// dataCarsLocationByPassenger.containsKey('message') &&
// dataCarsLocationByPassenger['message'] != null) {
// driversFound = true;
// break; // Exit loop if drivers are found
// }
// // Wait 2 seconds before next attempt
// await Future.delayed(const Duration(seconds: 2));
// }
// // If no drivers were found after 4 attempts, show a dialog
// if (!driversFound) {
// Get.dialog(
// BackdropFilter(
// filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
// child: CupertinoAlertDialog(
// title: Text(
// "No Car or Driver Found in your area.".tr,
// style: AppStyle.title.copyWith(
// fontSize: 20,
// fontWeight: FontWeight.bold,
// ),
// ),
// content: Text(
// "No Car or Driver Found in your area.".tr,
// style: AppStyle.title.copyWith(fontSize: 16),
// ),
// actions: [
// CupertinoDialogAction(
// onPressed: () {
// Get.back();
// Get.offAll(() => const MapPagePassenger());
// },
// child: Text('OK'.tr,
// style: const TextStyle(color: AppColor.greenColor)),
// ),
// ],
// ),
// ),
// barrierDismissible: false,
// );
// return;
// }
// PaymentController paymentController = Get.find<PaymentController>();
// rideConfirm = true;
// shouldFetch = true;
// isBottomSheetShown = false;
// timeToPassengerFromDriverAfterApplied = 60;
// // confirmRideForAllDriverAvailable();
// for (var i = 0; i < dataCarsLocationByPassenger['message'].length; i++) {
// List<String> body = [
// '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
// '${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
// totalPassenger.toStringAsFixed(2),
// totalDriver.toStringAsFixed(2),
// durationToRide.toString(),
// distance.toStringAsFixed(2),
// dataCarsLocationByPassenger['message'][i]['driver_id'].toString(),
// box.read(BoxName.passengerID).toString(),
// box.read(BoxName.name).toString(),
// box.read(BoxName.tokenFCM).toString(),
// box.read(BoxName.phone).toString(),
// durationByPassenger.toString(),
// distanceByPassenger.toString(),
// paymentController.isWalletChecked.toString(),
// dataCarsLocationByPassenger['message'][i]['token'].toString(),
// durationToPassenger.toString(),
// rideId.toString(),
// rideTimerBegin.toString(),
// dataCarsLocationByPassenger['message'][i]['driver_id'].toString(),
// durationToRide.toString(),
// Get.find<WayPointController>().wayPoints.length > 1
// ? 'haveSteps'
// : 'startEnd',
// placesCoordinate[0],
// placesCoordinate[1],
// placesCoordinate[2],
// placesCoordinate[3],
// placesCoordinate[4],
// costForDriver.toStringAsFixed(2),
// double.parse(box.read(BoxName.passengerWalletTotal)) < 0
// ? double.parse(box.read(BoxName.passengerWalletTotal))
// .toStringAsFixed(2)
// : '0',
// box.read(BoxName.email).toString(),
// data[0]['start_address'],
// data[0]['end_address'],
// box.read(BoxName.carType),
// kazan.toStringAsFixed(0),
// passengerRate.toStringAsFixed(2),
// ];
// // Log.print('body: ${body}');
// firebaseMessagesController.sendNotificationToDriverMAP(
// 'OrderSpeed',
// rideId.toString(),
// dataCarsLocationByPassenger['message'][i]['token'].toString(),
// body,
// 'order.wav');
// }
}
int tick = 0; // Move tick outside the function to maintain its state
// void delayAndFetchRideStatus(String rideId, carType) {
// Timer.periodic(const Duration(seconds: 1), (timer) async {
// if (shouldFetch) {
// if (remainingTimeToPassengerFromDriverAfterApplied > 0) {
// String res = await getRideStatus(rideId);
// Log.print('tick: $tick');
// String rideStatusDelayed = res.toString();
// if ((rideStatusDelayed == 'waiting' ||
// rideStatusDelayed == 'Refused') &&
// tick >= 15) {
// timer.cancel(); // Stop the current timer
// showAndResearchForCaptain();
// //TODO add to wait
// await getCarsLocationByPassengerAndReloadMarker(carType, 3000);
// // await getNearestDriverByPassengerLocationAPIGOOGLE();
// // getCarForFirstConfirm(carType);
// confirmRideForAllDriverAvailable();
// // delayAndFetchRideStatusForAllDriverAvailable(rideId);
// } else if (rideStatusDelayed == 'Apply' || statusRide == 'Apply') {
// Log.print('rideStatusDelayed == Apply: $rideStatusDelayed');
// // todo play sound
// Get.find<AudioRecorderController>()
// .playSoundFromAssets('assets/start.wav');
// timer.cancel(); // Stop the current timer
// await getUpdatedRideForDriverApply(rideId);
// shouldFetch = false; // Stop further fetches
// statusRide = 'Apply';
// rideConfirm = false;
// isSearchingWindow = false;
// update();
// startTimerFromDriverToPassengerAfterApplied();
// if (box.read(BoxName.carType) == 'Speed' ||
// box.read(BoxName.carType) == 'Awfar Car') {
// NotificationController().showNotification(
// 'The captain is responsible for the route.'.tr,
// 'This price is fixed even if the route changes for the driver.'
// .tr,
// 'ding');
// } else if (box.read(BoxName.carType) == 'Comfort' ||
// box.read(BoxName.carType) == 'Lady') {
// NotificationController().showNotification('Attention'.tr,
// 'The price may increase if the route changes.'.tr, 'ding');
// }
// } else if (rideStatusDelayed == 'Refused') {
// statusRide = 'Refused';
// if (isDriversTokensSend == false) {
// confirmRideForAllDriverAvailable();
// isDriversTokensSend = true;
// } // Start 15-second timer
// }
// //else if (isDriversTokensSend == false) {
// // No need to recall delayAndFetchRideStatus as Timer.periodic is already running
// update();
// // }
// if (tick < 19) {
// tick++;
// } else {
// timer.cancel();
// // Stop the timer if remainingTimeToPassengerFromDriverAfterApplied <= 0
// }
// } else {
// timer.cancel();
// // Stop the timer if remainingTimeToPassengerFromDriverAfterApplied <= 0
// }
// } else {
// timer.cancel(); // Stop the timer if shouldFetch is false
// }
// });
// }
showAndResearchForCaptain() {
Get.snackbar(
"No Captain Accepted Your Order".tr,
"We are looking for a captain but the price may increase to let a captain accept"
.tr,
duration: const Duration(seconds: 5),
backgroundColor: AppColor.yellowColor,
);
isSearchingWindow == true;
update();
}
String driversStatusForSearchWindow = '';
Future<void> confirmRideForAllDriverAvailable() async {
bool driversFound = false;
const maxAttempts = 8;
const attemptDelay = Duration(seconds: 3);
for (int attempt = 0; attempt < maxAttempts; attempt++) {
final reloadDuration = attempt > 5 ? 4500 : 3000;
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), reloadDuration);
// await getNearestDriverByPassengerLocation();
driversStatusForSearchWindow = 'We are search for nearst driver'.tr;
if (isDriversDataValid()) {
driversFound = true;
break;
}
await Future.delayed(attemptDelay);
}
if (!driversFound) {
showNoDriversDialog();
return;
}
driversStatusForSearchWindow = 'Your order is being prepared'.tr;
Log.print('driversStatusForSearchWindow: $driversStatusForSearchWindow');
update();
await postRideDetailsToServer();
driversStatusForSearchWindow = 'Your order sent to drivers'.tr;
await notifyAvailableDrivers();
driversStatusForSearchWindow = 'The drivers are reviewing your request'.tr;
Log.print('driversStatusForSearchWindow: $driversStatusForSearchWindow');
update();
delayAndFetchRideStatusForAllDriverAvailable(rideId);
// update();
}
Future<void> updateConfirmRideForAllDriverAvailable() async {
bool driversFound = false;
const maxAttempts = 8;
const attemptDelay = Duration(seconds: 3);
for (int attempt = 0; attempt < maxAttempts; attempt++) {
final reloadDuration = attempt > 5 ? 4500 : 3000;
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), reloadDuration);
// await getNearestDriverByPassengerLocation();
if (isDriversDataValid()) {
driversFound = true;
break;
}
await Future.delayed(attemptDelay);
}
if (!driversFound) {
showNoDriversDialog();
return;
}
// await postRideDetailsToServer();
delayAndFetchRideStatusForAllDriverAvailable(rideId);
// update();
await notifyAvailableDrivers();
}
bool isDriversDataValid() {
return dataCarsLocationByPassenger != 'failure' &&
dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('message') &&
dataCarsLocationByPassenger['message'] != null;
}
void showNoDriversDialog() {
Get.dialog(
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: CupertinoAlertDialog(
title: Text("No Car or Driver Found in your area.".tr,
style: AppStyle.title
.copyWith(fontSize: 20, fontWeight: FontWeight.bold)),
content: Text("No Car or Driver Found in your area.".tr,
style: AppStyle.title.copyWith(fontSize: 16)),
actions: [
CupertinoDialogAction(
onPressed: () {
Get.back();
Get.offAll(() => const MapPagePassenger());
},
child: Text('OK'.tr,
style: const TextStyle(color: AppColor.greenColor)),
),
],
),
),
barrierDismissible: false,
);
}
Future<void> postRideDetailsToServer() async {
final paymentController = Get.find<PaymentController>();
final payload = constructRidePayload(paymentController);
try {
final response = await CRUD().post(
link: "${AppLink.IntaleqCairoServer}/ride/rides/add.php",
payload: payload);
if (response is String) {
final parsedValue = jsonDecode(response);
rideId = parsedValue['message'];
} else if (response is Map) {
rideId = response['message'];
} else {
Log.print('Unexpected response type: ${response.runtimeType}');
}
} catch (e) {
Log.print('Error posting ride details: $e');
}
}
Map<String, dynamic> constructRidePayload(
PaymentController paymentController) {
final startLocation =
'${data[0]['start_location']['lat']},${data[0]['start_location']['lng']}';
final endLocation =
'${data[0]['end_location']['lat']},${data[0]['end_location']['lng']}';
return {
"start_location": startLocation,
"end_location": endLocation,
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['message'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
};
}
Future<void> notifyAvailableDrivers() async {
int iteration = 0;
const maxIterations = 5;
const iterationDelay = Duration(seconds: 2);
while (iteration < maxIterations) {
await Future.delayed(iterationDelay);
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
if (dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('message') &&
dataCarsLocationByPassenger['message'] != null) {
for (var driverData in dataCarsLocationByPassenger['message']) {
String driverId = driverData['driver_id'].toString();
if (!notifiedDrivers.contains(driverId)) {
notifiedDrivers.add(driverId);
double driverLat = double.parse(driverData['latitude']);
double driverLng = double.parse(driverData['longitude']);
double distanceToDriverInMeters = Geolocator.distanceBetween(
passengerLocation.latitude,
passengerLocation.longitude,
driverLat,
driverLng,
);
double distanceToDriverInKm = distanceToDriverInMeters *
1.25 / //to approximate to stright distance
1000;
double durationToDriverInHours =
distanceToDriverInKm / 25; // 25 km/h as default speed
double durationToDriverInSeconds = durationToDriverInHours * 3600;
durationToPassenger = durationToDriverInSeconds.toInt();
distanceByPassenger =
(distanceToDriverInMeters * 1.25).toStringAsFixed(0);
Future.delayed(const Duration(microseconds: 10));
final body = constructNotificationBody(driverData);
Log.print('body:ww $body');
firebaseMessagesController.sendNotificationToDriverMAP(
'Order', // without tr since background not valid
endNameAddress,
(driverData['token'].toString()),
body,
'order.wav');
}
}
}
iteration++;
}
}
Future<void> notifyAvailableDriversAgain() async {
Log.print('[DEBUG] Starting notifyAvailableDriversAgain()');
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
Log.print(
'[DEBUG] Found drivers to notify: ${dataCarsLocationByPassenger['message']?.length}');
if (dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('message') &&
dataCarsLocationByPassenger['message'] != null) {
for (var driverData in dataCarsLocationByPassenger['message']) {
String driverId = driverData['driver_id'].toString();
if (!notifiedDrivers.contains(driverId)) {
notifiedDrivers.add(driverId);
double driverLat = double.parse(driverData['latitude']);
double driverLng = double.parse(driverData['longitude']);
double distanceToDriverInMeters = Geolocator.distanceBetween(
passengerLocation.latitude,
passengerLocation.longitude,
driverLat,
driverLng,
);
double distanceToDriverInKm = distanceToDriverInMeters *
1.25 / //to approximate to stright distance
1000;
double durationToDriverInHours =
distanceToDriverInKm / 25; // 25 km/h as default speed
double durationToDriverInSeconds = durationToDriverInHours * 3600;
durationToPassenger = durationToDriverInSeconds.toInt();
distanceByPassenger =
(distanceToDriverInMeters * 1.25).toStringAsFixed(0);
Future.delayed(const Duration(microseconds: 10));
final body = constructNotificationBody(driverData);
// Log.print('body:ww ${body}');
Log.print(
'[DEBUG] Sending to driver: ${driverData['driver_id']}, token: ${driverData['token']}');
firebaseMessagesController.sendNotificationToDriverMAP(
'Order', // without tr since background not valid
endNameAddress,
(driverData['token'].toString()),
body,
'order.wav');
}
}
}
}
List<String> constructNotificationBody(var driverData) {
final paymentController = Get.find<PaymentController>();
return [
'${data[0]['start_location']['lat']},${data[0]['start_location']['lng']}',
'${data[0]['end_location']['lat']},${data[0]['end_location']['lng']}',
totalPassenger.toStringAsFixed(2),
totalDriver.toStringAsFixed(2),
durationToRide.toString(),
distance.toStringAsFixed(2),
driverData['driver_id'].toString(),
box.read(BoxName.passengerID).toString(),
(box.read(BoxName.name).toString().split(' ')[0]).toString(),
(box.read(BoxName.tokenFCM).toString()),
(box.read(BoxName.phone).toString()),
durationToPassenger.toStringAsFixed(0) ?? '120',
distanceByPassenger.toString() ?? '2000',
paymentController.isWalletChecked.toString(),
(driverData['token'].toString()),
durationToPassenger.toString(),
rideId.toString(),
rideTimerBegin.toString(),
driverData['driver_id'].toString(),
durationToRide.toString(),
Get.find<WayPointController>().wayPoints.length > 1
? 'haveSteps'
: 'startEnd',
placesCoordinate[0],
placesCoordinate[1],
placesCoordinate[2],
placesCoordinate[3],
placesCoordinate[4],
costForDriver.toStringAsFixed(2),
(double.parse(box.read(BoxName.passengerWalletTotal)) < 0
? double.parse(box.read(BoxName.passengerWalletTotal))
.toStringAsFixed(2)
: '0'),
box.read(BoxName.email).toString(),
data[0]['start_address'],
data[0]['end_address'],
box.read(BoxName.carType),
kazan.toStringAsFixed(0),
passengerRate.toStringAsFixed(2),
];
}
StreamController<String> _rideStatusStreamController =
StreamController<String>.broadcast();
Stream<String> get rideStatusStream => _rideStatusStreamController.stream;
int maxAttempts = 28;
Future<void> delayAndFetchRideStatusForAllDriverAvailable(
String rideId) async {
int attemptCounter = 0;
bool isApplied = false;
tick = 0;
await addRideToNotificationDriverAvailable();
Timer.periodic(const Duration(seconds: 1), (timer) async {
if (attemptCounter >= maxAttempts || isApplied == true) {
timer.cancel();
_rideStatusStreamController.close(); // Close the stream when done
return;
}
attemptCounter++;
tick++;
try {
var res = await getRideStatus(rideId);
String rideStatusDelayed = res.toString();
Log.print('rideStatusDelayed: $rideStatusDelayed');
_rideStatusStreamController
.add(rideStatusDelayed); // Emit the ride status
// addRideToNotificationDriverString();
if (rideStatusDelayed == 'Cancel') {
timer.cancel();
NotificationController().showNotification(
"Order Cancelled".tr, "you canceled order".tr, 'ding');
_rideStatusStreamController
.close(); // Close stream after cancellation
//
//
} else if (rideStatusDelayed == 'Apply' ||
rideStatusDelayed == 'Applied') {
isApplied = true;
rideAppliedFromDriver(isApplied);
timer.cancel();
// Close stream after applying
} else if (attemptCounter >= maxAttempts ||
rideStatusDelayed == 'waiting') {
// timer.cancel(); //todo
// addRideToNotificationDriverString();
// Show dialog to increase fee...
// buildTimerForIncrease();
Get.defaultDialog(
title: 'Are you want to wait drivers to accept your order'.tr,
middleText: '',
onConfirm: () {
Log.print('[DEBUG] User chose to wait again');
Get.back();
notifyAvailableDriversAgain();
delayAndFetchRideStatusForAllDriverAvailable(rideId);
// addRideToNotificationDriverAvailable();
},
onCancel: () {
timer.cancel();
Get.back();
showCancelRideBottomSheet();
},
);
// MyDialog().getDialog(
// 'Are you want to wait drivers to accept your order'.tr, '', () {
// Get.back();
// addRideToNotificationDriverAvailable();
// });
update();
_rideStatusStreamController
.close(); // Close stream after max attempts
}
} catch (e) {
_rideStatusStreamController.addError(e); // Handle errors in the stream
}
});
}
rideAppliedFromDriver(bool isApplied) async {
await getUpdatedRideForDriverApply(rideId);
NotificationController().showNotification(
'Accepted Ride'.tr,
'$driverName ${'accepted your order at price'.tr} ${totalPassenger.toStringAsFixed(1)} ${'with type'.tr} ${box.read(BoxName.carType)}',
'ding');
if (box.read(BoxName.carType) == 'Speed' ||
box.read(BoxName.carType) == 'Awfar Car') {
NotificationController().showNotification(
'The captain is responsible for the route.'.tr,
'This price is fixed even if the route changes for the driver.'.tr,
'ding');
} else if (box.read(BoxName.carType) == 'Comfort' ||
box.read(BoxName.carType) == 'Lady') {
NotificationController().showNotification('Attention'.tr,
'The price may increase if the route changes.'.tr, 'ding');
}
isApplied = true;
statusRide = 'Apply';
rideConfirm = false;
isSearchingWindow = false;
update();
startTimer();
// todo stop this because this method in startTimer()
// startTimerFromDriverToPassengerAfterApplied();
// timer.cancel();
_rideStatusStreamController.close();
}
// Listening to the Stream
void listenToRideStatusStream() {
rideStatusStream.listen((rideStatus) {
print("Ride Status: $rideStatus");
// Handle updates based on the ride status
}, onError: (error) {
print("Error in Ride Status Stream: $error");
// Handle stream errors
}, onDone: () {
print("Ride status stream closed.");
});
}
reSearchAfterCanceledFromDriver() async {
shouldFetch = true; // Stop further fetches
statusRide = 'wait';
rideConfirm = true;
isSearchingWindow = true;
update();
updateConfirmRideForAllDriverAvailable();
}
void start15SecondTimer(String rideId) {
Timer(const Duration(seconds: 15), () {
delayAndFetchRideStatusForAllDriverAvailable(rideId);
});
}
void startTimer() async {
for (int i = 0; i <= durationTimer; i++) {
await Future.delayed(const Duration(seconds: 1));
progress = i / durationTimer;
remainingTime = durationTimer - i;
if (remainingTime == 0) {
rideConfirm = false;
timeToPassengerFromDriverAfterApplied += durationToPassenger;
// timeToPassengerFromDriverAfterApplied.toString());
startTimerFromDriverToPassengerAfterApplied();
update();
}
update();
}
timerEnded();
}
void timerEnded() async {
runEvery30SecondsUntilConditionMet();
isCancelRidePageShown = false;
print('isCancelRidePageShown: $isCancelRidePageShown');
update();
}
Future<String> getRideStatus(String rideId) async {
final response = await CRUD().get(
link: "${AppLink.endPoint}/ride/rides/getRideStatus.php",
payload: {'id': rideId});
print(response);
print('2176');
return jsonDecode(response)['data'];
}
late String driverCarModel,
driverCarMake,
driverLicensePlate,
driverName = '';
getUpdatedRideForDriverApply(String rideId) async {
// if (isDriversTokensSend) {
final res = await CRUD().get(
link: "${AppLink.endPoint}/ride/rides/getRideOrderID.php",
payload: {'id': rideId});
if (res != 'failure') {
var response = jsonDecode(res);
Log.print('getUpdatedRideForDriverApply: $response');
driverId = response['data']['driver_id'];
driverPhone = (response['data']['phone']);
driverCarMake = response['data']['make'];
model = response['data']['model'];
colorHex = response['data']['color_hex'];
carColor = response['data']['color'];
make = response['data']['make'];
licensePlate = (response['data']['car_plate']);
passengerName = (response['data']['passengerName']);
driverName = (response['data']['driverName'].toString());
driverToken = (response['data']['token']);
// Log.print('driverToken updated: $driverToken');
carYear = response['data']['year'];
driverRate = response['data']['ratingDriver'].toString();
}
// driversToken.remove(driverToken);
// for (var i = 1; i < driversToken.length; i++) {
firebaseMessagesController.sendNotificationToDriverMAP(
'Order Accepted'.tr,
'$driverName${'Accepted your order'.tr}',
driverToken.toString(),
[],
'start.wav',
);
// }
// }
}
late LatLng currentDriverLocation;
late double headingList;
// Future getCarsLocationByPassengerAndReloadMarker() async {
// if (statusRide == 'wait') {
// carsLocationByPassenger = [];
// LatLngBounds bounds = calculateBounds(
// passengerLocation.latitude, passengerLocation.longitude, 7000);
// var res;
// if (box.read(BoxName.carType) == 'Lady') {
// res = await CRUD()
// .get(link: AppLink.getFemalDriverLocationByPassenger, payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// });
// } else if (box.read(BoxName.carType) == 'Speed') {
// res = await CRUD().get(
// link: AppLink.getCarsLocationByPassengerSpeed,
// payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// },
// );
// } else if (box.read(BoxName.carType) == 'Delivery') {
// res = await CRUD().get(
// link: AppLink.getCarsLocationByPassengerDelivery,
// payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// },
// );
// } else {
// res = await CRUD()
// .get(link: AppLink.getCarsLocationByPassenger, payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// });
// }
// if (res == 'failure') {
// noCarString = true;
// dataCarsLocationByPassenger = res;
// update();
// } else {
// // Get.snackbar('no car', 'message');
// noCarString = false;
// dataCarsLocationByPassenger = jsonDecode(res);
// // if (dataCarsLocationByPassenger.length > carsOrder) {
// driverId = dataCarsLocationByPassenger['message'][carsOrder]
// ['driver_id']
// .toString();
// gender = dataCarsLocationByPassenger['message'][carsOrder]['gender']
// .toString();
// // }
// carsLocationByPassenger.clear(); // Clear existing markers
// // late LatLng lastDriverLocation; // Initialize a variable for last location
// for (var i = 0;
// i < dataCarsLocationByPassenger['message'].length;
// i++) {
// var json = dataCarsLocationByPassenger['message'][i];
// // CarLocationModel model = CarLocationModel.fromJson(json);
// if (carLocationsModels.length < i + 1) {
// // carLocationsModels.add(model);
// markers.add(
// Marker(
// markerId: MarkerId(json['latitude']),
// position: LatLng(
// double.parse(json['latitude']),
// double.parse(json['longitude']),
// ),
// rotation: double.parse(json['heading']),
// icon: json['model'].toString().contains('دراجة')
// ? motoIcon
// : json['gender'] == 'Male'.tr
// ? carIcon
// : ladyIcon,
// ),
// );
// driversToken.add(json['token']);
// // driversToken = json['token'];
// } else {
// // carLocationsModels[i] = model;
// markers[i] = Marker(
// markerId: MarkerId(json['latitude']),
// position: LatLng(
// double.parse(json['latitude']),
// double.parse(json['longitude']),
// ),
// rotation: double.parse(json['heading']),
// icon: json['model'].contains('دراجة')
// ? motoIcon
// : json['gender'] == 'Male'.tr
// ? carIcon
// : ladyIcon,
// );
// // driversToken = json['token'];
// driversToken.add(json['token']);
// }
// }
// }
// update();
// }
// }
Map<String, Timer> _animationTimers = {};
final int updateIntervalMs = 100; // Update every 100ms
final double minMovementThreshold =
10; // Minimum movement in meters to trigger update
Future getCarForFirstConfirm(String carType) async {
bool foundCars = false;
int attempt = 0;
// Set up the periodic timer
Timer? timer = Timer.periodic(const Duration(seconds: 4), (Timer t) async {
// Attempt to get car location
foundCars = await getCarsLocationByPassengerAndReloadMarker(
carType, attempt * 2000);
Log.print('foundCars: $foundCars');
if (foundCars) {
// If cars are found, cancel the timer and exit the search
t.cancel();
} else if (attempt >= 4) {
// After 4 attempts, stop the search
t.cancel();
// No cars found after 4 attempts
// MyDialog().getDialog(
// "No Car or Driver Found in your area.".tr,
// "No Car or Driver Found in your area.".tr,
// () {
// Get.back();
// },
// );
if (!foundCars) {
noCarString = true;
dataCarsLocationByPassenger = 'failure';
}
update();
}
attempt++; // Increment attempt
});
}
void startCarLocationSearch(String carType) {
int searchInterval = 5; // Interval in seconds
Log.print('searchInterval: $searchInterval');
int boundIncreaseStep = 2500; // Initial bounds in meters
Log.print('boundIncreaseStep: $boundIncreaseStep');
int maxAttempts = 3; // Maximum attempts to increase bounds
int maxBoundIncreaseStep = 6000; // Maximum bounds increase step
int attempt = 0; // Current attempt
Log.print('initial attempt: $attempt');
Timer.periodic(Duration(seconds: searchInterval), (Timer timer) async {
Log.print('Current attempt: $attempt'); // Log current attempt
bool foundCars = false;
if (attempt >= maxAttempts) {
timer.cancel();
if (foundCars == false) {
noCarString = true;
// dataCarsLocationByPassenger = 'failure';
update();
}
// return;
} else if (reloadStartApp == true) {
Log.print('reloadStartApp: $reloadStartApp');
foundCars = await getCarsLocationByPassengerAndReloadMarker(
carType, boundIncreaseStep);
Log.print('foundCars: $foundCars');
if (foundCars) {
timer.cancel();
} else {
attempt++;
if (reloadCount >= 3 || tick > 18 || reloadCount > 15) {
timer.cancel();
}
Log.print(
'Incrementing attempt to: $attempt'); // Log incremented attempt
if (boundIncreaseStep < maxBoundIncreaseStep) {
boundIncreaseStep += 1500; // Increase bounds
if (boundIncreaseStep > maxBoundIncreaseStep) {
boundIncreaseStep =
maxBoundIncreaseStep; // Ensure it does not exceed the maximum
}
Log.print(
'New boundIncreaseStep: $boundIncreaseStep'); // Log new bounds
}
}
}
});
}
String getLocationArea(double latitude, double longitude) {
final locations = box.read(BoxName.locationName) ?? [];
for (final location in locations) {
final locationData = location as Map<String, dynamic>;
// Debugging: Print location data
// print('Location Data: $locationData');
// Convert string values to double
final minLatitude =
double.tryParse(locationData['min_latitude'].toString()) ?? 0.0;
final maxLatitude =
double.tryParse(locationData['max_latitude'].toString()) ?? 0.0;
final minLongitude =
double.tryParse(locationData['min_longitude'].toString()) ?? 0.0;
final maxLongitude =
double.tryParse(locationData['max_longitude'].toString()) ?? 0.0;
// Debugging: Print converted values
print(
'Converted Values: minLatitude=$minLatitude, maxLatitude=$maxLatitude, minLongitude=$minLongitude, maxLongitude=$maxLongitude');
if (latitude >= minLatitude &&
latitude <= maxLatitude &&
longitude >= minLongitude &&
longitude <= maxLongitude) {
box.write(BoxName.serverChosen, (locationData['server_link']));
// Log.print(
// 'locationData----server_link: ${(locationData['server_link'])}');
return locationData['name'];
}
}
// Default case
box.write(BoxName.serverChosen, AppLink.IntaleqCairoServer);
return 'Cairo';
}
// if (latitude >= 29.918901 &&
// latitude <= 30.198857 &&
// longitude >= 31.215009 &&
// longitude <= 31.532186) {
// box.write(BoxName.serverChosen, AppLink.IntaleqCairoServer);
// return 'Cairo';
// } else if (latitude >= 29.904975 &&
// latitude <= 30.143372 &&
// longitude >= 30.787030 &&
// longitude <= 31.215009) {
// box.write(BoxName.serverChosen, AppLink.IntaleqGizaServer);
// return 'Giza';
// } else if (latitude >= 30.396286 &&
// latitude <= 31.654458 &&
// longitude >= 29.041139 &&
// longitude <= 32.626259) {
// box.write(BoxName.serverChosen, AppLink.IntaleqAlexandriaServer);
// return 'Alexandria';
// } else {
// box.write(BoxName.serverChosen, AppLink.IntaleqCairoServer);
// return 'Cairo';
// }
// }
Future<bool> getCarsLocationByPassengerAndReloadMarker(
String carType, int boundIncreaseStep) async {
// if (statusRide == 'wait') {
carsLocationByPassenger = [];
LatLngBounds bounds = calculateBounds(passengerLocation.latitude,
passengerLocation.longitude, boundIncreaseStep.toDouble());
var res;
// await getLocation();
switch (carType) {
case 'Lady':
res = await CRUD()
.get(link: AppLink.getFemalDriverLocationByPassenger, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
case 'Comfort':
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassengerComfort, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
case 'Speed':
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassengerSpeed, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
case 'Scooter':
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassengerDelivery, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
case 'Awfar Car':
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassengerBalash, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
case 'Pink Bike':
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassengerPinkBike, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
break;
default:
res = await CRUD()
.get(link: AppLink.getCarsLocationByPassenger, payload: {
'southwestLat': bounds.southwest.latitude.toString(),
'southwestLon': bounds.southwest.longitude.toString(),
'northeastLat': bounds.northeast.latitude.toString(),
'northeastLon': bounds.northeast.longitude.toString(),
});
}
if (res == 'failure') {
noCarString = true;
// dataCarsLocationByPassenger = 'failure';
update();
return false;
} else {
noCarString = false;
dataCarsLocationByPassenger = jsonDecode(res);
// Log.print(
// 'dataCarsLocationByPassenger:getCarsLocationByPassengerAndReloadMarker $dataCarsLocationByPassenger');
// Check if 'message' is present and not null
if (dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.isNotEmpty) {
// Check if carsOrder is within bounds
// if (carsOrder < dataCarsLocationByPassenger['message'].length) {
// driverId = dataCarsLocationByPassenger['message'][carsOrder]
// ['driver_id']
// .toString();
// gender = dataCarsLocationByPassenger['message'][carsOrder]['gender']
// .toString();
// driverToken = dataCarsLocationByPassenger['message'][carsOrder]
// ['token']
// .toString();
// } else {
print('carsOrder is in of bounds for message array');
// return false;
// }
} else {
// Get.defaultDialog(title: 'No cars available ');
print('No cars available or message is null');
return false;
}
carsLocationByPassenger.clear(); // Clear existing markers
for (var i = 0; i < dataCarsLocationByPassenger['message'].length; i++) {
var json = dataCarsLocationByPassenger['message'][i];
_updateOrCreateMarker(
MarkerId(json['latitude']).toString(),
LatLng(
double.parse(json['latitude']), double.parse(json['longitude'])),
double.parse(json['heading']),
_getIconForCar(json),
);
driversToken.add((json['token']));
}
// Add fake car markers
_addFakeCarMarkers(passengerLocation, 1);
update();
return true;
}
// }
// return false;
}
final List<Map<String, dynamic>> fakeCarData = [];
void _addFakeCarMarkers(LatLng center, int count) {
if (fakeCarData.isEmpty) {
Random random = Random();
double radiusInKm = 2.5; // 3 km diameter, so 1.5 km radius
for (int i = 0; i < count; i++) {
// Generate a random angle and distance within the circle
double angle = random.nextDouble() * 2 * pi;
double distance = sqrt(random.nextDouble()) * radiusInKm;
// Convert distance to latitude and longitude offsets
double latOffset = (distance / 111.32); // 1 degree lat ≈ 111.32 km
double lonOffset =
(distance / (111.32 * cos(radians(center.latitude))));
// Calculate new position
double lat = center.latitude + (latOffset * cos(angle));
double lon = center.longitude + (lonOffset * sin(angle));
double heading = random.nextDouble() * 360;
fakeCarData.add({
'id': 'fake_$i',
'latitude': lat,
'longitude': lon,
'heading': heading,
'gender': 'Male', // Randomize gender
});
}
}
for (var carData in fakeCarData) {
_updateOrCreateMarker(
MarkerId(carData['id']).toString(),
LatLng(carData['latitude'], carData['longitude']),
carData['heading'],
_getIconForCar(carData),
);
}
}
BitmapDescriptor _getIconForCar(Map<String, dynamic> carData) {
if (carData['model'].toString().contains('دراجة')) {
return motoIcon;
} else if (carData['gender'] == 'Female') {
return ladyIcon;
} else {
return carIcon;
}
}
void _updateOrCreateMarker(String markerId, LatLng newPosition,
double newHeading, BitmapDescriptor icon) {
Marker? existingMarker = markers.cast<Marker?>().firstWhere(
(m) => m?.markerId == MarkerId(markerId),
orElse: () => null,
);
if (existingMarker == null) {
markers.add(Marker(
markerId: MarkerId(markerId),
position: newPosition,
rotation: newHeading,
icon: icon,
));
} else {
double distance =
_calculateDistance(existingMarker.position, newPosition);
if (distance >= minMovementThreshold) {
_smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
}
}
}
void _smoothlyUpdateMarker(Marker oldMarker, LatLng newPosition,
double newHeading, BitmapDescriptor icon) {
String markerId = oldMarker.markerId.value;
LatLng startPosition = oldMarker.position;
double startHeading = oldMarker.rotation ?? 0;
_animationTimers[markerId]?.cancel();
_animationTimers[markerId] =
Timer.periodic(Duration(milliseconds: updateIntervalMs), (timer) {
double progress =
timer.tick / (500 / updateIntervalMs); // 500ms total duration
if (progress >= 1.0) {
timer.cancel();
_animationTimers.remove(markerId);
progress = 1.0;
}
LatLng intermediatePosition = LatLng(
startPosition.latitude +
(newPosition.latitude - startPosition.latitude) * progress,
startPosition.longitude +
(newPosition.longitude - startPosition.longitude) * progress);
double intermediateHeading =
startHeading + (newHeading - startHeading) * progress;
markers.removeWhere((m) => m.markerId == oldMarker.markerId);
markers.add(Marker(
markerId: oldMarker.markerId,
position: intermediatePosition,
rotation: intermediateHeading,
icon: icon,
));
update();
});
}
double _calculateDistance(LatLng start, LatLng end) {
// Implement distance calculation (e.g., Haversine formula)
// For simplicity, this is a placeholder. Replace with actual implementation.
return 1000 *
sqrt(pow(start.latitude - end.latitude, 2) +
pow(start.longitude - end.longitude, 2));
}
String formatSyrianPhoneNumber(String phoneNumber) {
// Trim any whitespace from the input.
String trimmedPhone = phoneNumber.trim();
// If the number starts with '09', remove the leading '0' and prepend '963'.
if (trimmedPhone.startsWith('09')) {
return '963${trimmedPhone.substring(1)}';
}
// If the number already starts with '963', return it as is to avoid duplication.
if (trimmedPhone.startsWith('963')) {
return trimmedPhone;
}
// For any other case (e.g., number starts with '9' without a '0'),
// prepend '963' to ensure the correct format.
return '963$trimmedPhone';
}
Future getTokenForParent() async {
if (box.read(BoxName.sosPhonePassenger) == null) {
Get.defaultDialog(
title: 'Add SOS Phone'.tr,
titleStyle: AppStyle.title,
content: Form(
key: sosFormKey,
child: MyTextForm(
controller: sosPhonePassengerProfile,
label: 'insert sos phone'.tr,
hint: 'e.g. 0912345678'.tr,
type: TextInputType.phone,
),
),
confirm: MyElevatedButton(
title: 'Add SOS Phone'.tr,
onPressed: () async {
if (sosFormKey.currentState!.validate()) {
Get.back();
var numberPhone =
formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
await CRUD().post(
link: AppLink.updateprofile,
payload: {
'id': box.read(BoxName.passengerID),
'sosPhone': numberPhone,
},
);
box.write(BoxName.sosPhonePassenger, numberPhone);
}
}));
}
var numberPhone = formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
var res = await CRUD().getTokenParent(
link: AppLink.getTokenParent, payload: {'phone': numberPhone});
// Check if `res` is already a map
if (res is Map<String, dynamic>) {
var res1 = res;
handleResponse(res1);
} else {
// If it's a string, decode it
var res1 = jsonDecode(res);
handleResponse(res1);
}
}
// Function to check if the point is inside the polygon
bool isPointInPolygon(LatLng point, List<LatLng> polygon) {
int intersections = 0;
for (int i = 0; i < polygon.length; i++) {
LatLng vertex1 = polygon[i];
LatLng vertex2 =
polygon[(i + 1) % polygon.length]; // Loop back to the start
if (_rayIntersectsSegment(point, vertex1, vertex2)) {
intersections++;
}
}
// If the number of intersections is odd, the point is inside
return intersections % 2 != 0;
}
// Helper function to check if a ray from the point intersects with a polygon segment
bool _rayIntersectsSegment(LatLng point, LatLng vertex1, LatLng vertex2) {
double px = point.longitude;
double py = point.latitude;
double v1x = vertex1.longitude;
double v1y = vertex1.latitude;
double v2x = vertex2.longitude;
double v2y = vertex2.latitude;
// Check if the point is outside the vertical bounds of the segment
if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) {
return false;
}
// Calculate the intersection of the ray and the segment
double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y);
// Check if the intersection is to the right of the point
return intersectX > px;
}
bool isInUniversity = false;
// Function to check if the passenger is in any university polygon
// Function to check if the passenger is in any university polygon and return the university name
String checkPassengerLocation(LatLng passengerLocation,
List<List<LatLng>> universityPolygons, List<String> universityNames) {
for (int i = 0; i < universityPolygons.length; i++) {
if (isPointInPolygon(passengerLocation, universityPolygons[i])) {
isInUniversity = true;
return "Passenger is in ${universityNames[i]}";
}
}
return "Passenger is not in any university";
}
String passengerLocationStringUnvirsity = 'unKnown';
void getPassengerLocationUniversity() {
// Check if the passenger is inside any of the university polygons and get the university name
passengerLocationStringUnvirsity = checkPassengerLocation(
passengerLocation,
UniversitiesPolygons.universityPolygons,
UniversitiesPolygons.universityNames,
);
if (passengerLocationStringUnvirsity != 'unKnown') {
// Get.snackbar('you are in $passengerLocationStringUnvirsity', "");
}
print(passengerLocationStringUnvirsity);
}
var polygons = <Polygon>{}.obs;
// Initialize polygons from UniversitiesPolygons
void _initializePolygons() {
List<List<LatLng>> universityPolygons =
UniversitiesPolygons.universityPolygons;
List<String> universityNames = UniversitiesPolygons.universityNames;
for (int i = 0; i < universityPolygons.length; i++) {
Polygon polygon = Polygon(
polygonId: PolygonId(universityNames[i]),
points: universityPolygons[i],
strokeColor: Colors.blueAccent,
fillColor: Colors.blueAccent.withOpacity(0.2),
strokeWidth: 2,
);
polygons.add(polygon); // Add polygon to observable set
}
}
void handleResponse(Map<String, dynamic> res1) {
if (res1['message'] == "No passenger found for the given phone number") {
Get.defaultDialog(
title: "No user found for the given phone number".tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text(
"No passenger found for the given phone number".tr,
style: AppStyle.title,
),
Text(
"Send Intaleq app to him".tr,
style: AppStyle.title.copyWith(color: AppColor.greenColor),
)
],
),
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () {
Get.back();
var phone = box.read(BoxName.countryCode) == 'Egypt'
? '+2${box.read(BoxName.sosPhonePassenger)}'
: '+962${box.read(BoxName.sosPhonePassenger)}';
var message = '''Dear ,
🚀 I have just started an exciting trip and I would like to share the details of my journey and my current location with you in real-time! Please download the Intaleq app. It will allow you to view my trip details and my latest location.
👉 Download link:
Android [https://play.google.com/store/apps/details?id=com.mobileapp.store.ride]
iOS [https://getapp.cc/app/6458734951]
I look forward to keeping you close during my adventure!
Intaleq ,'''
.tr;
launchCommunication('whatsapp', phone, message);
}),
cancel: MyElevatedButton(
title: 'No'.tr,
onPressed: () {
Get.back();
}));
} else if (res1['status'] == 'success') {
var tokenParent = (res1['data'][0]['token']);
Get.snackbar("The invitation was sent successfully".tr, '',
backgroundColor: AppColor.greenColor);
firebaseMessagesController.sendNotificationToPassengerToken(
"Trip Monitoring".tr,
"Trip Monitoring".tr,
tokenParent,
[rideId, driverId],
'order1.wav',
);
box.write(BoxName.parentTripSelected, true);
box.write(BoxName.tokenParent, tokenParent);
}
}
LatLng driverLocationToPassenger = const LatLng(32, 35);
Future getDriverCarsLocationToPassengerAfterApplied() async {
driverCarsLocationToPassengerAfterApplied = [];
var res = await CRUD().get(
link: AppLink.getDriverCarsLocationToPassengerAfterApplied,
payload: {'driver_id': driverId});
datadriverCarsLocationToPassengerAfterApplied = jsonDecode(res);
driverLocationToPassenger = LatLng(
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['latitude']),
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['longitude']));
driverCarsLocationToPassengerAfterApplied.add(LatLng(
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['latitude']),
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['longitude'])));
CarLocationModel model = CarLocationModel.fromJson(
datadriverCarsLocationToPassengerAfterApplied['message'][0]);
carLocationsModels.add(model);
update();
}
Future runEvery30SecondsUntilConditionMet() async {
// Calculate the duration of the trip in minutes.
double tripDurationInMinutes = durationToPassenger / 5;
int loopCount = tripDurationInMinutes.ceil();
// If the trip duration is less than or equal to 50 minutes, then break the loop.
for (var i = 0; i < loopCount; i++) {
// Wait for 50 seconds.
await Future.delayed(const Duration(seconds: 5));
if (rideTimerBegin == true || statusRide == 'Apply') {
await getDriverCarsLocationToPassengerAfterApplied();
reloadMarkerDriverCarsLocationToPassengerAfterApplied();
}
}
}
Future runWhenRideIsBegin() async {
// Calculate the duration of the trip in minutes.
double tripDurationInMinutes = durationToRide / 6;
int loopCount = tripDurationInMinutes.ceil();
// If the trip duration is less than or equal to 50 minutes, then break the loop.
clearMarkersExceptStartEnd();
for (var i = 0; i < loopCount; i++) {
// Wait for 50 seconds.
await Future.delayed(const Duration(seconds: 4));
// if (rideTimerBegin == true && statusRide == 'Apply') {
await getDriverCarsLocationToPassengerAfterApplied();
// }
reloadMarkerDriverCarsLocationToPassengerAfterApplied();
}
}
Timer? _timer;
// final int updateIntervalMs = 100; // Update every 100ms
// final double minMovementThreshold =
// 1.0; // Minimum movement in meters to trigger update
void clearMarkersExceptStartEnd() {
Set<Marker> markersToRemove = markers
.where((marker) =>
marker.markerId != const MarkerId("start") &&
marker.markerId != const MarkerId("end"))
.toSet();
for (Marker marker in markersToRemove) {
markers.remove(marker);
}
update();
}
void reloadMarkerDriverCarsLocationToPassengerAfterApplied() {
// clearMarkersExceptStartEnd();
LatLng driverPosition = LatLng(
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['latitude']),
double.parse(datadriverCarsLocationToPassengerAfterApplied['message'][0]
['longitude']));
double heading = double.parse(
datadriverCarsLocationToPassengerAfterApplied['message'][0]['heading']
.toString());
BitmapDescriptor icon =
datadriverCarsLocationToPassengerAfterApplied['message'][0]
['model']
.toString()
.contains('دراجة') ||
datadriverCarsLocationToPassengerAfterApplied['message'][0]
['make']
.toString()
.contains('دراجة')
? motoIcon
: datadriverCarsLocationToPassengerAfterApplied['message'][0]
['gender'] ==
'Female'
? ladyIcon
: carIcon;
_updateMarkerPosition(driverPosition, heading, icon);
}
void _updateMarkerPosition(
LatLng newPosition, double newHeading, BitmapDescriptor icon) {
const String markerId = 'driverToPassengers';
Marker? existingMarker = markers.cast<Marker?>().firstWhere(
(m) => m?.markerId == const MarkerId(markerId),
orElse: () => null,
);
if (existingMarker == null) {
// If the marker doesn't exist, create it at the new position
markers.add(Marker(
markerId: const MarkerId(markerId),
position: newPosition,
rotation: newHeading,
icon: icon,
));
update();
} else {
// If the marker exists, check if the movement is significant enough to update
double distance =
_calculateDistance(existingMarker.position, newPosition);
if (distance >= minMovementThreshold) {
_smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
}
}
mapController?.animateCamera(CameraUpdate.newLatLng(newPosition));
}
// @override
// void onClose() {
// _timer?.cancel();
// _animationTimers.forEach((_, timer) => timer.cancel());
// _animationTimers.clear();
// super.onClose();
// }
@override
void onClose() {
print(
"--- MapPassengerController: Closing and cleaning up all resources. ---");
// 1. إلغاء المؤقتات الفردية
// Using ?.cancel() is safe even if the timer is null
markerReloadingTimer.cancel();
markerReloadingTimer1.cancel();
markerReloadingTimer2.cancel();
timerToPassengerFromDriverAfterApplied?.cancel();
_timer?.cancel();
// 2. إلغاء جميع المؤقتات في الخريطة (للتحريكات السلسة)
_animationTimers.forEach((key, timer) {
timer.cancel();
});
_animationTimers.clear();
// 3. إغلاق متحكمات البث (StreamControllers) لمنع تسريب الذاكرة
// Using .close() is the correct way to shut down a stream
if (!_timerStreamController.isClosed) {
_timerStreamController.close();
}
if (!_beginRideStreamController.isClosed) {
_beginRideStreamController.close();
}
if (!_rideStatusStreamController.isClosed) {
_rideStatusStreamController.close();
}
if (!timerController.isClosed) {
timerController.close();
}
// 4. التخلص من متحكم الخريطة (ممارسة جيدة)
mapController?.dispose();
print("--- Cleanup complete. ---");
super.onClose();
}
restCounter() {
clearPlacesDestination();
clearPolyline();
data = [];
rideConfirm = false;
shouldFetch = false;
timeToPassengerFromDriverAfterApplied = 0;
update();
}
//driver behaviour
double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
double deltaLon = lon2 - lon1;
double y = sin(deltaLon) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaLon);
double bearing = atan2(y, x);
return (bearing * 180 / pi + 360) % 360; // تحويل إلى درجات
}
void analyzeBehavior(Position currentPosition, List<LatLng> routePoints) {
double actualBearing = currentPosition.heading; // الاتجاه الفعلي من GPS
double expectedBearing = calculateBearing(
routePoints[0].latitude,
routePoints[0].longitude,
routePoints[1].latitude,
routePoints[1].longitude,
);
double bearingDifference = (expectedBearing - actualBearing).abs();
if (bearingDifference > 30) {
print("⚠️ السائق انحرف عن المسار!");
}
}
void detectStops(Position currentPosition) {
if (currentPosition.speed < 0.5) {
print("🚦 السائق توقف في موقع غير متوقع!");
}
}
Future<void> cancelRideAfterRejectFromAll() async {
clearPlacesDestination();
clearPolyline();
data = [];
await CRUD().post(link: AppLink.updateRides, payload: {
"id": rideId.toString(), // Convert to String
"status": 'notApplyFromAnyDriver'
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
"id": rideId.toString(), // Convert to String
"status": 'notApplyFromAnyDriver'
});
}
rideConfirm = false;
statusRide == 'Cancel';
isSearchingWindow = false;
shouldFetch = false;
isPassengerChosen = false;
isCashConfirmPageShown = false;
// totalStepDurations = 0;
isCashSelectedBeforeConfirmRide = false;
timeToPassengerFromDriverAfterApplied = 0;
changeCancelRidePageShow();
remainingTime = 0;
update();
}
Future cancelRide() async {
// if (rideConfirm == true ||
// statusRide == 'Apply' ||
// statusRide == 'Applied' ||
// statusRide == 'wait' ||
// statusRide == 'waiting') {
clearPlacesDestination();
clearPolyline();
// clearPolylineAll();
data = [];
changeCancelRidePageShow();
if (rideId != 'yet') {
Log.print('cancelRide: 1');
await firebaseMessagesController.sendNotificationToDriverMAP(
'Cancel Trip'.tr,
'Trip Cancelled'.tr,
driverToken.toString(),
[],
'cancel.wav',
);
await Future.wait([
CRUD().post(link: AppLink.updateRides, payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
}),
CRUD().post(link: AppLink.updateDriverOrder, payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
}),
CRUD().post(link: AppLink.updateWaitingTrip, payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
}),
]);
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/update.php",
payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
CRUD()
.post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
CRUD().post(
link:
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
}
print('Cancel');
// }
}
Future.delayed(const Duration(seconds: 1));
Get.offAll(() => const MapPagePassenger());
}
void changePickerShown() {
isPickerShown = !isPickerShown;
heightPickerContainer = isPickerShown == true ? 150 : 90;
update();
}
void changeHeightPointsPageForRider() {
isPointsPageForRider = !isPointsPageForRider;
heightPointsPageForRider = isPointsPageForRider == true ? Get.height : 0;
update();
}
getCoordinateFromMapWayPoints(int index) {
placesCoordinate[index] = newStartPointLocation.toString();
update();
}
// --- ابدأ الإضافة هنا ---
// 1. قائمة لتخزين نقاط التوقف
List<Map<String, dynamic>> waypoints = [];
// 2. دالة لإضافة نقطة توقف جديدة
void addWaypoint(Map<String, dynamic> placeDetails) {
// يمكنك إضافة منطق للتحقق من عدد نقاط التوقف المسموح بها هنا
waypoints.add(placeDetails);
update(); // لتحديث الواجهة
// TODO: أضف هنا استدعاء دالة إعادة رسم المسار مع نقاط التوقف الجديدة
// getDirectionMapWithWaypoints();
}
// 3. دالة لحذف نقطة توقف
void removeWaypoint(int index) {
if (index >= 0 && index < waypoints.length) {
waypoints.removeAt(index);
update(); // لتحديث الواجهة
// TODO: أضف هنا استدعاء دالة إعادة رسم المسار بعد حذف النقطة
// getDirectionMapWithWaypoints();
}
}
// --- انتهى ---
void changeMainBottomMenuMap() {
if (isWayPointStopsSheetUtilGetMap == true) {
changeWayPointSheet();
} else {
isMainBottomMenuMap = !isMainBottomMenuMap;
mainBottomMenuMapHeight =
isMainBottomMenuMap == true ? Get.height * .22 : Get.height * .6;
isWayPointSheet = false;
if (heightMenuBool == true) {
getDrawerMenu();
}
initilizeGetStorage();
update();
}
}
void downPoints() {
if (Get.find<WayPointController>().wayPoints.length < 2) {
isWayPointStopsSheetUtilGetMap = false;
isWayPointSheet = false;
wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
// changeWayPointStopsSheet();
update();
}
// changeWayPointStopsSheet();
// isWayPointSheet = false;
update();
}
void changeWayPointSheet() {
isWayPointSheet = !isWayPointSheet;
wayPointSheetHeight = isWayPointSheet == false ? 0 : Get.height * .45;
// if (heightMenuBool == true) {
// getDrawerMenu();
// }
update();
}
void changeWayPointStopsSheet() {
// int waypointsLength = Get.find<WayPointController>().wayPoints.length;
if (wayPointIndex > -1) {
isWayPointStopsSheet = true;
isWayPointStopsSheetUtilGetMap = true;
}
isWayPointStopsSheet = !isWayPointStopsSheet;
wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
// if (heightMenuBool == true) {
// getDrawerMenu();
// }
update();
}
changeHeightPlaces() {
if (placesDestination.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightStartPlaces() {
if (placesStart.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightPlacesAll(int index) {
if (placeListResponseAll[index].isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightPlaces1() {
if (wayPoint1.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightPlaces2() {
if (wayPoint2.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightPlaces3() {
if (wayPoint3.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
changeHeightPlaces4() {
if (wayPoint4.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
hidePlaces() {
height = 0;
update();
}
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
const R = 6371.0; // km
final dLat = (lat2 - lat1) * math.pi / 180.0;
final dLon = (lon2 - lon1) * math.pi / 180.0;
final a = math.sin(dLat / 2) * math.sin(dLat / 2) +
math.cos(lat1 * math.pi / 180.0) *
math.cos(lat2 * math.pi / 180.0) *
math.sin(dLon / 2) *
math.sin(dLon / 2);
final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
return R * c;
}
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
double _kmToLatDelta(double km) => km / 111.0;
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض)
double _kmToLngDelta(double km, double atLat) =>
km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9);
/// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة)
double _relevanceScore(String name, String query) {
final n = name.toLowerCase();
final parts =
query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2);
double s = 0.0;
for (final p in parts) {
if (n.startsWith(p)) {
s += 2.0;
} else if (n.contains(p)) {
s += 1.0;
}
}
return s;
}
Future<void> getPlaces() async {
final q = placeDestinationController.text.trim();
if (q.isEmpty) {
placesDestination = [];
update();
return;
}
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
// نصف قطر البحث بالكيلومتر (عدّل حسب رغبتك)
const radiusKm = 200.0;
// حساب الباوند الصحيح (درجات، وليس 2.2 درجة ثابتة)
final latDelta = _kmToLatDelta(radiusKm);
final lngDelta = _kmToLngDelta(radiusKm, lat);
final latMin = lat - latDelta;
final latMax = lat + latDelta;
final lngMin = lng - lngDelta;
final lngMax = lng + lngDelta;
try {
final response = await CRUD().post(
link: AppLink.getPlacesSyria,
payload: {
'query': q,
'lat_min': latMin.toString(),
'lat_max': latMax.toString(),
'lng_min': lngMin.toString(),
'lng_max': lngMax.toString(),
},
);
// يدعم شكلي استجابة: إما {"...","message":[...]} أو قائمة مباشرة [...]
List list;
if (response is Map && response['message'] is List) {
list = List.from(response['message'] as List);
} else if (response is List) {
list = List.from(response);
} else {
print('Unexpected response shape');
return;
}
// جهّز الحقول المحتملة للأسماء
String _bestName(Map p) {
return (p['name'] ?? p['name_ar'] ?? p['name_en'] ?? '').toString();
}
// احسب المسافة ودرجة التطابق والنقاط
for (final p in list) {
final plat = double.tryParse(p['latitude']?.toString() ?? '') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '') ?? 0.0;
final d = _haversineKm(lat, lng, plat, plng);
final rel = _relevanceScore(_bestName(p), q);
// معادلة ترتيب ذكية: مسافة أقل + تطابق أعلى = نقاط أعلى
// تضيف +1 لضمان عدم وصول الوزن للصفر عند عدم وجود تطابق
final score = (1.0 / (1.0 + d)) * (1.0 + rel);
p['distanceKm'] = d;
p['relevance'] = rel;
p['score'] = score;
}
// رتّب حسب score تنازليًا، ثم المسافة تصاعديًا كحسم
list.sort((a, b) {
final sa = (a['score'] ?? 0.0) as double;
final sb = (b['score'] ?? 0.0) as double;
final cmp = sb.compareTo(sa);
if (cmp != 0) return cmp;
final da = (a['distanceKm'] ?? 1e9) as double;
final db = (b['distanceKm'] ?? 1e9) as double;
return da.compareTo(db);
});
// خذ أول 1015 للعرض (اختياري)، أو اعرض الكل
placesDestination = list.take(15).toList();
Log.print('placesDestination: $placesDestination');
update();
} catch (e) {
print('Exception in getPlaces: $e');
}
}
// var languageCode;
// // تحديد اللغة حسب الإدخال
// if (RegExp(r'[a-zA-Z]').hasMatch(placeDestinationController.text)) {
// languageCode = 'en';
// } else {
// languageCode = 'ar';
// }
// final bool isTextEmpty = placeDestinationController.text.trim().isEmpty;
// var key = Platform.isAndroid ? AK.mapAPIKEY : AK.mapAPIKEYIOS;
// final Uri url = Uri.parse(
// isTextEmpty
// ? 'https://places.googleapis.com/v1/places:searchNearby?key=$key'
// : 'https://places.googleapis.com/v1/places:searchText?key=$key',
// );
// Log.print('url: $url');
// // بناء الجسم حسب نوع الطلب
// final body = isTextEmpty
// ? jsonEncode({
// "languageCode": languageCode,
// "locationRestriction": {
// "circle": {
// "center": {
// "latitude": passengerLocation.latitude,
// "longitude": passengerLocation.longitude
// },
// "radius": 40000 // 40 كم
// }
// },
// "maxResultCount": 10
// })
// : jsonEncode({
// "textQuery": placeDestinationController.text,
// "languageCode": languageCode,
// "maxResultCount": 10,
// "locationBias": {
// "circle": {
// "center": {
// "latitude": passengerLocation.latitude,
// "longitude": passengerLocation.longitude
// },
// "radius": 40000
// }
// }
// });
// final headers = {
// 'Content-Type': 'application/json',
// 'X-Goog-Api-Key': AK.mapAPIKEY,
// 'X-Goog-FieldMask':
// 'places.displayName,places.formattedAddress,places.location'
// };
// try {
// final response = await http.post(url, headers: headers, body: body);
// print('response: ${response.statusCode} - ${response.body}');
// if (response.statusCode == 200) {
// final data = jsonDecode(response.body);
// placesDestination = data['places'] ?? [];
// update();
// } else {
// print('Error: ${response.statusCode} - ${response.reasonPhrase}');
// }
// } catch (e) {
// print('Exception: $e');
// }
// }
getAIKey(String key) async {
var res =
await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
return d[key].toString();
} else {}
}
// Future<void> 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';
// }
// // Construct the URL
// var url = Uri.parse(
// '${AppLink.searcMaps}?q=${Uri.encodeQueryComponent(placeDestinationController.text)}&limit=10&in=circle:${passengerLocation.latitude},${passengerLocation.longitude};r=50000&lang=$languageCode&apiKey=$k',
// );
// // Log the URL for debugging
// print(url);
// // box.remove(BoxName.placesDestination);
// try {
// // Make the API request
// var response = await CRUD().getHereMap(
// link: url.toString(),
// );
// // Log the response for debugging
// // Log.print('response: ${response}');
// // Check if the response is valid
// if (response != null && response['items'] != null) {
// placesDestination = response['items'];
// // Log.print('placesDestination: ${placesDestination}');
// placesDestination = response['items'];
// // box.write(BoxName.placesDestination, placesDestination);
// for (var i = 0; i < placesDestination.length; i++) {
// var res = placesDestination[i];
// // Extract fields with null safety
// var title = res['title']?.toString() ?? 'Unknown Place';
// var position = res['position'];
// var address = res['address']?['label'] ?? 'Unknown Address';
// if (position == null) {
// Log.print('Position is null for place: $title');
// continue; // Skip this place and continue with the next one
// }
// String latitude = position['lat']?.toString() ?? '0.0';
// String longitude = position['lng']?.toString() ?? '0.0';
// try {
// await savePlaceToServer(latitude, longitude, title, address);
// // Log.print('Place saved successfully: $title');
// } catch (e) {
// // Log.print('Failed to save place: $e');
// }
// } // todo save key in env then get key and use it
// } else {
// placesDestination = [];
// }
// } catch (e) {
// // Handle any errors that occur during the API request
// Log.print('Error fetching places: $e');
// placesDestination = [];
// }
// // Notify listeners that the state has changed
// 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=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}';
var response = await CRUD().getGoogleApi(link: url, payload: {});
placesStart = response['results'];
update();
}
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=250000&language=$languageCode&key=${AK.mapAPIKEY.toString()}';
try {
var response = await CRUD().getGoogleApi(link: url, payload: {});
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');
}
}
// داخل MapPassengerController
bool lowPerf = false;
Timer? _camThrottle;
DateTime _lastUiUpdate = DateTime.fromMillisecondsSinceEpoch(0);
// نادِها في onInit مثلاً
Future<void> detectPerfMode() async {
try {
if (GetPlatform.isAndroid) {
final info = await DeviceInfoPlugin().androidInfo;
final sdk = info.version.sdkInt ?? 0;
final ram = info.availableRamSize ?? 0;
lowPerf = (sdk < 28) || (ram > 0 && ram < 3 * 1024 * 1024 * 1024);
} else {
lowPerf = false;
}
} catch (_) {
lowPerf = false;
}
update();
}
// تحديث الكاميرا بثروتل
void onCameraMoveThrottled(CameraPosition pos) {
if (_camThrottle?.isActive ?? false) return;
_camThrottle = Timer(const Duration(milliseconds: 160), () {
// ضع فقط المنطق الضروري هنا لتقليل الحمل
int waypointsLength = Get.find<WayPointController>().wayPoints.length;
int index = wayPointIndex;
if (waypointsLength > 0) {
placesCoordinate[index] =
'${pos.target.latitude},${pos.target.longitude}';
}
newMyLocation = pos.target;
});
}
// تهيئة polylines خفيفة (استدعها بعد جلب المسار)
Set<Polyline> polyLinesLight = {};
List<LatLng> simplifyPolyline(List<LatLng> pts, double epsilonMeters) {
if (pts.length <= 2) return pts;
double _perpDist(LatLng p, LatLng a, LatLng b) {
// مسافة عمودية تقريبية بالأمتار
double _toRad(double d) => d * math.pi / 180.0;
// تحويل بسيط للمتر (تقريبي)
final x1 = a.longitude, y1 = a.latitude;
final x2 = b.longitude, y2 = b.latitude;
final x0 = p.longitude, y0 = p.latitude;
final num = ((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1).abs();
final den = math.sqrt(math.pow(y2 - y1, 2) + math.pow(x2 - x1, 2));
// تحويل درجات -> أمتار تقريبي (1 درجة ~ 111km)
final degDist = den == 0 ? 0 : num / den;
return degDist * 111000; // متر
}
List<LatLng> dp(int start, int end) {
double maxDist = 0;
int index = start;
for (int i = start + 1; i < end; i++) {
final d = _perpDist(pts[i], pts[start], pts[end]);
if (d > maxDist) {
maxDist = d;
index = i;
}
}
if (maxDist > epsilonMeters) {
final r1 = dp(start, index);
final r2 = dp(index, end);
return [...r1.sublist(0, r1.length - 1), ...r2];
} else {
return [pts[start], pts[end]];
}
}
return dp(0, pts.length - 1);
}
void buildLightPolylines(List<LatLng> originalPoints) {
final simplified = simplifyPolyline(originalPoints, lowPerf ? 12 : 3);
polyLinesLight = {
Polyline(
polylineId: const PolylineId('route_light'),
points: simplified,
width: lowPerf ? 4 : 6,
geodesic: true,
color: AppColor.primaryColor,
endCap: Cap.roundCap,
startCap: Cap.roundCap,
jointType: JointType.round,
),
};
update();
}
Future<void> savePlaceToServer(
String latitude, String longitude, String name, String rate) async {
var data = {
'latitude': latitude,
'longitude': longitude,
'name': name,
'rate': rate,
};
try {
CRUD().post(
link: AppLink.savePlacesServer,
payload: data,
);
} catch (e) {
print('Error: $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<String> parts = location.split(',');
double lat = double.parse(parts[0]);
double lng = double.parse(parts[1]);
return LatLng(lat, lng);
}
void clearPolyline() {
polyLines = [];
polylineCoordinates.clear();
// polylineCoordinates = [];
polylineCoordinatesPointsAll[0].clear();
polylineCoordinatesPointsAll[1].clear();
polylineCoordinatesPointsAll[2].clear();
polylineCoordinatesPointsAll[3].clear();
polylineCoordinatesPointsAll[4].clear();
isMarkersShown = false;
update();
}
void addCustomPicker() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio
// scale: 1.0,
);
BitmapDescriptor.asset(
config,
'assets/images/picker.png',
// mipmaps: false,
).then((value) {
markerIcon = value;
update();
});
}
void addCustomStartIcon() async {
// Create the marker with the resized image
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/A.png',
// mipmaps: false,
).then((value) {
startIcon = value;
update();
});
}
void addCustomEndIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/b.png',
// mipmaps: false,
).then((value) {
endIcon = value;
update();
});
}
void addCustomCarIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 35), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/car.png',
// mipmaps: false,
).then((value) {
carIcon = value;
update();
});
}
void addCustomMotoIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/moto1.png',
// mipmaps: false,
).then((value) {
motoIcon = value;
update();
});
}
void addCustomLadyIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/lady1.png',
// mipmaps: false,
).then((value) {
ladyIcon = value;
update();
});
}
void addCustomStepIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/brand.png',
// mipmaps: false,
).then((value) {
tripIcon = value;
update();
});
}
dialoge() {
Get.defaultDialog(
title: 'Location '.tr,
content: Container(
child: Column(
children: [
Text(
'We use location to get accurate and nearest driver for you'.tr,
style: AppStyle.title,
),
TextButton(
onPressed: () async {
// await Permission.location.request();
Get.back();
},
child: Text(
'Grant'.tr,
style: AppStyle.title,
),
)
],
),
),
);
}
double speed = 0;
Future<void> getLocation() async {
isLoading = true;
update();
bool serviceEnabled;
PermissionStatus permissionGranted;
// dialoge();
// Check if location services are enabled
serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
serviceEnabled = await location.requestService();
if (!serviceEnabled) {
// Location services are still not enabled, handle the error
return;
}
}
// Check if the app has permission to access location
permissionGranted = await location.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await location.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
// Location permission is still not granted, handle the error
return;
}
}
// Configure location accuracy
// LocationAccuracy desiredAccuracy = LocationAccuracy.high;
// Get the current location
LocationData _locationData = await location.getLocation();
passengerLocation =
(_locationData.latitude != null && _locationData.longitude != null
? LatLng(_locationData.latitude!, _locationData.longitude!)
: null)!;
getLocationArea(passengerLocation.latitude, passengerLocation.longitude);
Log.print('AppLink.endPoint: ${AppLink.endPoint}');
// Log.print('BoxName.serverChosen: ${box.read(BoxName.serverChosen)}');
newStartPointLocation = passengerLocation;
Log.print('passengerLocation: $passengerLocation');
speed = _locationData.speed!;
// //print location details
isLoading = false;
update();
}
LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
const double earthRadius = 6378137.0; // Earth's radius in meters
double latDelta = (radiusInMeters / earthRadius) * (180 / pi);
double lngDelta =
(radiusInMeters / (earthRadius * cos(pi * lat / 180))) * (180 / pi);
double minLat = lat - latDelta;
double maxLat = lat + latDelta;
double minLng = lng - lngDelta;
double maxLng = lng + lngDelta;
// Ensure the latitude is between -90 and 90
minLat = max(-90.0, minLat);
maxLat = min(90.0, maxLat);
// Ensure the longitude is between -180 and 180
minLng = (minLng + 180) % 360 - 180;
maxLng = (maxLng + 180) % 360 - 180;
// Ensure the bounds are in the correct order
if (minLng > maxLng) {
double temp = minLng;
minLng = maxLng;
maxLng = temp;
}
return LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
);
}
GoogleMapController? mapController;
void onMapCreated(GoogleMapController controller) {
// myLocation = Get.find<LocationController>().location as LatLng;
// myLocation = myLocation;
mapController = controller;
controller.getVisibleRegion();
controller.animateCamera(
CameraUpdate.newLatLng(passengerLocation),
);
// Future.delayed(const Duration(milliseconds: 500), () {
// markers.forEach((marker) {
// controller.showMarkerInfoWindow(marker.markerId);
// });
// });
update();
}
// void startMarkerReloading() {
// int count = 0;
// markerReloadingTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
// reloadMarkers();
//
// count++;
// if (count == 10) {
// timer.cancel();
// }
// });
// }
bool reloadStartApp = false;
int reloadCount = 0;
startMarkerReloading() async {
if (reloadStartApp == false) {
Timer.periodic(const Duration(seconds: 3), (timer) async {
reloadCount++;
Log.print('reloadCount: $reloadCount');
if (rideConfirm == false) {
clearMarkersExceptStartEnd();
// _smoothlyUpdateMarker();
// startCarLocationSearch(box.read(BoxName.carType));
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 5000);
await getNearestDriverByPassengerLocation();
// Log.print('reloadMarkers: from startMarkerReloading');
} else {
// runWhenRideIsBegin();
}
if (reloadCount >= 6) {
reloadStartApp = true;
timer.cancel(); // Stop the timer after 5 reloads
}
});
}
}
String durationByPassenger = '';
late DateTime newTime1 = DateTime.now();
late DateTime timeFromDriverToPassenger = DateTime.now();
String distanceByPassenger = '';
late Duration durationFromDriverToPassenger;
double nearestDistance = double.infinity;
// Future<CarLocation?> getNearestDriverByPassengerLocation() async {
// if (polyLines.isEmpty || data.isEmpty) {
// return null; // Early return if data is empty
// }
// if (!rideConfirm) {
// if (dataCarsLocationByPassenger != 'failure') {
// if (dataCarsLocationByPassenger != null) {
// if (dataCarsLocationByPassenger['message'].length > 0) {
// double nearestDistance = double
// .infinity; // Initialize nearest distance to a large number
// CarLocation? nearestCar;
// for (var i = 0;
// i < dataCarsLocationByPassenger['message'].length;
// i++) {
// var carLocation = dataCarsLocationByPassenger['message'][i];
// // Calculate the distance between passenger's location and current driver's location
// final distance = Geolocator.distanceBetween(
// passengerLocation.latitude,
// passengerLocation.longitude,
// double.parse(carLocation['latitude']),
// double.parse(carLocation['longitude']),
// );
// // Calculate duration assuming an average speed of 25 km/h (adjust as needed)
// int durationToPassenger =
// (distance * 25 * (1000 / 3600)).round(); // 25 km/h in m/s
// // Update the UI with the distance and duration for each car
// update();
// // If this distance is smaller than the nearest distance found so far, update nearestCar
// if (distance < nearestDistance) {
// nearestDistance = distance;
// nearestCar = CarLocation(
// distance: distance,
// duration: durationToPassenger.toDouble(),
// id: carLocation['driver_id'],
// latitude: double.parse(carLocation['latitude']),
// longitude: double.parse(carLocation['longitude']),
// );
// // Update the UI with the nearest driver
// update();
// }
// }
// // Return the nearest car found
// return nearestCar;
// }
// }
// }
// }
// // Return null if no drivers are found or if ride is confirmed
// return null;
// }
Future<CarLocation?> getNearestDriverByPassengerLocation() async {
if (!rideConfirm) {
if (dataCarsLocationByPassenger != 'failure' &&
dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger['message'] != null &&
dataCarsLocationByPassenger['message'].length > 0) {
double nearestDistance = double.infinity; // Initialize nearest distance
CarLocation? nearestCar;
for (var i = 0;
i < dataCarsLocationByPassenger['message'].length;
i++) {
var carLocation = dataCarsLocationByPassenger['message'][i];
// Log.print('carLocation: $carLocation');
try {
// Calculate distance between passenger's location and current driver's location
final distance = Geolocator.distanceBetween(
passengerLocation.latitude,
passengerLocation.longitude,
double.parse(carLocation['latitude']),
double.parse(carLocation['longitude']),
);
// Calculate duration assuming an average speed of 25 km/h (adjust as needed)
int durationToPassenger = (distance / 1000 / 25 * 3600).round();
// Log.print('distance: $distance');
// Log.print('durationToPassenger: $durationToPassenger');
// Log.print('passengerLocation: $passengerLocation');
// Log.print('carLocation: $carLocation');
// Log.print('distance: $distance meters');
// Log.print('durationToPassenger: $durationToPassenger seconds');
// Update the UI with the distance and duration for each car
update();
// If this distance is smaller than the nearest distance found so far, update nearestCar
if (distance < nearestDistance) {
nearestDistance = distance;
nearestCar = CarLocation(
distance: distance,
duration: durationToPassenger.toDouble(),
id: carLocation['driver_id'],
latitude: double.parse(carLocation['latitude']),
longitude: double.parse(carLocation['longitude']),
);
// Log.print('nearestCar: $nearestCar');
// Update the UI with the nearest driver
update();
}
} catch (e) {
Log.print('Error calculating distance/duration: $e');
}
}
// Return the nearest car found
return nearestCar;
}
}
// Return null if no drivers are found or if ride is confirmed
return null;
}
getNearestDriverByPassengerLocationAPIGOOGLE() async {
if (polyLines.isEmpty || data.isEmpty) {
return null; // Early return if data is empty
}
if (!rideConfirm) {
double nearestDistance = double.infinity;
if (dataCarsLocationByPassenger != 'failure') {
if (dataCarsLocationByPassenger['message'].length > 0) {
for (var i = 0;
i < dataCarsLocationByPassenger['message'].length;
i++) {
var carLocation = dataCarsLocationByPassenger['message'][i];
// }
// isloading = true;
update();
// Make API request to get exact distance and duration
String apiUrl =
'${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${passengerLocation.latitude},${passengerLocation.longitude}&units=metric&key=${AK.mapAPIKEY}';
var response = await CRUD().getGoogleApi(link: apiUrl, payload: {});
if (response != null && response['status'] == "OK") {
var data = response;
// Extract distance and duration from the response and handle accordingly
int distance1 =
data['rows'][0]['elements'][0]['distance']['value'];
distanceByPassenger =
data['rows'][0]['elements'][0]['distance']['text'];
durationToPassenger =
data['rows'][0]['elements'][0]['duration']['value'];
durationFromDriverToPassenger =
Duration(seconds: durationToPassenger.toInt());
newTime1 = currentTime.add(durationFromDriverToPassenger);
timeFromDriverToPassenger =
newTime1.add(Duration(minutes: 2.toInt()));
durationByPassenger =
data['rows'][0]['elements'][0]['duration']['text'];
update();
if (distance1 < nearestDistance) {
nearestDistance = distance1.toDouble();
nearestCar = CarLocation(
distance: distance1.toDouble(),
duration: durationToPassenger.toDouble(),
id: carLocation['driver_id'],
latitude: double.parse(carLocation['latitude']),
longitude: double.parse(carLocation['longitude']),
);
// isloading = false;
update();
}
}
// Handle the distance and duration as needed
else {
// 'Failed to retrieve distance and duration: ${response['status']}');
Log.print('${response['status']}: ${response['status']}}');
// Handle the failure case
}
}
}
}
}
}
calculateDistanceBetweenPassengerAndDriverBeforeCancelRide() async {
await getDriverCarsLocationToPassengerAfterApplied();
double distance = Geolocator.distanceBetween(
passengerLocation.latitude,
passengerLocation.longitude,
driverCarsLocationToPassengerAfterApplied.last.latitude,
driverCarsLocationToPassengerAfterApplied.last.longitude,
);
if (distance > 500) {
isCancelRidePageShown = true;
update();
} else {
Get.defaultDialog(
barrierDismissible: false,
title: 'The Driver Will be in your location soon .'.tr,
middleText: 'The distance less than 500 meter.'.tr,
confirm: Column(
children: [
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Ok'.tr,
onPressed: () {
Get.back();
},
),
MyElevatedButton(
kolor: AppColor.redColor,
title: 'No, I want to cancel this trip'.tr,
onPressed: () {
Get.back();
MyDialog().getDialog(
'Attention'.tr,
'You will be charged for the cost of the driver coming to your location.'
.tr,
() async {
Get.back();
Get.find<PaymentController>()
.payToDriverForCancelAfterAppliedAndHeNearYou(rideId);
// isCancelRidePageShown = true;
// update();
},
);
},
),
],
),
);
// cancel: MyElevatedButton(
// title: 'No.Iwant Cancel Trip.'.tr, onPressed: () {}));
}
}
List<double> headingAngles = [];
double calculateAngleBetweenLocations(LatLng start, LatLng end) {
double startLat = start.latitude * math.pi / 180;
double startLon = start.longitude * math.pi / 180;
double endLat = end.latitude * math.pi / 180;
double endLon = end.longitude * math.pi / 180;
double dLon = endLon - startLon;
double y = math.sin(dLon) * cos(endLat);
double x = cos(startLat) * math.sin(endLat) -
math.sin(startLat) * cos(endLat) * cos(dLon);
double angle = math.atan2(y, x);
double angleDegrees = angle * 180 / math.pi;
return angleDegrees;
}
late LatLngBounds boundsData;
late String startNameAddress = '';
late String endNameAddress = '';
List<Map<String, dynamic>> stopPoints = [];
void removeStop(Map<String, dynamic> stop) {
stopPoints.remove(stop);
update(); // Trigger a rebuild of the UI
}
getDirectionMap(String origin, destination,
[List<String> waypoints = const []]) async {
isLoading = true;
update();
remainingTime = 25; //to make cancel every call
// startCarLocationSearch(box.read(BoxName.carType));
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 5000);
// await getCarsLocationByPassengerAndReloadMarker();
var coordDestination = destination.split(',');
double latPassengerDestination = double.parse(coordDestination[0]);
double lngPassengerDestination = double.parse(coordDestination[1]);
myDestination = LatLng(latPassengerDestination, lngPassengerDestination);
if (origin.isEmpty) {
origin =
'${passengerLocation.latitude.toString().split(',')[0]},${passengerLocation.longitude.toString().split(',')[1]}'; //todo
}
isLoading = false;
update();
var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang) ?? 'ar'}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
if (waypoints.isNotEmpty) {
String formattedWaypoints = waypoints.join('|');
url += '&waypoints=$formattedWaypoints';
}
var response = await CRUD().getGoogleApi(link: url, payload: {});
data = response['routes'][0]['legs'];
box.remove(BoxName.tripData);
box.write(BoxName.tripData, response);
startNameAddress = shortenAddress(data[0]['start_address']);
// print('data[0][start_address]: ${data[0]['start_address']}');
endNameAddress = shortenAddress(data[0]['end_address']);
isLoading = false;
newStartPointLocation = LatLng(
data[0]["start_location"]['lat'], data[0]["start_location"]['lng']);
durationToRide = data[0]['duration']['value'];
final points =
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
for (int i = 0; i < points.length; i++) {
double lat = points[i][0].toDouble();
double lng = points[i][1].toDouble();
polylineCoordinates.add(LatLng(lat, lng));
}
// Define the northeast and southwest coordinates
// Define the northeast and southwest coordinates
final bounds = response["routes"][0]["bounds"];
LatLng northeast =
LatLng(bounds['northeast']['lat'], bounds['northeast']['lng']);
LatLng southwest =
LatLng(bounds['southwest']['lat'], bounds['southwest']['lng']);
// Create the LatLngBounds object
LatLngBounds boundsData =
LatLngBounds(northeast: northeast, southwest: southwest);
// Fit the camera to the bounds
var cameraUpdate = CameraUpdate.newLatLngBounds(boundsData, 130);
mapController!.animateCamera(cameraUpdate);
// getDistanceFromText(data[0]['distance']['text']);
double distanceOfTrip = (data[0]['distance']['value']) / 1000;
distance = distanceOfTrip;
durationToAdd = Duration(seconds: durationToRide);
hours = durationToAdd.inHours;
minutes = (durationToAdd.inMinutes % 60).round();
// updateCameraForDistanceAfterGetMap();
markers.clear();
update();
markers.add(
Marker(
markerId: const MarkerId('start'),
position: newStartPointLocation,
icon: startIcon,
infoWindow: InfoWindow(
title: startNameAddress,
snippet: '',
),
),
);
// Add end marker
markers.add(
Marker(
markerId: const MarkerId('end'),
position: LatLng(
data[0]["end_location"]['lat'], data[0]["end_location"]['lng']),
icon: endIcon,
// infoWindow: InfoWindow(
// title: endNameAddress,
// snippet:
// '$distance ${'KM'.tr} ⌛ ${hours > 0 ? '${'Your Ride Duration is '.tr}$hours ${'H and'.tr} $minutes ${'m'.tr}' : '${'Your Ride Duration is '.tr} $minutes ${'m'.tr}'}'),
),
);
// // Show info windows automatically
// Future.delayed(const Duration(milliseconds: 500), () {
// mapController?.showMarkerInfoWindow(const MarkerId('start'));
// });
// Future.delayed(const Duration(milliseconds: 500), () {
// mapController?.showMarkerInfoWindow(const MarkerId('end'));
// });
// update();
if (polyLines.isNotEmpty) {
clearPolyline();
} else {
// الآن بإمكانك قراءة القيمة من الـBox في أي مكان:
bool lowEndMode = box.read(BoxName.lowEndMode) ?? true;
animatePolylineLayered(polylineCoordinates);
rideConfirm = false;
isMarkersShown = true;
update();
}
}
// 1) تقليل النقاط إلى حد أقصى 30 نقطة (مع بداية ونهاية محفوظة وتوزيع متساوٍ)
List<LatLng> _downsampleEven(List<LatLng> coords, {int maxPoints = 30}) {
if (coords.isEmpty) return const [];
if (coords.length <= maxPoints) return List<LatLng>.from(coords);
final int n = coords.length;
final int keep = maxPoints.clamp(2, n);
final List<int> idx = [];
for (int i = 0; i < keep; i++) {
final double pos = i * (n - 1) / (keep - 1);
idx.add(pos.round());
}
final seen = <int>{};
final List<LatLng> out = [];
for (final i in idx) {
if (seen.add(i)) out.add(coords[i]);
}
if (out.first != coords.first) out.insert(0, coords.first);
if (out.last != coords.last) out.add(coords.last);
while (out.length > maxPoints) {
out.removeAt(out.length ~/ 2);
}
return out;
}
// 2) رسم متدرّج بطبقات متراكبة (بدون حذف)، برونزي ↔ أخضر، مع zIndex وعرض مختلف
Future<void> animatePolylineLayered(List<LatLng> coordinates,
{int layersCount = 8, int stepDelayMs = 10}) async {
// امسح أي طبقات قديمة فقط الخاصة بالطريق
polyLines.removeWhere((p) => p.polylineId.value.startsWith('route_layer_'));
update();
final List<LatLng> coords = _downsampleEven(coordinates, maxPoints: 20);
if (coords.length < 2) return;
// ألوان مع شفافية خفيفة للتمييز
Color bronze([int alpha = 220]) => const Color(0xFFCD7F32).withAlpha(alpha);
Color green([int alpha = 220]) => Colors.green.withAlpha(alpha);
Color _layerColor(int layer) => (layer % 2 == 0) ? bronze() : green();
// عرض الخط: البرونزي أعرض، الأخضر أنحف
int _layerWidth(int layer) => (layer % 2 == 0) ? 6 : 4;
// لكل طبقة: أنشئ Polyline بهوية فريدة وزي إندكس أعلى من السابقة
for (int layer = 0; layer < layersCount; layer++) {
final id = PolylineId('route_layer_$layer');
polyLines.add(Polyline(
polylineId: id,
points: const [],
width: _layerWidth(layer),
color: _layerColor(layer),
zIndex: layer, // مهم لإظهار جميع الطبقات
endCap: Cap.roundCap,
startCap: Cap.roundCap,
geodesic: true,
visible: true,
));
}
update();
// نبني كل طبقة تدريجيًا فوق التي قبلها — بدون مسح الطبقات السابقة
for (int layer = 0; layer < layersCount; layer++) {
final id = PolylineId('route_layer_$layer');
final List<LatLng> growing = [];
for (int i = 0; i < coords.length; i++) {
growing.add(coords[i]);
// حدّث فقط هذه الطبقة
polyLines.removeWhere((p) => p.polylineId == id);
polyLines.add(Polyline(
polylineId: id,
points: List<LatLng>.from(growing),
width: _layerWidth(layer),
color: _layerColor(layer),
zIndex: layer,
endCap: Cap.roundCap,
startCap: Cap.roundCap,
geodesic: true,
visible: true,
));
update();
await Future.delayed(Duration(milliseconds: stepDelayMs));
}
// مهلة خفيفة بين الطبقات عشان يبان التبديل
await Future.delayed(const Duration(milliseconds: 120));
}
}
String shortenAddress(String fullAddress) {
// Split the address into parts
List<String> parts = fullAddress.split('،');
// Remove any leading or trailing whitespace from each part
parts = parts.map((part) => part.trim()).toList();
// Remove any empty parts
parts = parts.where((part) => part.isNotEmpty).toList();
// Initialize the short address
String shortAddress = '';
if (parts.isNotEmpty) {
// Add the first part (usually the most specific location)
shortAddress += parts[0];
}
if (parts.length > 2) {
// Add the district or area name (usually the third part in Arabic format)
shortAddress += '، ${parts[2]}';
} else if (parts.length > 1) {
// Add the second part for English or shorter addresses
shortAddress += '، ${parts[1]}';
}
// Add the country (usually the last part)
if (parts.length > 1) {
shortAddress += '، ${parts.last}';
}
// Remove any part that's just numbers (like postal codes)
shortAddress = shortAddress
.split('،')
.where((part) => !RegExp(r'^[0-9 ]+$').hasMatch(part.trim()))
.join('،');
// Check if the address is in English
bool isEnglish =
RegExp(r'^[a-zA-Z0-9 ]+$').hasMatch(shortAddress.replaceAll('،', ''));
if (isEnglish) {
// Further processing for English addresses
List<String> englishParts = shortAddress.split('،');
if (englishParts.length > 2) {
shortAddress =
'${englishParts[0]}، ${englishParts[1]}، ${englishParts.last}';
} else if (englishParts.length > 1) {
shortAddress = '${englishParts[0]}، ${englishParts.last}';
}
}
return shortAddress;
}
double distanceOfDestination = 0;
bool haveSteps = false;
late LatLng latestPosition;
getMapPoints(String originSteps, String destinationSteps, int index) async {
isWayPointStopsSheetUtilGetMap = false;
// haveSteps = true;
// startCarLocationSearch(box.read(BoxName.carType));
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 7000);
// await getCarsLocationByPassengerAndReloadMarker();
// isLoading = true;
update();
var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destinationSteps&origin=$originSteps&key=${AK.mapAPIKEY}');
var response = await CRUD().getGoogleApi(link: url, payload: {});
data = response['routes'][0]['legs'];
// isLoading = false;
int durationToRide0 = data[0]['duration']['value'];
durationToRide = durationToRide + durationToRide0;
distance = distanceOfDestination + (data[0]['distance']['value']) / 1000;
update();
final points =
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
for (int i = 0; i < points.length; i++) {
if (points[i][0].toString() != '') {
double lat = points[i][0].toDouble();
double lng = points[i][1].toDouble();
polylineCoordinatesPointsAll[index].add(LatLng(lat, lng));
}
}
// Define the northeast and southwest coordinates
if (polyLines.isNotEmpty) {
// clearPolyline();
} else {
var polyline = Polyline(
polylineId: PolylineId(response["routes"][0]["summary"]),
points: polylineCoordinatesPointsAll[index],
width: 10,
color: Colors.blue,
);
polyLines.add(polyline);
rideConfirm = false;
// isMarkersShown = true;
update();
}
}
void updateCameraForDistanceAfterGetMap() {
LatLng coord1 = LatLng(
double.parse(coordinatesWithoutEmpty.first.split(',')[0]),
double.parse(coordinatesWithoutEmpty.first.split(',')[1]));
LatLng coord2 = LatLng(
double.parse(coordinatesWithoutEmpty.last.split(',')[0]),
double.parse(coordinatesWithoutEmpty.last.split(',')[1]));
LatLng northeast;
LatLng southwest;
if (coord1.latitude > coord2.latitude) {
northeast = coord1;
southwest = coord2;
} else {
northeast = coord2;
southwest = coord1;
}
// Create the LatLngBounds object
LatLngBounds bounds =
LatLngBounds(northeast: northeast, southwest: southwest);
// Fit the camera to the bounds
var cameraUpdate = CameraUpdate.newLatLngBounds(bounds, 180);
mapController!.animateCamera(cameraUpdate);
update();
}
int selectedIndex = -1; // Initialize with no selection
void selectCarFromList(int index) {
selectedIndex = index; // Update selected index
carTypes.forEach(
(element) => element.isSelected = false); // Reset selection flags
carTypes[index].isSelected = true;
update();
}
showBottomSheet1() async {
await bottomSheet();
isBottomSheetShown = true;
heightBottomSheetShown = 250;
update();
}
final promo = TextEditingController();
bool promoTaken = false;
applyPromoCodeToPassenger(BuildContext context) async {
if (promoTaken == true) {
MyDialog().getDialog(
'Promo Already Used'.tr,
'You have already used this promo code.'.tr,
() => Get.back(),
);
return;
}
if (!promoFormKey.currentState!.validate()) return;
// عتبات تفعيل البرومو بالليرة السورية
const double minPromoLowSYP = 16000; // Speed / Balash
const double minPromoHighSYP = 20000; // Comfort / Electric / Lady
try {
final value = await CRUD().get(
link: AppLink.getPassengersPromo,
payload: {'promo_code': promo.text},
);
if (value == 'failure') {
MyDialog().getDialog(
'Promo Ended'.tr,
'The promotion period has ended.'.tr,
() => Get.back(),
);
return;
}
// تحقق أولي: هل عندنا أي فئة حاليًا فوق العتبات لتفعيل البرومو؟
final bool eligibleNow = (totalPassengerSpeed >= minPromoLowSYP) ||
(totalPassengerBalash >= minPromoLowSYP) ||
(totalPassengerComfort >= minPromoHighSYP) ||
(totalPassengerElectric >= minPromoHighSYP) ||
(totalPassengerLady >= minPromoHighSYP);
if (!eligibleNow) {
Get.snackbar(
'Lowest Price Achieved'.tr, 'Cannot apply further discounts.'.tr,
backgroundColor: AppColor.yellowColor);
return;
}
final decode = jsonDecode(value);
if (decode["status"] != "success") {
MyDialog().getDialog(
'Promo Ended'.tr,
'The promotion period has ended.'.tr,
() => Get.back(),
);
return;
}
Get.snackbar('Promo Code Accepted'.tr, '',
backgroundColor: AppColor.greenColor);
final firstElement = decode["message"][0];
final int discountPercentage =
int.tryParse(firstElement['amount'].toString()) ?? 0;
// قيمة المحفظة (قد تكون سالبة)
final double walletVal = double.tryParse(
box.read(BoxName.passengerWalletTotal)?.toString() ?? '0') ??
0.0;
// دالة مساعدة: تطبق الخصم لكل فئة حسب عتبتها
double _applyDiscountPerTier({
required double fare,
required double minThreshold,
required bool isWalletNegative,
}) {
if (fare < minThreshold) return fare; // غير مؤهل
// منطقك السابق:
// - إذا المحفظة سالبة: fare + (-wallet) - (fare * pct)
// - إذا المحفظة ليست سالبة: fare - (fare * pct)
final double discount = fare * (discountPercentage / 100.0);
if (isWalletNegative) {
final double neg = (-1) * walletVal; // walletVal < 0
return (fare + neg - discount).clamp(0, double.infinity);
} else {
return (fare - discount).clamp(0, double.infinity);
}
}
final bool isWalletNegative = walletVal < 0;
// Comfort
totalPassengerComfort = _applyDiscountPerTier(
fare: totalPassengerComfort,
minThreshold: minPromoHighSYP,
isWalletNegative: isWalletNegative,
);
// Electric
totalPassengerElectric = _applyDiscountPerTier(
fare: totalPassengerElectric,
minThreshold: minPromoHighSYP,
isWalletNegative: isWalletNegative,
);
// Lady
totalPassengerLady = _applyDiscountPerTier(
fare: totalPassengerLady,
minThreshold: minPromoHighSYP,
isWalletNegative: isWalletNegative,
);
// Speed
totalPassengerSpeed = _applyDiscountPerTier(
fare: totalPassengerSpeed,
minThreshold: minPromoLowSYP,
isWalletNegative: isWalletNegative,
);
// Balash
totalPassengerBalash = _applyDiscountPerTier(
fare: totalPassengerBalash,
minThreshold: minPromoLowSYP,
isWalletNegative: isWalletNegative,
);
// (اختياري) لو عندك فئات أخرى تريد تطبيق البرومو عليها، أضفها بنفس النمط.
// تعديل دخل السائق حسب نسبة البرومو (كما كان في كودك)
totalDriver = totalDriver - (totalDriver * discountPercentage / 100.0);
promoTaken = true;
update();
// مؤثرات نجاح
Confetti.launch(
context,
options: const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
);
Get.back();
await Future.delayed(const Duration(milliseconds: 120));
} catch (e) {
Get.snackbar('Error'.tr, e.toString(),
backgroundColor: AppColor.redColor);
}
}
double getDistanceFromText(String distanceText) {
// Remove any non-digit characters from the distance text
String distanceValue = distanceText.replaceAll(RegExp(r'[^0-9.]+'), '');
// Parse the extracted numerical value as a double
double distance = double.parse(distanceValue);
return distance;
}
double costForDriver = 0;
double totalPassengerSpeed = 0;
double totalPassengerBalash = 0;
double totalPassengerElectric = 0;
double totalPassengerLady = 0;
double totalPassengerRayehGai = 0;
double totalPassengerRayehGaiComfort = 0;
double totalPassengerRayehGaiBalash = 0;
Future bottomSheet() async {
if (data.isEmpty) return;
// === إعدادات عامة ===
const double minFareSYP = 16000; // حد أدنى
const double minBillableKm = 0.3; // حد أدنى للمسافة المفوترة
const double ladyFlatAddon = 2000; // إضافة ثابتة لـ Lady
const double airportAddonSYP = 20000; // إضافة المطار
// كهرباء
const double electricPerKmUplift = 400; // زيادة/كم
const double electricFlatAddon = 1000; // زيادة ثابتة
// Long Speed
const double longSpeedThresholdKm = 40.0;
const double longSpeedPerKm = 2600.0; // Speed عند >40كم
// قواعد الرحلات البعيدة للدقائق (تعمل لكل الأوقات)
const double mediumDistThresholdKm = 25.0; // >25كم
const double longDistThresholdKm = 35.0; // >35كم
const double longTripPerMin = 600.0;
const int minuteCapMedium = 60; // سقف دقائق عند >25كم
const int minuteCapLong = 60; // سقف دقائق عند >35كم
const int freeMinutesLong = 10; // عفو 10 دقائق عند >35كم
// تخفيضات المسافات الكبيرة للفئات غير Speed
const double extraReduction100 = 0.07; // +7% فوق تخفيض >40كم للرحلات >100كم
const double maxReductionCap = 0.35; // سقف 35% كحد أقصى
// ====== زمن الرحلة ======
durationToAdd = Duration(seconds: durationToRide);
hours = durationToAdd.inHours;
minutes = (durationToAdd.inMinutes % 60).round();
final DateTime currentTime = DateTime.now();
newTime = currentTime.add(durationToAdd);
averageDuration = (durationToRide / 60) / distance;
final int totalMinutes = (durationToRide / 60).floor();
// ====== أدوات مساعدة ======
bool _isAirport(String s) {
final t = s.toLowerCase();
return t.contains('airport') ||
s.contains('مطار') ||
s.contains('المطار');
}
bool _isClub(String s) {
final t = s.toLowerCase();
return t.contains('club') ||
t.contains('nightclub') ||
t.contains('night club') ||
s.contains('ديسكو') ||
s.contains('ملهى ليلي');
}
// أسعار الدقيقة من السيرفر
final double naturePerMin = naturePrice; // طبيعي
final double latePerMin = latePrice; // ليل
final double heavyPerMin = heavyPrice; // ذروة
// سعر الدقيقة حسب الوقت (أساس قبل قواعد المسافة)
double _perMinuteByTime(DateTime now, bool clubCtx) {
final h = now.hour;
if (h >= 21 || h < 1) return latePerMin; // ليل
if (h >= 1 && h < 5) return clubCtx ? (latePerMin * 2) : latePerMin;
if (h >= 14 && h <= 17) return heavyPerMin; // ذروة
return naturePerMin; // طبيعي
}
// حد أدنى
double _applyMinFare(double fare) =>
(fare < minFareSYP) ? minFareSYP : fare;
// عمولة الراكب (kazan من السيرفر)
double _withCommission(double base) =>
(base * (1 + kazan / 100)).ceilToDouble();
// ====== سياق ======
final bool airportCtx =
_isAirport(startNameAddress) || _isAirport(endNameAddress);
final bool clubCtx = _isClub(startNameAddress) || _isClub(endNameAddress);
// ====== مسافة مفوترة ======
final double billableDistance =
(distance < minBillableKm) ? minBillableKm : distance;
// ====== Speed (قصير/طويل) ======
final bool isLongSpeed = billableDistance > longSpeedThresholdKm;
final double perKmSpeedBaseFromServer =
speedPrice; // مثال: 2900 يأتي من السيرفر
final double perKmSpeed =
isLongSpeed ? longSpeedPerKm : perKmSpeedBaseFromServer;
// ====== تخفيضات الفئات الأخرى حسب بُعد الرحلة ======
// نسبة التخفيض عند >40كم محسوبة مقارنة بسعر Speed الأساسي القادم من السيرفر
double reductionPct40 = 0.0;
if (perKmSpeedBaseFromServer > 0) {
reductionPct40 = (1.0 - (longSpeedPerKm / perKmSpeedBaseFromServer))
.clamp(0.0, maxReductionCap);
}
// نسبة التخفيض عند >100كم
final double reductionPct100 =
(reductionPct40 + extraReduction100).clamp(0.0, maxReductionCap);
// نحدد أي تخفيض سنستخدمه
double distanceReduction = 0.0; // لا تخفيض افتراضيًا
if (billableDistance > 100.0) {
distanceReduction = reductionPct100;
} else if (billableDistance > 40.0) {
distanceReduction = reductionPct40;
}
// ====== منطق الدقيقة يعمل لكل الأوقات ويتكيّف مع المسافة ======
double effectivePerMin = _perMinuteByTime(currentTime, clubCtx);
int billableMinutes = totalMinutes;
if (billableDistance > longDistThresholdKm) {
// >35كم: 600/د + سقف 60 + عفو 10
effectivePerMin = longTripPerMin;
final int capped =
(billableMinutes > minuteCapLong) ? minuteCapLong : billableMinutes;
billableMinutes = capped - freeMinutesLong;
if (billableMinutes < 0) billableMinutes = 0;
} else if (billableDistance > mediumDistThresholdKm) {
// >25كم و≤35كم: 600/د + سقف 60 (بدون عفو)
effectivePerMin = longTripPerMin;
billableMinutes = (billableMinutes > minuteCapMedium)
? minuteCapMedium
: billableMinutes;
}
// ≤25كم: نبقي السعر حسب الوقت بدون سقف/عفو
// ====== أسعار/كم قبل التخفيض ======
final double perKmComfortRaw = comfortPrice;
final double perKmDelivery = deliveryPrice;
final double perKmVanRaw =
(familyPrice > 0 ? familyPrice : (speedPrice + 1300));
// Electric مبني على Comfort
final double perKmElectricRaw = perKmComfortRaw + electricPerKmUplift;
// ====== تطبيق التخفيضات على الفئات (نفس نسبة Speed للبعيد) ======
// ملاحظة: Balash يتبع Speed دائمًا (Speed-500) فلا حاجة لتخفيض إضافي له.
double perKmComfort = perKmComfortRaw * (1.0 - distanceReduction);
double perKmElectric = perKmElectricRaw * (1.0 - distanceReduction);
double perKmVan = perKmVanRaw * (1.0 - distanceReduction);
// نضمن عدم الهبوط تحت الصفر
perKmComfort = perKmComfort.clamp(0, double.infinity);
perKmElectric = perKmElectric.clamp(0, double.infinity);
perKmVan = perKmVan.clamp(0, double.infinity);
// Awfar/Balash: مرتبط بسرعة Speed بعد التفعيل
final double perKmBalash = (perKmSpeed - 500).clamp(0, double.infinity);
// ====== دوال الاحتساب ======
double _oneWayFare({
required double perKm,
required bool isLady,
double flatAddon = 0,
}) {
double fare = billableDistance * perKm;
fare +=
billableMinutes * effectivePerMin; // دقائق بعد السقف/العفو إن وُجد
fare += flatAddon;
if (isLady) fare += ladyFlatAddon;
if (airportCtx) fare += airportAddonSYP;
return _applyMinFare(fare);
}
double _roundTripFare({required double perKm}) {
// خصم 40% لمسافة إياب واحدة + زمن مضاعف (بنفس قواعد الدقيقة المعدّلة)
double distPart =
(billableDistance * 2 * perKm) - ((billableDistance * perKm) * 0.4);
double timePart = (billableMinutes * 2) * effectivePerMin;
double fare = distPart + timePart;
if (airportCtx) fare += airportAddonSYP;
return _applyMinFare(fare);
}
// ====== حساب كل الفئات (Base قبل العمولة) ======
final double costSpeed = _oneWayFare(perKm: perKmSpeed, isLady: false);
final double costBalash = _oneWayFare(perKm: perKmBalash, isLady: false);
final double costComfort = _oneWayFare(perKm: perKmComfort, isLady: false);
final double costElectric = _oneWayFare(
perKm: perKmElectric, isLady: false, flatAddon: electricFlatAddon);
final double costDelivery =
_oneWayFare(perKm: perKmDelivery, isLady: false);
final double costLady = _oneWayFare(
perKm: perKmComfort,
isLady: true); // Lady تعتمد Comfort بعد التخفيض + إضافة ثابتة
final double costVan = _oneWayFare(perKm: perKmVan, isLady: false);
final double costRayehGai = _roundTripFare(perKm: perKmSpeed);
final double costRayehGaiComfort = _roundTripFare(perKm: perKmComfort);
final double costRayehGaiBalash = _roundTripFare(perKm: perKmBalash);
// ====== أسعار الراكب بعد العمولة (kazan من السيرفر) ======
totalPassengerSpeed = _withCommission(costSpeed);
totalPassengerBalash = _withCommission(costBalash);
totalPassengerComfort = _withCommission(costComfort);
totalPassengerElectric = _withCommission(costElectric);
totalPassengerLady = _withCommission(costLady);
totalPassengerScooter = _withCommission(costDelivery);
totalPassengerVan = _withCommission(costVan);
totalPassengerRayehGai = _withCommission(costRayehGai);
totalPassengerRayehGaiComfort = _withCommission(costRayehGaiComfort);
totalPassengerRayehGaiBalash = _withCommission(costRayehGaiBalash);
// افتراضي للعرض
totalPassenger = totalPassengerSpeed;
totalCostPassenger = totalPassenger;
// ====== دعم رصيد محفظة سلبي ======
try {
final walletStr = box.read(BoxName.passengerWalletTotal).toString();
final walletVal = double.tryParse(walletStr) ?? 0.0;
if (walletVal < 0) {
final neg = (-1) * walletVal;
totalPassenger += neg;
totalPassengerComfort += neg;
totalPassengerElectric += neg;
totalPassengerLady += neg;
totalPassengerBalash += neg;
totalPassengerScooter += neg;
totalPassengerRayehGai += neg;
totalPassengerRayehGaiComfort += neg;
totalPassengerRayehGaiBalash += neg;
totalPassengerVan += neg;
}
} catch (_) {}
update();
changeBottomSheetShown();
}
addToken() async {
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
await CRUD()
.post(link: "${AppLink.server}/ride/firebase/add.php", payload: {
'token': (box.read(BoxName.tokenFCM.toString())),
'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint
});
CRUD().postWallet(
link: "${AppLink.seferPaymentServer}/ride/firebase/add.php",
payload: {
'token': (box.read(BoxName.tokenFCM.toString())),
'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint
});
// CRUD().post(
// link: "${AppLink.IntaleqGizaServer}/ride/firebase/add.php",
// payload: {
// 'token': (box.read(BoxName.tokenFCM.toString())),
// 'passengerID': box.read(BoxName.passengerID).toString(),
// "fingerPrint": fingerPrint
// });
}
List<LatLng> polylineCoordinate = [];
String? cardNumber;
void readyWayPoints() {
hintTextwayPointStringAll = [
hintTextwayPoint0,
hintTextwayPoint1,
hintTextwayPoint2,
hintTextwayPoint3,
hintTextwayPoint4,
];
polylineCoordinatesPointsAll = [
polylineCoordinates0,
polylineCoordinates1,
polylineCoordinates2,
polylineCoordinates3,
polylineCoordinates4,
];
allTextEditingPlaces = [
wayPoint0Controller,
wayPoint1Controller,
wayPoint2Controller,
wayPoint3Controller,
wayPoint4Controller,
];
currentLocationToFormPlacesAll = [
currentLocationToFormPlaces0,
currentLocationToFormPlaces1,
currentLocationToFormPlaces2,
currentLocationToFormPlaces3,
currentLocationToFormPlaces4,
];
placeListResponseAll = [
wayPoint0,
wayPoint1,
wayPoint2,
wayPoint3,
wayPoint4
];
startLocationFromMapAll = [
startLocationFromMap0,
startLocationFromMap1,
startLocationFromMap2,
startLocationFromMap3,
startLocationFromMap4,
];
currentLocationStringAll = [
currentLocationString0,
currentLocationString1,
currentLocationString2,
currentLocationString3,
currentLocationString4,
];
placesCoordinate = [
placesCoordinate0,
placesCoordinate1,
placesCoordinate2,
placesCoordinate3,
placesCoordinate4,
];
update();
}
List driversForMishwari = [];
Future selectDriverAndCarForMishwariTrip() async {
// 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;
}
}
final Rx<DateTime> selectedDateTime = DateTime.now().obs;
void updateDateTime(DateTime newDateTime) {
selectedDateTime.value = newDateTime;
}
Future mishwariOption() async {
isLoading = true;
update();
// add dialoug for select driver and car
await selectDriverAndCarForMishwariTrip();
Future.delayed(Duration.zero);
isLoading = false;
update();
Get.to(() => CupertinoDriverListWidget());
// changeCashConfirmPageShown();
}
var driverIdVip = '';
Future<void> saveTripData(
Map<String, dynamic> driver, DateTime tripDateTime) async {
try {
// Prepare trip data
Map<String, dynamic> tripData = {
'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'] ?? 'UnKnown',
'age': driver['age'].toString(), // Convert age to String
'education': driver['education'] ?? 'UnKnown', //startlocationname
'license_type': driver['license_type'] ?? 'UnKnown',
'national_number': driver['national_number'] ?? 'UnKnown',
'car_plate': driver['car_plate'],
'make': driver['make'],
'model': driver['model'],
'year': driver['year'].toString(), // Convert year to String
'color': driver['color'],
'color_hex': driver['color_hex'],
'displacement': driver['displacement'],
'fuel': driver['fuel'],
'token': driver['token'],
'rating': driver['rating'].toString(), // Convert rating to String
'countRide':
driver['ride_count'].toString(), // Convert countRide to String
'passengerId': box.read(BoxName.passengerID),
'timeSelected': tripDateTime.toIso8601String(),
'status': 'pending',
'startNameAddress': startNameAddress.toString(),
'locationCoordinate':
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
};
Log.print('tripData: $tripData');
// Send data to server
var response =
await CRUD().post(link: AppLink.addMishwari, payload: tripData);
// Log.print('response: $response');
if (response != 'failure') {
// Trip saved successfully
// Get.snackbar('Success'.tr, 'Trip booked successfully'.tr);
var id = response['message']['id'].toString();
await CRUD().post(
link: '${AppLink.IntaleqCairoServer}/ride/rides/add.php',
payload: {
"start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime":
DateTime.now().add(const Duration(hours: 2)).toString(),
"price": '50',
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": driver['driver_id'].toString(),
"status": "waiting",
'carType': 'vip',
"price_for_driver": '50',
"price_for_passenger": '50',
"distance": '20',
"paymentMethod": 'cash',
}).then((value) {
if (value is String) {
final parsedValue = jsonDecode(value);
rideId = parsedValue['message'];
} else if (value is Map) {
rideId = value['message'];
} else {
Log.print('Unexpected response type: ${value.runtimeType}');
}
});
if (AppLink.endPoint != AppLink.IntaleqCairoServer) {
await CRUD().post(
link: "${AppLink.endPoint}/ride/mishwari/add.php",
payload: tripData);
CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
"start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": DateTime.now().add(const Duration(hours: 2)).toString(),
"price": '50',
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": driver['driver_id'].toString(),
"status": "waiting",
'carType': 'vip',
"price_for_driver": '50',
"price_for_passenger": '50',
"distance": '20',
"paymentMethod": 'cash',
});
}
driverIdVip = driver['driver_id'].toString();
driverId = driver['driver_id'].toString();
DateTime timeSelected = DateTime.parse(tripDateTime.toIso8601String());
Get.find<NotificationController>().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 firebaseMessagesController.sendNotificationToDriverMAP(
'OrderVIP',
rideId.toString(),
(driver['token'].toString()),
[
id,
rideId,
driver['id'],
passengerLocation.latitude.toString(),
startNameAddress.toString(),
passengerLocation.longitude.toString(),
(box.read(BoxName.name).toString().split(' ')[0]).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);
Log.print(
'previous_driver_token: ${response['previous_driver_token']}');
firebaseMessagesController.sendNotificationToDriverMAP(
'Order VIP Canceld'.tr,
'Passenger cancel order'.tr,
response['previous_driver_token'].toString(),
[],
'cancel.wav',
);
}
// data = [];
isBottomSheetShown = false;
update();
Get.to(() => VipWaittingPage());
} else {
throw Exception('Failed to save trip');
}
} catch (e) {
// Show error message with more details for debugging
Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr,
backgroundColor: AppColor.redColor);
Log.print('Error: $e');
}
}
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);
}
}
sendToDriverAgain(String token) {
firebaseMessagesController.sendNotificationToDriverMAP(
'Order VIP Canceld'.tr,
'Passenger cancel order'.tr,
token,
[],
'cancel.wav',
);
}
initilizeGetStorage() async {
if (box.read(BoxName.addWork) == null) {
box.write(BoxName.addWork, 'addWork');
}
if (box.read(BoxName.addHome) == null) {
box.write(BoxName.addHome, 'addHome');
}
}
late List recentPlaces = [];
getFavioratePlaces0() async {
recentPlaces = await sql.getCustomQuery(
'SELECT DISTINCT latitude, longitude, name, rate FROM ${TableName.recentLocations}');
}
getFavioratePlaces() async {
recentPlaces = await sql.getCustomQuery(
'SELECT * FROM ${TableName.recentLocations} ORDER BY createdAt DESC');
// Log.print('recentPlaces: ${recentPlaces}');
}
double passengerRate = 5;
double comfortPrice = 8;
double speedPrice = 4;
double mashwariPrice = 4;
double familyPrice = 4;
double deliveryPrice = 1.2;
double minFareSYP = 16000; // حد أدنى للأجرة (سوريا)
double minBillableKm = 1.0; // حد أدنى للمسافة المفوترة
double commissionPct = 15; // عمولة التطبيق % (راكب)
getKazanPercent() async {
var res = await CRUD().get(
link: AppLink.getKazanPercent,
payload: {'country': box.read(BoxName.countryCode).toString()},
);
if (res != 'failure') {
var json = jsonDecode(res);
kazan = double.parse(json['message'][0]['kazan']);
naturePrice = double.parse(json['message'][0]['naturePrice']);
heavyPrice = double.parse(json['message'][0]['heavyPrice']);
latePrice = double.parse(json['message'][0]['latePrice']);
comfortPrice = double.parse(json['message'][0]['comfortPrice']);
speedPrice = double.parse(json['message'][0]['speedPrice']);
deliveryPrice = double.parse(json['message'][0]['deliveryPrice']);
mashwariPrice = double.parse(json['message'][0]['freePrice']);
familyPrice = double.parse(json['message'][0]['familyPrice']);
fuelPrice = double.parse(json['message'][0]['fuelPrice']);
}
}
getPassengerRate() async {
var res = await CRUD().get(
link: AppLink.getPassengerRate,
payload: {'passenger_id': box.read(BoxName.passengerID)});
if (res != 'failure') {
var message = jsonDecode(res)['message'];
if (message['rating'] == null) {
passengerRate = 5.0; // Default rating
} else {
// Safely parse the rating to double
var rating = message['rating'];
if (rating is String) {
passengerRate =
double.tryParse(rating) ?? 5.0; // Default if parsing fails
} else if (rating is num) {
passengerRate =
rating.toDouble(); // Already a number, convert to double
} else {
passengerRate = 5.0; // Default for unexpected data types
}
}
} else {
passengerRate = 5.0; // Default rating for failure
}
}
addFingerPrint() async {
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
await CRUD().postWallet(link: AppLink.addFingerPrint, payload: {
'token': (box.read(BoxName.tokenFCM.toString())),
'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint
});
}
firstTimeRunToGetCoupon() async {
// Check if it's the first time and the app is installed and gift token is available
if (box.read(BoxName.isFirstTime).toString() == '0' &&
box.read(BoxName.isInstall).toString() == '1' &&
box.read(BoxName.isGiftToken).toString() == '0') {
var promo, discount, validity;
var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: {
"passengerID": box.read(BoxName.passengerID).toString(),
});
if (resPromo != 'failure') {
var d1 = jsonDecode(resPromo);
promo = d1['message']['promo_code'];
discount = d1['message']['amount'];
validity = d1['message']['validity_end_date'];
}
box.write(BoxName.isFirstTime, '1');
// Show a full-screen modal styled as an ad
Get.dialog(
AlertDialog(
contentPadding:
EdgeInsets.zero, // Removes the padding around the content
content: SizedBox(
width: 300, // Match the width of PromoBanner
// height: 250, // Match the height of PromoBanner
child: PromoBanner(
promoCode: promo,
discountPercentage: discount,
validity: validity,
),
),
),
);
}
}
// --- دالة جديدة للاستماع ومعالجة الرابط ---
void _listenForDeepLink() {
// استمع إلى أي تغيير في الإحداثيات القادمة من الرابط
ever(_deepLinkController.deepLinkLatLng, (LatLng? latLng) {
if (latLng != null) {
print('MapPassengerController detected deep link LatLng: $latLng');
// عندما يتم استلام إحداثيات جديدة، عينها كوجهة
myDestination = latLng;
// قم بتشغيل المنطق الخاص بك لعرض المسار
// (تأكد من أن `passengerLocation` لديها قيمة أولاً)
if (passengerLocation != null) {
getDirectionMap(
'${passengerLocation.latitude},${passengerLocation.longitude}',
'${myDestination.latitude},${myDestination.longitude}');
} else {
// يمكنك إظهار رسالة للمستخدم لتمكين الموقع أولاً
print(
'Cannot process deep link route yet, passenger location is null.');
}
// إعادة تعيين القيمة إلى null لمنع التشغيل مرة أخرى عند إعادة بناء الواجهة
_deepLinkController.deepLinkLatLng.value = null;
}
});
}
@override
void onInit() async {
super.onInit();
// // --- إضافة جديدة: تهيئة وحدة التحكم في الروابط العميقة ---
// Get.put(DeepLinkController(), permanent: true);
// // ----------------------------------------------------
// مرحلة 0: الضروري جداً لعرض الخريطة سريعاً
mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
await initilizeGetStorage(); // إعداد سريع
await _initMinimalIcons(); // start/end فقط
await addToken(); // لو لازم للمصادقة
_listenForDeepLink();
await getLocation(); // لتحديد الكاميرا
box.write(BoxName.carType, 'yet');
box.write(BoxName.tipPercentage, '0');
await detectAndCacheDeviceTier();
// لا تُنشئ Controllers الثقيلة الآن:
Get.lazyPut<TextToSpeechController>(() => TextToSpeechController(),
fenix: true);
Get.lazyPut<FirebaseMessagesController>(() => FirebaseMessagesController(),
fenix: true);
Get.lazyPut<AudioRecorderController>(() => AudioRecorderController(),
fenix: true);
// ابدأ الخريطة الآن (الشاشة ظهرت للمستخدم)
// لاحقاً: استخدم SchedulerBinding.instance.addPostFrameCallback إذا احتجت.
// مرحلة 1: مهام ضرورية للتسعير لكن غير حرجة لظهور UI
unawaited(_stagePricingAndState());
// مرحلة 2: تحسينات/كماليات بالخلفية
unawaited(_stageNiceToHave());
// ابدأ إعادة تحميل الماركر لكن بثروتل داخلي
startMarkerReloading(); // تأكد أنه مَخنوق التحديث (throttled)
}
// === Helpers ===
Future<void> _initMinimalIcons() async {
addCustomStartIcon();
addCustomEndIcon();
// أجّل باقي الأيقونات:
// addCustomCarIcon(), addCustomLadyIcon(), addCustomMotoIcon(), addCustomStepIcon()
}
Future<void> _stagePricingAndState() async {
try {
await getKazanPercent(); // أسعار السيرفر
} catch (_) {}
try {
await getRideStatusFromStartApp();
} catch (_) {}
// لو عندك ضبط “وضع خفيف” حسب الجهاز:
_applyLowEndModeIfNeeded();
}
Future<void> _stageNiceToHave() async {
// نفّذ بالتوازي
await Future.wait([
Future(() async {
try {
getFavioratePlaces();
} catch (_) {}
}),
Future(() async {
try {
readyWayPoints();
} catch (_) {}
}),
Future(() async {
try {
addCustomPicker();
} catch (_) {}
}),
Future(() async {
try {
addCustomCarIcon();
} catch (_) {}
}),
Future(() async {
try {
addCustomLadyIcon();
} catch (_) {}
}),
Future(() async {
try {
addCustomMotoIcon();
} catch (_) {}
}),
Future(() async {
try {
addCustomStepIcon();
} catch (_) {}
}),
Future(() async {
try {
getPassengerRate();
} catch (_) {}
}),
Future(() async {
try {
firstTimeRunToGetCoupon();
} catch (_) {}
}),
]);
try {
cardNumber = await SecureStorage().readData(BoxName.cardNumber);
} catch (_) {}
}
void _applyLowEndModeIfNeeded() {
// مثال بسيط: يمكنك حفظ فلاج بنظامك (من السيرفر/الإعدادات/الكاش) لتفعيل وضع خفيف
// لاحقاً فعّل: map.trafficEnabled=false, buildingsEnabled=false, تبسيط polylines...
// controller.lowEndMode = true;
}
// @override
// void onInit() async {
// mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
// // k = await getAIKey('HERE_API');
// getFavioratePlaces();
// readyWayPoints();
// addCustomPicker();
// addCustomCarIcon();
// addCustomLadyIcon();
// addCustomMotoIcon();
// addCustomStepIcon();
// addCustomStartIcon();
// addCustomEndIcon();
// addToken();
// await getLocation();
// addFingerPrint();
// // getPassengerLocationUniversity();
// // _initializePolygons();
// // await addToken();
// getKazanPercent();
// getPassengerRate();
// getRideStatusFromStartApp();
// reloadStartApp = false;
// startMarkerReloading();
// Get.put(TextToSpeechController());
// Get.put(FirebaseMessagesController());
// box.write(BoxName.carType, 'yet');
// box.write(BoxName.tipPercentage, '0');
// Get.put(AudioRecorderController());
// // await getNearestDriverByPassengerLocation();
// firstTimeRunToGetCoupon();
// initilizeGetStorage();
// cardNumber = await SecureStorage().readData(BoxName.cardNumber);
// super.onInit();
// }
uploadPassengerLocation() async {
await CRUD().post(link: AppLink.addpassengerLocation, payload: {
"passengerId": box.read(BoxName.passengerID),
"lat": passengerLocation.latitude.toString(),
"lng": passengerLocation.longitude.toString(),
"rideId": rideId.toString()
});
}
}
class CarLocation {
final String id;
final double latitude;
final double longitude;
final double distance;
final double duration;
CarLocation({
required this.id,
required this.latitude,
required this.longitude,
this.distance = 10000,
this.duration = 10000,
});
}