Files
intaleq_driver/lib/controller/home/captin/map_driver_controller.dart
2025-09-01 19:04:50 +03:00

2200 lines
81 KiB
Dart
Executable File

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:math' as math;
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:bubble_head/bubble.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:url_launcher/url_launcher.dart';
import '../../../constant/api_key.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/table_names.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../../views/Rate/rate_passenger.dart';
import '../../../views/home/Captin/home_captain/home_captin.dart';
import '../../firebase/firbase_messge.dart';
import '../../functions/crud.dart';
import '../../functions/encrypt_decrypt.dart';
import '../../functions/location_controller.dart';
import '../../functions/tts.dart';
class MapDriverController extends GetxController {
bool isLoading = true;
final formKey1 = GlobalKey<FormState>();
final formKey2 = GlobalKey<FormState>();
final formKeyCancel = GlobalKey<FormState>();
final messageToPassenger = TextEditingController();
final sosEmergincyNumberCotroller = TextEditingController();
final cancelTripCotroller = TextEditingController();
List data = [];
List dataDestination = [];
LatLngBounds? boundsData;
BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor passengerIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor startIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor endIcon = BitmapDescriptor.defaultMarker;
final List<LatLng> polylineCoordinates = [];
final List<LatLng> polylineCoordinatesDestination = [];
List<Polyline> polyLines = [];
List<Polyline> polyLinesDestination = [];
Set<Marker> markers = {};
late String passengerLocation;
late String passengerDestination;
late String step0;
late String step1;
late String step2;
late String step3;
late String step4;
late String passengerWalletBurc;
late String timeOfOrder;
late String duration;
late String totalCost;
late String distance;
late String passengerName;
late String passengerEmail;
late String totalPricePassenger;
late String passengerPhone;
late String rideId;
late String isHaveSteps;
late String paymentAmount;
late String paymentMethod;
late String passengerId;
late String driverId;
late String tokenPassenger;
late String durationToPassenger;
late String walletChecked;
late String direction;
late String durationOfRideValue;
late String status;
int timeWaitingPassenger = 5; //5 miniute
bool isPassengerInfoWindow = false;
bool isBtnRideBegin = false;
bool isArrivedSend = true;
bool isdriverWaitTimeEnd = false;
bool isRideFinished = false;
bool isRideStarted = false;
bool isPriceWindow = false;
double passengerInfoWindowHeight = Get.height * .38;
double driverEndPage = 100;
double progress = 0;
double progressToPassenger = 0;
double progressInPassengerLocationFromDriver = 0;
bool isRideBegin = false;
int progressTimerToShowPassengerInfoWindowFromDriver = 25;
int remainingTimeToShowPassengerInfoWindowFromDriver = 25;
int remainingTimeToPassenger = 60;
int remainingTimeInPassengerLocatioWait = 60;
bool isDriverNearPassengerStart = false;
GoogleMapController? mapController;
late LatLng myLocation;
int remainingTimeTimerRideBegin = 60;
String stringRemainingTimeRideBegin = '';
String stringRemainingTimeRideBegin1 = '';
double progressTimerRideBegin = 0;
late Timer timer;
String? mapAPIKEY;
final zones = <Zone>[];
String canelString = 'yet';
LatLng latLngPassengerLocation = LatLng(0, 0);
late LatLng latLngPassengerDestination = LatLng(0, 0);
List<Map<String, dynamic>> routeSteps = [];
String currentInstruction = "";
int currentStepIndex = 0;
@override
void onClose() {
print("--- Controller is closing. Cleaning up listener. ---");
// This will stop the listener when you leave the page
_posSub?.cancel();
_navigationTimer?.cancel();
// It's also good practice to dispose the map controller
mapController?.dispose();
super.onClose();
}
void onMapCreated(GoogleMapController controller) async {
myLocation = Get.find<LocationController>().myLocation;
// myLocation = myLocation;
mapController = controller;
controller.getVisibleRegion();
// LatLngBounds bounds = await controller.getVisibleRegion();
controller.animateCamera(
CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
);
update();
// Set up a timer or interval to trigger the marker update every 3 seconds.
timer = Timer.periodic(const Duration(seconds: 1), (_) {
updateMarker();
});
}
void changeStatusDriver() {
status = 'On';
update();
}
void changeDriverEndPage() {
remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;
update();
}
takeSnapMap() {
mapController!.takeSnapshot();
}
@override
void dispose() {
mapController!.dispose();
super.dispose();
}
Future openGoogleMapFromDriverToPassenger() async {
var endLat = latLngPassengerLocation.latitude;
var endLng = latLngPassengerLocation.longitude;
var startLat = Get.find<LocationController>().myLocation.latitude;
var startLng = Get.find<LocationController>().myLocation.longitude;
String url =
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
throw 'Could not launch google maps';
}
}
void clearPolyline() {
polyLines = [];
polyLinesDestination = [];
polylineCoordinates.clear();
polylineCoordinatesDestination.clear();
update();
}
void changeRideToBeginToPassenger() {
isRideBegin = true;
passengerInfoWindowHeight = Get.height * .22;
update();
}
cancelTripFromDriverAfterApplied() async {
if (formKeyCancel.currentState!.validate()) {
box.write(BoxName.statusDriverLocation, 'off');
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"Cancel Trip from driver",
"Trip Cancelled from driver. We are looking for a new driver. Please wait."
.tr,
tokenPassenger,
[],
'cancel.wav',
);
await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/update.php",
payload: {
"id": (rideId).toString(), // Convert to String
"status": 'CancelFromDriverAfterApply'
});
CRUD().postFromDialogue(
link: '${AppLink.seferCairoServer}/driver_order/add.php',
payload: {
'driver_id': box.read(BoxName.driverID).toString(),
// box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'CancelFromDriverAfterApply'
});
await CRUD().post(
link:
"${AppLink.seferCairoServer}/ride/cancelRide/addCancelTripFromDriverAfterApplied.php",
payload: {
"order_id": (rideId).toString(),
"driver_id": box.read(BoxName.driverID).toString(),
"status": 'reject After Applied',
"notes": (cancelTripCotroller.text).toString()
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD()
.post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
"id": (rideId).toString(), // Convert to String
"status": 'CancelFromDriverAfterApply'
});
CRUD().postFromDialogue(
link: '${AppLink.endPoint}/driver_order/add.php',
payload: {
'driver_id': box.read(BoxName.driverID).toString(),
// box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'CancelFromDriverAfterApply'
});
CRUD().post(
link:
"${AppLink.endPoint}/ride/cancelRide/addCancelTripFromDriverAfterApplied.php",
payload: {
"order_id": (rideId).toString(),
"driver_id": box.read(BoxName.driverID).toString(),
"status": 'reject After Applied',
"notes": (cancelTripCotroller.text).toString()
});
}
sql.insertData({
'order_id': (rideId),
'created_at': DateTime.now().toString(),
'driver_id': box.read(BoxName.driverID).toString(),
}, TableName.driverOrdersRefuse);
box.write(BoxName.rideStatus, 'Cancel');
Log.print('rideStatus from map 240 : ${box.read(BoxName.rideStatus)}');
Get.find<HomeCaptainController>().getRefusedOrderByCaptain();
Get.offAll(() => HomeCaptain());
}
}
void startTimerToShowPassengerInfoWindowFromDriver() async {
if (box.read(BoxName.rideStatus) == 'Begin') {
Log.print('rideStatus from map 248 : ${box.read(BoxName.rideStatus)}');
isPassengerInfoWindow = false;
} else {
isPassengerInfoWindow = true;
for (int i = 0; i <= int.parse(durationToPassenger); i++) {
await Future.delayed(const Duration(seconds: 1));
progressToPassenger = i / int.parse(durationToPassenger);
remainingTimeToPassenger = int.parse(durationToPassenger) - i;
if (remainingTimeToPassenger == 0) {
isBtnRideBegin = true;
update();
}
int minutes = (remainingTimeToPassenger / 60).floor();
int seconds = remainingTimeToPassenger % 60;
stringRemainingTimeToPassenger =
'$minutes:${seconds.toString().padLeft(2, '0')}';
update();
}
}
// update();
// startTimerToShowDriverToPassengerDuration();
}
String stringRemainingTimeToPassenger = '';
String stringRemainingTimeWaitingPassenger = '';
void startTimerToShowDriverWaitPassengerDuration() async {
for (int i = 0; i <= timeWaitingPassenger * 60; i++) {
await Future.delayed(const Duration(seconds: 1));
progressInPassengerLocationFromDriver = i / (timeWaitingPassenger * 60);
remainingTimeInPassengerLocatioWait = (timeWaitingPassenger * 60) - i;
if (isRideBegin == true) {
remainingTimeInPassengerLocatioWait = 0;
update();
}
if (remainingTimeInPassengerLocatioWait == 0) {
isdriverWaitTimeEnd = true;
update();
}
'remainingTimeInPassengerLocatioWait $remainingTimeInPassengerLocatioWait';
int minutes = (remainingTimeInPassengerLocatioWait / 60).floor();
int seconds = remainingTimeInPassengerLocatioWait % 60;
stringRemainingTimeWaitingPassenger =
'$minutes:${seconds.toString().padLeft(2, '0')}';
update();
}
}
void driverGoToPassenger() async {
changeRideToBeginToPassenger();
box.write(BoxName.rideStatus, 'Applied');
Log.print('rideStatus from map 304 : ${box.read(BoxName.rideStatus)}');
update();
await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/update.php",
payload: {
'id': (rideId),
'driverGoToPassengerTime': DateTime.now().toString(),
'status': 'Applied'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
'id': (rideId),
'driverGoToPassengerTime': DateTime.now().toString(),
'status': 'Applied'
});
}
// Get.find<HomeCaptainController>().changeToAppliedRide('Applied');
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Driver Is Going To Passenger'.tr,
box.read(BoxName.nameDriver).toString(), //todo name driver
tokenPassenger,
[],
'start.wav');
}
bool isSocialPressed = false;
driverCallPassenger() async {
String scam = await getDriverScam();
if (scam != 'failure') {
if (int.parse(scam) > 3) {
box.write(BoxName.statusDriverLocation, 'on');
Get.find<LocationController>().stopLocationUpdates();
await CRUD().post(link: AppLink.addNotificationCaptain, payload: {
'driverID': box.read(BoxName.driverID),
'title': 'scams operations'.tr,
'body':
'you have connect to passengers and let them cancel the order'.tr,
});
} else if (isSocialPressed == true) {
box.write(BoxName.statusDriverLocation, 'off');
await CRUD().post(link: AppLink.addDriverScam, payload: {
'driverID': box.read(BoxName.driverID),
'passengerID': passengerId,
'rideID': rideId,
'isDriverCalledPassenger': '$isSocialPressed'
});
}
}
}
Future<String> getDriverScam() async {
var res = await CRUD().post(link: AppLink.getDriverScam, payload: {
'driverID': box.read(BoxName.driverID),
});
if (res == 'failure') {
box.write(BoxName.statusDriverLocation, 'off');
return '0';
}
var d = jsonDecode(res);
return d['message'][0]['count'];
}
void startRideFromStartApp() {
// if (box.read(BoxName.rideStatus) == 'Begin') {
changeRideToBeginToPassenger();
isPassengerInfoWindow = false;
isRideStarted = true;
isRideFinished = false;
remainingTimeInPassengerLocatioWait = 0;
timeWaitingPassenger = 0;
box.write(BoxName.statusDriverLocation, 'on');
update();
// }
// rideIsBeginPassengerTimer();
}
Position? currentPosition;
startRideFromDriver() async {
double _distance =
await calculateDistanceBetweenDriverAndPassengerLocation();
if (_distance < 400) {
changeRideToBeginToPassenger();
isPassengerInfoWindow = false;
isRideStarted = true;
isRideFinished = false;
remainingTimeInPassengerLocatioWait = 0;
timeWaitingPassenger = 0;
box.write(BoxName.statusDriverLocation, 'on');
// // ابدأ التتبع الحيّ لخطوات الملاحة
await startListeningStepNavigation();
// box.write(BoxName.rideStatus, 'Begin'); //
// todo ride details
// Get.find<HomeCaptainController>().changeToAppliedRide('Begin');
box.write(BoxName.rideStatus, 'Begin');
Log.print('rideStatus from map 399 : ${box.read(BoxName.rideStatus)}');
// Get.find<HomeCaptainController>().update();
update();
await CRUD().post(link: AppLink.updateRides, payload: {
'id': (rideId),
'rideTimeStart': DateTime.now().toString(),
'status': 'Begin',
});
CRUD().post(link: AppLink.addDriverOrder, payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'Begin'
});
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Trip is Begin'.tr,
box.read(BoxName.nameDriver).toString(),
tokenPassenger,
[],
'start.wav');
rideIsBeginPassengerTimer();
update();
} else {
Get.back();
MyDialog().getDialog('Your are far from passenger location'.tr,
'go to your passenger location before\nPassenger cancel trip'.tr, () {
Get.back();
});
}
}
calculateDistanceInMeter(LatLng prev, LatLng current) async {
double distance2 = Geolocator.distanceBetween(
prev.latitude,
prev.longitude,
current.latitude,
current.longitude,
);
return distance2;
}
double speedoMeter = 0;
void updateLocation() async {
try {
for (var i = 0; i < remainingTimeTimerRideBegin; i++) {
await Future.delayed(const Duration(seconds: 1));
mapController!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
bearing: Get.find<LocationController>().heading,
target: myLocation,
zoom: 17, // Adjust zoom level as needed
),
),
);
// });
update();
}
// Stop listening after ride finishes
if (!isRideBegin) {}
} catch (error) {
debugPrint('Error listening to GPS: $error');
// Handle GPS errors gracefully
}
// Periodically call updateLocation again
await Future.delayed(const Duration(seconds: 1));
updateLocation();
}
calculateDistanceBetweenDriverAndPassengerLocation() async {
Get.put(LocationController());
var res = await CRUD().get(
link: AppLink.getLatestLocationPassenger,
payload: {'rideId': (rideId)});
if (res != 'failure') {
var passengerLatestLocationString = jsonDecode(res)['message'];
double distance2 = Geolocator.distanceBetween(
double.parse(passengerLatestLocationString[0]['lat'].toString()),
double.parse(passengerLatestLocationString[0]['lng'].toString()),
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
return distance2;
} else {
double distance2 = Geolocator.distanceBetween(
latLngPassengerLocation.latitude,
latLngPassengerLocation.longitude,
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
return distance2;
}
}
addWaitingTimeCostFromPassengerToDriverWallet() async {
double distance2 =
await calculateDistanceBetweenDriverAndPassengerLocation();
if (distance2 > 60) {
MyDialog().getDialog('Your are far from passenger location'.tr,
'go to your passenger location before\nPassenger cancel trip'.tr, () {
Get.back();
});
} else {
double costOfWaiting5Minute = box.read(BoxName.countryCode) == 'Egypt'
? (distanceBetweenDriverAndPassengerWhenConfirm * .08) + (5 * 1)
: (distanceBetweenDriverAndPassengerWhenConfirm * .06) +
(5 * .06); //for Eygpt other like jordan .06 per minute
await CRUD().post(link: AppLink.updateRides, payload: {
'id': (rideId),
'rideTimeStart': DateTime.now().toString(),
'status': 'CancelAfterWait',
});
CRUD().post(link: AppLink.addDriverOrder, payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'CancelAfterWait'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
'id': (rideId),
'rideTimeStart': DateTime.now().toString(),
'status': 'CancelAfterWait',
});
CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'CancelAfterWait'
});
}
var paymentTokenWait =
await generateTokenDriver(costOfWaiting5Minute.toString());
var res = await CRUD().post(link: AppLink.addDrivePayment, payload: {
'rideId': (rideId),
'amount': (costOfWaiting5Minute.toString()),
'payment_method': 'wait-cancel',
'passengerID': (passengerId),
'token': paymentTokenWait,
'driverID': box.read(BoxName.driverID).toString(),
});
var paymentTokenWait1 =
await generateTokenDriver(costOfWaiting5Minute.toString());
var res1 =
await CRUD().post(link: AppLink.addDriversWalletPoints, payload: {
'paymentID': 'rideId${(rideId)}',
'amount': (costOfWaiting5Minute).toStringAsFixed(0),
'paymentMethod': 'wait',
'token': paymentTokenWait1,
'driverID': box.read(BoxName.driverID).toString(),
});
if (res != 'failure') {
Get.snackbar(
'You will get cost of your work for this trip'.tr,
'${'you gain'.tr} $costOfWaiting5Minute \$${' in your wallet'.tr}',
backgroundColor: AppColor.deepPurpleAccent,
);
}
var paymentTokenWaitPassenger1 =
await generateTokenPassenger((costOfWaiting5Minute * -1).toString());
await CRUD().post(link: AppLink.addPassengersWallet, payload: {
'passenger_id': (passengerId),
'balance': (costOfWaiting5Minute * -1).toString(),
'token': paymentTokenWaitPassenger1,
});
box.write(BoxName.statusDriverLocation, 'off');
Get.offAll(HomeCaptain());
}
}
Future<void> finishRideFromDriver() async {
// double distanceToDestination = Geolocator.distanceBetween(
// latLngPassengerDestination.latitude,
// latLngPassengerDestination.longitude,
// Get.find<LocationController>().myLocation.latitude,
// Get.find<LocationController>().myLocation.longitude,
// );
final originalDistanceM = double.parse(distance.toString()) * 1000;
// 2. احسب المسافة التي قطعها السائق حتى الآن
final movedDistanceM = Geolocator.distanceBetween(
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
latLngPassengerLocation.latitude,
latLngPassengerLocation.longitude,
);
// originalDistanceM - distanceToDestination;
// 3. عتبة ثلث المسافة
final oneThirdDistanceM = originalDistanceM / 5;
// Logging للتتبع
Log.print('originalDistanceM: $originalDistanceM');
// Log.print('distanceToDestinationM: $distanceToDestination');
Log.print('movedDistanceM: $movedDistanceM');
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
if (movedDistanceM > oneThirdDistanceM) {
MyDialog().getDialog(
'Are you sure to exit ride?'.tr,
'',
() {
Get.back();
finishRideFromDriver1();
},
);
} /////
else {
final textToSpeechController = Get.put(TextToSpeechController());
MyDialog().getDialog(
"You haven't moved sufficiently!".tr,
'',
() => Get.back(),
);
await textToSpeechController
.speakText("You haven't moved sufficiently!".tr);
}
}
String paymentToken = '';
Future<String> generateTokenDriver(String amount) async {
var res =
await CRUD().postWallet(link: AppLink.addPaymentTokenDriver, payload: {
'driverID': box.read(BoxName.driverID).toString(),
'amount': amount.toString(),
});
var d = (res);
return d['message'];
}
String paymentTokenPassenger = '';
Future<String> generateTokenPassenger(String amount) async {
var res = await CRUD()
.postWallet(link: AppLink.addPaymentTokenPassenger, payload: {
'passengerId': passengerId,
'amount': amount.toString(),
});
var d = (res);
return d['message'];
}
void finishRideFromDriver1() async {
isRideFinished = true;
isRideStarted = false;
isPriceWindow = false;
box.write(BoxName.rideStatus, 'Finished');
Log.print('rideStatus from map 664 : ${box.read(BoxName.rideStatus)}');
// Calculate totalCost more concisely
if (price < 16000) {
totalCost = (carType == 'Comfort' ||
carType == 'Mishwar Vip' ||
carType == 'Lady')
? '20000'
: '16000';
} else if (price < double.parse(totalPricePassenger)) {
totalCost = totalPricePassenger;
} else {
totalCost = (carType == 'Comfort' ||
carType == 'Mishwar Vip' ||
carType == 'Lady')
? price.toStringAsFixed(2)
: totalPricePassenger;
}
paymentAmount = totalCost;
box.write(BoxName.statusDriverLocation, 'off');
// Prepare data for API calls
final nowString = DateTime.now().toString();
final basePayload = {
'id': (rideId),
'rideTimeFinish': nowString,
'status': 'Finished',
'price': totalCost,
};
final driverOrderPayload = {
'order_id': (rideId.toString()),
'status': 'Finished'
};
// List to hold all asynchronous operations
List<Future<dynamic>> futures = [];
// API calls that can run in parallel
futures.add(CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/update.php",
payload: basePayload,
));
futures.add(CRUD().post(
link: "${AppLink.seferCairoServer}/ride/driver_order/update.php",
payload: driverOrderPayload,
));
// Wallet transactions (can potentially be parallelized if independent)
if (walletChecked == 'true') {
paymentToken = await generateTokenPassenger(
((-1) * double.parse(paymentAmount)).toString());
futures
.add(CRUD().postWallet(link: AppLink.addPassengersWallet, payload: {
'passenger_id': (passengerId),
'balance': ((-1) * double.parse(paymentAmount)).toString(),
'token': paymentToken,
}));
}
paymentToken = await generateTokenDriver(paymentAmount.toString());
futures.add(CRUD().postWallet(link: AppLink.addDrivePayment, payload: {
'rideId': (rideId),
'amount': paymentAmount,
'payment_method':
walletChecked == 'true' ? "${paymentMethod}Ride" : paymentMethod,
'passengerID': (passengerId),
'token': paymentToken,
'driverID': box.read(BoxName.driverID).toString(),
}));
if (double.parse(passengerWalletBurc) < 0) {
final paymentToken1 = await generateTokenPassenger(
((-1) * double.parse(passengerWalletBurc)).toString());
futures
.add(CRUD().postWallet(link: AppLink.addPassengersWallet, payload: {
'passenger_id': (passengerId),
'token': paymentToken1,
'balance': ((-1) * double.parse(passengerWalletBurc)).toString()
}));
}
double pointsSubtraction = double.parse(paymentAmount) * (-1) * 0.1;
final paymentToken2 =
await generateTokenDriver((pointsSubtraction).toStringAsFixed(0));
futures
.add(CRUD().postWallet(link: AppLink.addDriversWalletPoints, payload: {
'paymentID': 'rideId${(rideId)}',
'amount': (pointsSubtraction).toStringAsFixed(0),
'paymentMethod': paymentMethod,
'token': paymentToken2,
'driverID': box.read(BoxName.driverID).toString(),
}));
// Wait for all independent API calls to complete
await Future.wait(futures);
Get.put(DriverBehaviorController()).sendSummaryToServer(driverId, rideId);
// Send notification (this likely depends on previous steps)
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"Driver Finish Trip".tr,
'${'you will pay to Driver'.tr} $paymentAmount \$',
tokenPassenger,
[
box.read(BoxName.driverID),
rideId,
box.read(BoxName.tokenDriver),
paymentAmount.toString()
],
'ding.wav');
// Navigate to the next screen (likely depends on previous steps being done)
Get.to(() => RatePassenger(), arguments: {
'passengerId': passengerId,
'rideId': rideId,
'price': paymentAmount.toString(),
'walletChecked': walletChecked
});
}
// void finishRideFromDriver1() async {
// // if (carType != 'Comfort' || carType != 'Free Ride') {
// isRideFinished = true;
// isRideStarted = false;
// isPriceWindow = false;
// box.write(BoxName.rideStatus, 'Finished');
// // Get.find<HomeCaptainController>().changeToAppliedRide('Finished');
// // Get.find<HomeCaptainController>().update();
// totalCost = price < 20
// ? carType != 'Comfort' && carType != 'Mishwar Vip' && carType != 'Lady'
// ? '20'
// : '30'
// : price < double.parse(totalPricePassenger)
// ? totalPricePassenger
// : carType != 'Comfort' &&
// carType != 'Mishwar Vip' &&
// carType != 'Lady'
// ? totalPricePassenger
// : price.toStringAsFixed(2);
// paymentAmount = totalCost;
// box.write(BoxName.statusDriverLocation, 'off');
// // changeRideToBeginToPassenger();
// await CRUD().post(
// link: "${AppLink.seferCairoServer}/ride/rides/update.php",
// payload: {
// 'id': rideId,
// 'rideTimeFinish': DateTime.now().toString(),
// 'status': 'Finished',
// 'price': totalCost,
// });
// CRUD().post(
// link: "${AppLink.seferCairoServer}/ride/driver_order/update.php",
// payload: {
// // 'driver_id': box.read(BoxName.driverID).toString(),
// 'order_id': rideId.toString(),
// 'status': 'Finished'
// });
// if (AppLink.endPoint != AppLink.seferCairoServer) {
// CRUD().post(
// link: "${AppLink.endPoint}/ride/rides/update.php",
// payload: {
// 'id': rideId,
// 'rideTimeFinish': DateTime.now().toString(),
// 'status': 'Finished',
// 'price': totalCost,
// },
// );
// CRUD().post(
// link: "${AppLink.endPoint}/ride/driver_order/update.php",
// payload: {
// // 'driver_id': box.read(BoxName.driverID).toString(),
// 'order_id': rideId.toString(),
// 'status': 'Finished'
// });
// }
// if (walletChecked == 'true') {
// paymentToken = await generateTokenPassenger(
// ((-1) * double.parse(paymentAmount)).toString());
// await CRUD().post(link: AppLink.addPassengersWallet, payload: {
// 'passenger_id': passengerId,
// 'balance': ((-1) * double.parse(paymentAmount)).toString(),
// 'token': paymentToken,
// });
// }
// paymentToken = await generateTokenDriver(paymentAmount.toString());
// await CRUD().post(link: AppLink.addDrivePayment, payload: {
// 'rideId': rideId,
// 'amount': paymentAmount,
// 'payment_method':
// walletChecked == 'true' ? "${paymentMethod}Ride" : paymentMethod,
// 'passengerID': passengerId,
// 'token': paymentToken,
// 'driverID': box.read(BoxName.driverID).toString(),
// });
// if (double.parse(passengerWalletBurc) < 0) {
// // for zero passenger
// var paymentToken1 = await generateTokenPassenger(
// ((-1) * double.parse(passengerWalletBurc)).toString());
// await CRUD().post(link: AppLink.addPassengersWallet, payload: {
// 'passenger_id': passengerId,
// 'token': paymentToken1,
// 'balance': ((-1) * double.parse(passengerWalletBurc)).toString()
// });
// }
// double pointsSubtraction = 0;
// pointsSubtraction =
// double.parse(paymentAmount) * (-1) * .08; //for 300 from 3000
// var paymentToken2 =
// await generateTokenDriver((pointsSubtraction).toStringAsFixed(0));
// var res = await CRUD().post(link: AppLink.addDriversWalletPoints, payload: {
// 'paymentID': 'rideId$rideId',
// 'amount': (pointsSubtraction).toStringAsFixed(0),
// 'paymentMethod': paymentMethod,
// 'token': paymentToken2,
// 'driverID': box.read(BoxName.driverID).toString(),
// });
// Future.delayed(const Duration(milliseconds: 300));
// Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
// "Driver Finish Trip".tr,
// '${'you will pay to Driver'.tr} $paymentAmount \$',
// tokenPassenger,
// [
// box.read(BoxName.driverID),
// rideId,
// box.read(BoxName.tokenDriver),
// // carType == 'Comfort' || carType == 'Mishwar Vip'
// // ? price.toStringAsFixed(2)
// // : totalPassenger
// paymentAmount.toString()
// ],
// 'ding.wav');
// Get.to(() => RatePassenger(), arguments: {
// 'passengerId': passengerId,
// 'rideId': rideId,
// 'price': paymentAmount.toString(), //price
// 'walletChecked': walletChecked
// });
// // Get.back();
// }
void cancelCheckRideFromPassenger() async {
var res = await CRUD().get(
link: "${AppLink.endPoint}/ride/driver_order/getOrderCancelStatus.php",
payload: {
'order_id': (rideId),
}); //.then((value) {
var response = jsonDecode(res);
canelString = response['data']['status'];
update();
if (canelString == 'Cancel') {
remainingTimeTimerRideBegin = 0;
remainingTimeToShowPassengerInfoWindowFromDriver = 0;
remainingTimeToPassenger = 0;
isRideStarted = false;
isRideFinished = false;
isPassengerInfoWindow = false;
clearPolyline();
update();
MyDialog().getDialog(
'Order Cancelled'.tr,
'Order Cancelled by Passenger'.tr,
() {
Get.offAll(HomeCaptain());
},
);
}
}
int rideTimerFromBegin = 0;
double price = 0;
DateTime currentTime = DateTime.now();
/// أثناء الرحلة: نعرض السعر لحظياً بدون مضاعفة العمولة في كل ثانية.
/// - نستخدم سعر الدقيقة حسب الوقت، مع قواعد الرحلات البعيدة:
/// >25كم أو >35كم => دقيقة = 600، سقف 60 دقيقة، ومع >35كم عفو 10 دقائق.
/// - سرعة طويلة: لو المسافة المخططة > 40كم نستخدم 2600 ل.س/كم للـ Speed،
//// ونطبق نفس نسبة التخفيض على Comfort/Electric/Van.
/// - نضيف فقط "الزيادة" فوق التسعيرة المقتبسة (وقت زائد + كم زائد).
/// - نعكس العمولة kazán مرة واحدة على الزيادة (وليس كل ثانية).
void rideIsBeginPassengerTimer() async {
// === مراجع عامة ===
final hc = Get.find<HomeCaptainController>();
final loc = Get.find<LocationController>();
// أسعار/كم من السيرفر
final double perKmSpeedBase = hc.speedPrice; // مثال: 2900
final double perKmComfortRaw = hc.comfortPrice; // مثال: 3600
final double perKmDelivery = hc.deliveryPrice; // للدليفري
final double perKmVanRaw = hc.familyPrice; // فان
const double electricUpliftKm = 400; // كهرباء = كومفورت + 400/كم
final double perKmElectricRaw = perKmComfortRaw + electricUpliftKm;
// أسعار الدقيقة (ل.س/دقيقة)
final double perMinNature = hc.naturePrice; // طبيعي
final double perMinLate = hc.latePrice; // ليل
final double perMinHeavy = hc.heavyPrice; // ذروة
// Long/بعيد
const double longSpeedThresholdKm = 40.0;
const double longSpeedPerKm = 2600.0;
const double mediumDistThresholdKm = 25.0; // >25كم
const double longDistThresholdKm = 35.0; // >35كم
const double longTripPerMin = 600.0;
const int minuteCapMedium = 60; // سقف 60 دقيقة
const int minuteCapLong = 60; // سقف 60 دقيقة
const int freeMinutesLong = 10; // عفو 10 دقائق عند >35كم
const double extraReduction100 = 0.07; // +7% فوق تخفيض >40كم عند >100كم
const double maxReductionCap = 0.35; // سقف إجمالي للتخفيض
// ——— الأساسات المقتبسة من شاشة التسعير ———
final double basePassengerQuote = double.tryParse(totalCost) ?? 0.0;
// الدقائق المقتبسة (من نفس المصدر الذي تستخدمه مسبقًا)
final int quotedMinutes =
int.tryParse(duration) ?? int.tryParse(durationOfRideValue) ?? 0;
// ——— المسافة المخططة من Notification/OnInit ———
// حاول أولًا من متغيّر الرحلة (تمريره عند onInit من النوتيفيكيشن):
double plannedKm = 0.0;
try {
// أمثلة أسماء: غيّر وفق ما عندك
// 1) إن كانت محفوظة في هذا الكنترولر: this.plannedDistanceKm
plannedKm = (int.parse(distance) as num).toDouble();
} catch (_) {}
final double startKm = loc.totalDistance;
if (plannedKm <= 0) {
// fallback ثالث: خذ المسافة الحيّة وقت البدء كأساس
plannedKm = (startKm > 0) ? startKm : 0.0;
}
// ——— سياق المطار (لا نضيف 20,000 هنا مرة ثانية) ———
bool _isAirport(String s) =>
s.toLowerCase().contains('airport') ||
s.contains('مطار') ||
s.contains('المطار');
final bool airportCtx =
_isAirport(startNameLocation) || _isAirport(endNameLocation);
// عمولة الراكب (kazan ٪)
final double kazanPct = (double.tryParse(kazan) ?? 0.0) / 100.0;
// ——— أدوات ———
double _perMinuteByTime(DateTime now) {
final h = now.hour;
if (airportCtx) return perMinLate; // مطار = دقيقة ليل (مثل منطقك القديم)
if (h >= 21 || h < 1) return perMinLate; // ليل
if (h >= 14 && h <= 17) return perMinHeavy; // ذروة
return perMinNature; // طبيعي
}
bool _isLongSpeed(double km) => km > longSpeedThresholdKm;
double _distanceReductionPct(double km) {
double r40 = 0.0;
if (perKmSpeedBase > 0) {
r40 = (1.0 - (longSpeedPerKm / perKmSpeedBase))
.clamp(0.0, maxReductionCap);
}
if (km > 100.0)
return (r40 + extraReduction100).clamp(0.0, maxReductionCap);
if (km > 40.0) return r40;
return 0.0;
}
// فلترة اهتزاز GPS
const double jitterMeters = 10.0;
double lastKmForNoise = startKm;
// تحديث كل 5 ثواني
final int loopCapSec = (quotedMinutes + 3600) * 60; // أمان
for (int sec = 0; sec <= loopCapSec; sec += 5) {
await Future.delayed(const Duration(seconds: 5));
final now = DateTime.now();
// كم حي (مع فلترة الاهتزاز)
double liveKm = loc.totalDistance;
final double deltaMeters = (liveKm - lastKmForNoise) * 1000.0;
if (deltaMeters < jitterMeters) {
liveKm = lastKmForNoise;
} else {
lastKmForNoise = liveKm;
}
// كم معتمد لقواعد التسعير
final double plannedForRulesKm = (plannedKm > 0) ? plannedKm : liveKm;
// LongSpeed لسعر/كم Speed
final bool isLong = _isLongSpeed(plannedForRulesKm);
// نسبة التخفيض للفئات الأخرى (Comfort/Electric/Van) في كل الأوقات
final double reductionPct = _distanceReductionPct(plannedForRulesKm);
// سعر/كم فعّال لكل فئة
final double perKmSpeed = isLong ? longSpeedPerKm : perKmSpeedBase;
final double perKmBalash = (perKmSpeed - 500).clamp(0, double.infinity);
final double perKmComfort =
(perKmComfortRaw * (1.0 - reductionPct)).clamp(0, double.infinity);
final double perKmElectric =
(perKmElectricRaw * (1.0 - reductionPct)).clamp(0, double.infinity);
final double perKmVan =
(perKmVanRaw * (1.0 - reductionPct)).clamp(0, double.infinity);
final double perKmForType = () {
switch (carType) {
case 'Speed':
return perKmSpeed;
case 'Awfar Car':
return perKmBalash;
case 'Comfort':
case 'Mishwar Vip':
case 'RayehGaiComfort':
case 'Lady':
return perKmComfort;
case 'Electric':
return perKmElectric;
case 'Van':
return perKmVan;
case 'Delivery':
return perKmDelivery;
default:
return perKmSpeed;
}
}();
// قواعد الدقيقة (تطبّق على كل الأوقات بما فيها المطار)
double perMinEff = _perMinuteByTime(now);
int capMinutes = 1 << 30; // لا سقف افتراضيًا
int freeMinutes = 0;
if (plannedForRulesKm > longDistThresholdKm) {
perMinEff = longTripPerMin; // 600/د
capMinutes = minuteCapLong; // سقف 60
freeMinutes = freeMinutesLong; // عفو 10
} else if (plannedForRulesKm > mediumDistThresholdKm) {
perMinEff = longTripPerMin; // 600/د
capMinutes = minuteCapMedium; // سقف 60
}
// دقائق منقضية
final int elapsedMinutes = (sec ~/ 60);
// دقائق إضافية فوق المقتبس (مع السقف/العفو)
int extraMinutes = 0;
final int rawExtra = elapsedMinutes - quotedMinutes - freeMinutes;
if (rawExtra > 0) {
extraMinutes = rawExtra;
if (capMinutes < (1 << 30)) {
final int maxBill = (capMinutes - quotedMinutes - freeMinutes);
if (extraMinutes > maxBill)
extraMinutes = maxBill.clamp(0, extraMinutes);
}
}
// كيلومترات إضافية: إن لم يكن لدينا plannedKm «حقيقي»، نقيس على لحظة البدء
double extraKm = liveKm - plannedForRulesKm;
if (plannedKm <= 0) {
extraKm = (liveKm - startKm);
}
if (extraKm < 0) extraKm = 0;
// زيادات قبل العمولة (بدون إضافة مطار ثابتة — فهي مضافة في التسعير الأولي)
final double extraBefore =
(extraMinutes * perMinEff) + (extraKm * perKmForType);
// السعر المعروض = المقتبس + الزيادات * (1 + kazan)
price = basePassengerQuote + (extraBefore * (1.0 + kazanPct));
// تحديث واجهة
speed = loc.speed * 3.6;
remainingTimeTimerRideBegin =
(quotedMinutes * 60 - sec).clamp(0, 1 << 30);
progressTimerRideBegin = (quotedMinutes == 0)
? 0
: (sec / (quotedMinutes * 60)).clamp(0.0, 1.0);
remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;
updateMarker();
update();
}
} // int durationOfRide = int.parse(durationOfRideValue);
// double latePrice = Get.find<HomeCaptainController>().latePrice;
// update();
// int infinity = 40000;
// if (carType == 'Comfort' ||
// carType == 'Mishwar Vip' ||
// carType == 'RayehGaiComfort') {
// durationOfRide = infinity;
// update();
// }
// price = double.parse(totalCost);
// for (int i = 0; i <= durationOfRide; i++) {
// await Future.delayed(const Duration(seconds: 1));
// recentDistanceToDash = Get.find<LocationController>().totalDistance;
// // rideTimerFromBegin = i;
// if (int.parse(duration) + 300 > i) {
// price = double.parse(totalCost);
// } else {
// if (startNameLocation.toLowerCase().contains('airport') ||
// endNameLocation.toLowerCase().contains('airport') ||
// startNameLocation.contains('مطار') ||
// startNameLocation.contains('المطار') ||
// endNameLocation.contains('مطار') ||
// endNameLocation.contains('المطار')) {
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
// : carType == 'Lady'
// ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
// : carType == 'RayehGaiComfort'
// ? (i ~/ 60) * latePrice +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().comfortPrice)
// : (i ~/ 60) * latePrice +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().mashwariPrice);
// } else if (currentTime.hour >= 21 && currentTime.hour < 0) {
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
// : carType == 'Lady'
// ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
// : carType == 'RayehGaiComfort'
// ? (i ~/ 60) * latePrice +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().comfortPrice)
// : (i ~/ 60) * latePrice +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().mashwariPrice);
// } else if (currentTime.hour >= 1 && currentTime.hour < 5) {
// if (startNameLocation.contains('club') ||
// startNameLocation.contains('nightclub') ||
// startNameLocation.contains('ديسكو') ||
// startNameLocation.contains('ملهى ليلي') ||
// startNameLocation.contains('Night club')) {
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? (i ~/ 60) * (latePrice + .5) * 2 +
// (price) -
// int.parse(duration) * (latePrice + .5) * 2
// : carType == 'Lady'
// ? (i ~/ 60) * (latePrice + .5) * 2 +
// (price) -
// int.parse(duration) * (latePrice + .5) * 2
// : carType == 'RayehGaiComfort'
// ? (i ~/ 60) * (latePrice + .5) * 2 + (price)
// : (i ~/ 60) * (latePrice + .5) * 2 + (price);
// }
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) + price
// : carType == 'Lady'
// ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) +
// price
// : carType == 'RayehGaiComfort'
// ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) +
// price
// : price;
// } else if (currentTime.hour >= 14 && currentTime.hour <= 17) {
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? Get.find<HomeCaptainController>().heavyPrice *
// ((i ~/ 60) - int.parse(duration)) +
// price -
// (0.5 * int.parse(duration))
// : carType == 'Lady'
// ? Get.find<HomeCaptainController>().heavyPrice *
// ((i ~/ 60) - int.parse(duration)) +
// price -
// (0.5 * int.parse(duration))
// : carType == 'RayehGaiComfort'
// ? (i ~/ 60) *
// (Get.find<HomeCaptainController>().heavyPrice) +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().comfortPrice)
// : (i ~/ 60) *
// (Get.find<HomeCaptainController>().heavyPrice) +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().mashwariPrice);
// } else {
// price = carType == 'Comfort' // || carType == 'Free Ride'
// ? (i ~/ 60) + (price) - int.parse(duration)
// : carType == 'Lady'
// ? (i ~/ 60) + (price) - int.parse(duration)
// : carType == 'RayehGaiComfort'
// ? (i ~/ 60) +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().comfortPrice)
// : (i ~/ 60) +
// (recentDistanceToDash *
// Get.find<HomeCaptainController>().mashwariPrice);
// }
// }
// // $1 for each minute + $4 for each km
// price = (price * double.parse(kazan)) + price; // Add 10% tax
// speed = Get.find<LocationController>().speed * 3.6;
// progressTimerRideBegin = i / durationOfRide;
// remainingTimeTimerRideBegin = durationOfRide - i;
// remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;
// updateMarker();
// if (remainingTimeTimerRideBegin < 60) {
// // to make driver available on last 2 minute in his trip
// box.write(BoxName.statusDriverLocation, 'off');
// }
// int minutes = (remainingTimeTimerRideBegin / 60).floor();
// int seconds = remainingTimeTimerRideBegin % 60;
// stringRemainingTimeRideBegin =
// '$minutes:${seconds.toString().padLeft(2, '0')}';
// int minutes1 = (i / 60).floor();
// int seconds1 = i % 60;
// stringRemainingTimeRideBegin1 =
// '$minutes1:${seconds1.toString().padLeft(2, '0')}';
// update();
// }
// }
double recentDistanceToDash = 0;
double recentAngelToMarker = 0;
double speed = 0;
void updateMarker() async {
// Remove the existing marker with the ID `MyLocation`.
markers.remove(MarkerId('MyLocation'));
// Add a new marker with the ID `MyLocation` at the current location of the user.
LocationController locationController = Get.find<LocationController>();
myLocation = locationController.myLocation;
markers.add(
Marker(
markerId: MarkerId('MyLocation'.tr),
position: myLocation,
draggable: true,
icon: carIcon,
rotation: locationController.heading,
// infoWindow: const InfoWindow(
// title: 'Time',
// ),
),
);
// Animate camera only once after updating the marker
// mapController!.animateCamera(
// CameraUpdate.newLatLng(myLocation),
// );
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 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',
).then((value) {
startIcon = value;
update();
});
}
void addCustomEndIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(25, 25), devicePixelRatio: Get.pixelRatio);
BitmapDescriptor.asset(
config,
'assets/images/b.png',
).then((value) {
endIcon = value;
update();
});
}
void addCustomPassengerIcon() {
ImageConfiguration config = ImageConfiguration(
size: const Size(30, 30), devicePixelRatio: Get.pixelRatio
// scale: 1.0,
);
BitmapDescriptor.asset(
config,
'assets/images/picker.png',
).then((value) {
passengerIcon = value;
update();
});
}
var activeRouteSteps = <Map<String, dynamic>>[];
var traveledPathPoints = <LatLng>[]; // المسار المقطوع (رمادي)
var upcomingPathPoints = <LatLng>[]; // المسار المتبقي (أزرق/أحمر)
// --- متغيرات الأيقونات والمواقع ---
var heading = 0.0;
// ... يمكنك إضافة أيقونات البداية والنهاية هنا
// --- متغيرات قياس الأداء الذكي ---
final List<int> _performanceReadings = [];
final int _readingsToCollect = 10; // اجمع 10 قراءات
bool _hasMadeDecision = false;
var updateInterval = 5.obs; // القيمة الافتراضية
// --- متغيرات داخلية للملاحة ---
var _stepBounds = <LatLngBounds>[];
var _stepEndPoints = <LatLng>[];
var _allPointsForActiveRoute = <LatLng>[];
// دالة موحّدة وقوية لجلب أي مسار
Future<void> getRoute({
required LatLng origin,
required LatLng destination,
required Color routeColor,
}) async {
// إظهار مؤشر التحميل لو رغبت
// isLoading.value = true;
var url =
('${AppLink.googleMapsLink}directions/json?language=ar&destination=${destination.latitude},${destination.longitude}&origin=${origin.latitude},${origin.longitude}&key=${AK.mapAPIKEY}');
var response = await CRUD().getGoogleApi(link: url, payload: {});
if (response == null || response['routes'].isEmpty) {
// isLoading.value = false;
// أظهر رسالة خطأ
return;
}
_resetRouteState(); // تنظيف الحالة القديمة قبل رسم الجديد
final route = response['routes'][0];
final leg = route['legs'][0];
// استخراج النقاط ورسم المسار المبدئي بالكامل
final pointsString = route["overview_polyline"]["points"];
_allPointsForActiveRoute = decodePolylineToLatLng(pointsString);
upcomingPathPoints.assignAll(_allPointsForActiveRoute);
// استخراج خطوات الملاحة
activeRouteSteps.assignAll(List<Map<String, dynamic>>.from(leg['steps']));
_prepareStepData(activeRouteSteps);
// تحديث التعليمات الأولية
if (activeRouteSteps.isNotEmpty) {
currentInstruction =
_parseInstruction(activeRouteSteps[0]['html_instructions']);
Get.find<TextToSpeechController>().speakText(currentInstruction);
}
// تحديث الكاميرا لتناسب المسار الجديد
final boundsData = route["bounds"];
_fitToBounds(LatLngBounds(
northeast: LatLng(
boundsData['northeast']['lat'], boundsData['northeast']['lng']),
southwest: LatLng(
boundsData['southwest']['lat'], boundsData['southwest']['lng']),
));
// isLoading.value = false;
update(); // تحديث الواجهة مرة واحدة بعد كل العمليات
}
// الدالة التي يتم استدعاؤها من خدمة الموقع كل 5 ثوان (أو حسب الفترة المحددة)
void onLocationUpdated(Position newPosition) {
myLocation = LatLng(newPosition.latitude, newPosition.longitude);
heading = newPosition.heading;
// -->> منطق قياس الأداء يبدأ هنا <<--
final stopwatch = Stopwatch()..start();
// -->> منطق الملاحة وتحديث المسار <<--
_onLocationTick(myLocation);
stopwatch.stop();
// -->> تحليل الأداء واتخاذ القرار <<--
if (!_hasMadeDecision) {
_performanceReadings.add(stopwatch.elapsedMilliseconds);
if (_performanceReadings.length >= _readingsToCollect) {
_analyzePerformance();
_hasMadeDecision = true;
}
}
}
// =================================================================
// 3. منطق الملاحة الداخلي (Internal Navigation Logic)
// =================================================================
void _onLocationTick(LatLng pos) {
if (activeRouteSteps.isEmpty || currentStepIndex >= _stepBounds.length) {
return;
}
final double dToEnd =
_distanceMeters(pos, _stepEndPoints[currentStepIndex]);
if (dToEnd <= 35) {
// 35 متر عتبة للوصول لنهاية الخطوة
_advanceStep();
}
}
void _advanceStep() {
if (currentStepIndex >= _stepBounds.length - 1) {
// وصل للنهاية
currentInstruction = "لقد وصلت إلى وجهتك";
return;
}
currentStepIndex++;
currentInstruction = _parseInstruction(
activeRouteSteps[currentStepIndex]['html_instructions']);
Get.find<TextToSpeechController>().speakText(currentInstruction);
// -->> هنا يتم تحديث لون المسار <<--
_updateTraveledPath();
_fitToBounds(_stepBounds[currentStepIndex],
padding: 80); // تقريب الكاميرا على الخطوة التالية
update();
}
void _updateTraveledPath() {
// استخراج كل النقاط للخطوات التي تم اجتيازها
List<LatLng> pointsForTraveledSteps = [];
for (int i = 0; i < currentStepIndex; i++) {
final stepPolyline = activeRouteSteps[i]['polyline']['points'];
pointsForTraveledSteps.addAll(decodePolylineToLatLng(stepPolyline));
}
traveledPathPoints.assignAll(pointsForTraveledSteps);
}
void _prepareStepData(List<Map<String, dynamic>> steps) {
_stepBounds.clear();
_stepEndPoints.clear();
for (final s in steps) {
// 1. استخراج نقطة النهاية (الكود الحالي سليم)
final end = s['end_location'];
_stepEndPoints.add(LatLng(
(end['lat'] as num).toDouble(),
(end['lng'] as num).toDouble(),
));
// 2. فك تشفير البوليلاين الخاص بالخطوة وتحويله إلى LatLng
// -->> هنا تم التصحيح <<--
List<LatLng> pts = decodePolyline(s['polyline']['points'])
.map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
.toList();
// أضف نقاط البداية والنهاية إذا لم تكن موجودة في البوليلاين لضمان دقة الحدود
if (pts.isNotEmpty) {
final start = s['start_location'];
final startLatLng = LatLng(
(start['lat'] as num).toDouble(), (start['lng'] as num).toDouble());
if (pts.first != startLatLng) {
pts.insert(0, startLatLng);
}
}
_stepBounds.add(_boundsFromPoints(pts));
}
}
// A helper function to decode and convert the polyline string
List<LatLng> decodePolylineToLatLng(String polylineString) {
// 1. Decode the string into a list of number lists (e.g., [[lat, lng], ...])
List<List<num>> decodedPoints = decodePolyline(polylineString);
// 2. Map each [lat, lng] pair to a LatLng object, ensuring conversion to double
List<LatLng> latLngPoints = decodedPoints
.map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
.toList();
return latLngPoints;
}
// =================================================================
// 4. منطق الأداء الذكي (Smart Performance Logic)
// =================================================================
void _analyzePerformance() {
final int sum = _performanceReadings.reduce((a, b) => a + b);
final double averageTime = sum / _performanceReadings.length;
if (averageTime > 1000) {
// إذا كانت العملية تستغرق أكثر من ثانية
_suggestOptimization();
}
}
void _suggestOptimization() {
Get.snackbar(
"تحسين أداء التطبيق",
"لضمان أفضل تجربة، نقترح تعديل الإعدادات لتناسب جهازك. هل تود المتابعة؟",
duration: const Duration(seconds: 15),
mainButton: TextButton(
child: const Text("نعم، قم بالتحسين"),
onPressed: () {
updateInterval.value = 8; // غير الفترة إلى 8 ثوانٍ
// save setting to shared_preferences
box.write(BoxName.updateInterval, 8);
Get.back();
},
),
);
}
// =================================================================
// 5. دوال مساعدة (Helper Functions)
// =================================================================
void _resetRouteState() {
activeRouteSteps.clear();
traveledPathPoints.clear();
upcomingPathPoints.clear();
_allPointsForActiveRoute.clear();
currentStepIndex = 0;
}
String _parseInstruction(String html) =>
html.replaceAll(RegExp(r'<[^>]*>'), '');
Future<void> _fitToBounds(LatLngBounds b, {double padding = 60}) async {
await mapController
?.animateCamera(CameraUpdate.newLatLngBounds(b, padding));
}
double distanceBetweenDriverAndPassengerWhenConfirm = 0;
getMap(String origin, destination) async {
isLoading = false;
update();
var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
var response = await CRUD().getGoogleApi(link: url, payload: {});
Log.print('response: ${response}');
data = response['routes'][0]['legs'];
distanceBetweenDriverAndPassengerWhenConfirm =
(data[0]['distance']['value']) / 1000;
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));
}
if (polyLines.isNotEmpty) {
clearPolyline();
var polyline = Polyline(
polylineId: PolylineId(response["routes"][0]["summary"]),
points: polylineCoordinates,
width: 10,
color: AppColor.blueColor,
);
polyLines.add(polyline);
// rideConfirm = false;
update();
} else {
var polyline = Polyline(
polylineId: PolylineId(response["routes"][0]["summary"]),
points: polylineCoordinates,
width: 10,
color: AppColor.blueColor,
);
// final dataBounds = response["routes"][0]["bounds"];
// updateCameraFromBoundsAfterGetMap(dataBounds);
// Fit the camera to the bounds
polyLines.add(polyline);
// rideConfirm = false;
// 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
boundsData = LatLngBounds(northeast: northeast, southwest: southwest);
// Fit the camera to the bounds
var cameraUpdate = CameraUpdate.newLatLngBounds(boundsData!, 140);
mapController!.animateCamera(cameraUpdate);
update();
}
}
void checkForNextStep(LatLng currentPosition) {
if (currentStepIndex >= routeSteps.length) return;
final step = routeSteps[currentStepIndex];
final endLocation = step['end_location'];
final endLatLng = LatLng(endLocation['lat'], endLocation['lng']);
final distance = calculateDistance(
currentPosition.latitude,
currentPosition.longitude,
endLatLng.latitude,
endLatLng.longitude,
);
if (distance < 50) {
// 50 متر قبل النقطة
currentStepIndex++;
if (currentStepIndex < routeSteps.length) {
currentInstruction = _parseInstruction(
routeSteps[currentStepIndex]['html_instructions']);
Get.isRegistered<TextToSpeechController>()
? Get.find<TextToSpeechController>().speakText(currentInstruction)
: Get.put(TextToSpeechController()).speakText(currentInstruction);
Log.print('Current Instruction: $currentInstruction');
update();
}
}
}
/// Calculates the distance in meters between two latitude/longitude points.
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
const double earthRadius = 6371000; // meters
double dLat = _degreesToRadians(lat2 - lat1);
double dLon = _degreesToRadians(lon2 - lon1);
double a = (sin(dLat / 2) * sin(dLat / 2)) +
cos(_degreesToRadians(lat1)) *
cos(_degreesToRadians(lat2)) *
(sin(dLon / 2) * sin(dLon / 2));
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
double distance = earthRadius * c;
return distance;
}
double _degreesToRadians(double degrees) {
return degrees * (3.1415926535897932 / 180.0);
}
// String _parseInstruction(String htmlInstruction) {
// return htmlInstruction.replaceAll(RegExp(r'<[^>]*>'), '');
// }
void checkDestinationProximity() {
final distance = calculateDistance(
myLocation.latitude,
myLocation.longitude,
latLngPassengerDestination.latitude,
latLngPassengerDestination.longitude,
);
if (distance < 300) {
// 300 متر قبل الوجهة
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"You are near the destination".tr,
"You are near the destination".tr,
tokenPassenger,
[
box.read(BoxName.driverID),
rideId,
box.read(BoxName.tokenDriver),
paymentAmount.toString()
],
'ding.wav',
);
// يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة
}
}
List<LatLngBounds> stepBounds = [];
List<LatLng> stepEndPoints = [];
List<String> stepInstructions = [];
StreamSubscription<Position>? _posSub;
DateTime _lastCameraUpdateTs = DateTime.fromMillisecondsSinceEpoch(0);
getMapDestination(String origin, destination) async {
var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
var response = await CRUD().getGoogleApi(link: url, payload: {});
dataDestination = response['routes'][0]['legs'];
final points =
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
polylineCoordinatesDestination.clear();
for (int i = 0; i < points.length; i++) {
double lat = points[i][0].toDouble();
double lng = points[i][1].toDouble();
polylineCoordinatesDestination.add(LatLng(lat, lng));
}
// الخطوات من أول leg
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
// حضّر بيانات كل step
_prepareStepData(routeSteps);
currentStepIndex = 0;
if (stepInstructions.isNotEmpty) {
currentInstruction = _parseInstruction(stepInstructions[0]);
Log.print('currentInstruction: $currentInstruction');
Get.isRegistered<TextToSpeechController>()
? Get.find<TextToSpeechController>().speakText(currentInstruction)
: Get.put(TextToSpeechController()).speakText(currentInstruction);
}
// ارسم الـ polyline الرئيسي (مثل ما لديك)
final summary = response["routes"][0]["summary"];
final polyline = Polyline(
polylineId: PolylineId(summary.isEmpty ? 'route' : summary),
points: polylineCoordinatesDestination,
width: 10,
color: AppColor.redColor,
);
polyLinesDestination.add(polyline);
// fit للخريطة على أول step
if (stepBounds.isNotEmpty) {
_fitToBounds(stepBounds[0]);
}
update();
// // ابدأ التتبع الحيّ لخطوات الملاحة
// await startListeningStepNavigation();
}
// ————————————————————————————————————————————————————————————————
// يُنادى عند كل تحديث للموقع
// void _onLocationTick(LatLng pos) async {
// if (routeSteps.isEmpty || currentStepIndex >= stepBounds.length) return;
// // إذا تجاوزت نهاية الـ step الحالية بمسافة صغيرة -> انتقل
// final double dToEnd = _distanceMeters(pos, stepEndPoints[currentStepIndex]);
// final bool nearEnd = dToEnd <= 25; // عتبة 25م (عدّلها حسب حاجتك)
// // إذا دخلت نطاق الـ step التالية -> انتقل
// bool insideNext = false;
// if (currentStepIndex < stepBounds.length - 1) {
// insideNext = _contains(stepBounds[currentStepIndex + 1], pos);
// }
// if (nearEnd || insideNext) {
// _advanceStep();
// return;
// }
// // تحديث الكاميرا بشكل خفيف أثناء الحركة (كل 2 ثانية مثلاً)
// final now = DateTime.now();
// if (now.difference(_lastCameraUpdateTs).inMilliseconds > 2000) {
// _lastCameraUpdateTs = now;
// // ركّز على bounds الحالية مع الحفاظ على تتبّع عام
// _fitToBounds(stepBounds[currentStepIndex], padding: 60);
// }
// }
LatLngBounds _boundsFromPoints(List<LatLng> pts) {
double? minLat, maxLat, minLng, maxLng;
for (final p in pts) {
minLat = (minLat == null) ? p.latitude : math.min(minLat, p.latitude);
maxLat = (maxLat == null) ? p.latitude : math.max(maxLat, p.latitude);
minLng = (minLng == null) ? p.longitude : math.min(minLng, p.longitude);
maxLng = (maxLng == null) ? p.longitude : math.max(maxLng, p.longitude);
}
return LatLngBounds(
southwest: LatLng(minLat ?? 0, minLng ?? 0),
northeast: LatLng(maxLat ?? 0, maxLng ?? 0),
);
}
bool _contains(LatLngBounds b, LatLng p) {
final south = math.min(b.southwest.latitude, b.northeast.latitude);
final north = math.max(b.southwest.latitude, b.northeast.latitude);
final west = math.min(b.southwest.longitude, b.northeast.longitude);
final east = math.max(b.southwest.longitude, b.northeast.longitude);
return (p.latitude >= south &&
p.latitude <= north &&
p.longitude >= west &&
p.longitude <= east);
}
double _distanceMeters(LatLng a, LatLng b) {
// هافرساين مبسطة
const R = 6371000.0; // m
final dLat = _deg2rad(b.latitude - a.latitude);
final dLng = _deg2rad(b.longitude - a.longitude);
final s1 = math.sin(dLat / 2);
final s2 = math.sin(dLng / 2);
final aa = s1 * s1 +
math.cos(_deg2rad(a.latitude)) *
math.cos(_deg2rad(b.latitude)) *
s2 *
s2;
final c = 2 * math.atan2(math.sqrt(aa), math.sqrt(1 - aa));
return R * c;
}
double _deg2rad(double d) => d * math.pi / 180.0;
// تحريك الكاميرا لباوند معيّن
// Future<void> _fitToBounds(LatLngBounds b, {double padding = 40}) async {
// if (mapController == null) return;
// try {
// // أحياناً يلزم انتظار فريم حتى تكون الخريطة مرسومة
// await Future.delayed(const Duration(milliseconds: 50));
// await mapController!.animateCamera(
// CameraUpdate.newLatLngBounds(b, padding),
// );
// } catch (_) {
// // fallback لو حصلت مشكلة الحجم
// final center = LatLng(
// (b.northeast.latitude + b.southwest.latitude) / 2,
// (b.northeast.longitude + b.southwest.longitude) / 2,
// );
// await mapController!.animateCamera(CameraUpdate.newLatLng(center));
// }
// }
// الانتقال للخطوة التالية وتحديث التعليمات والكاميرا
// void _advanceStep() {
// if (currentStepIndex >= stepBounds.length - 1) return;
// currentStepIndex++;
// currentInstruction = _parseInstruction(stepInstructions[currentStepIndex]);
// // نطق التعليمات
// if (Get.isRegistered<TextToSpeechController>()) {
// Get.find<TextToSpeechController>().speakText(currentInstruction);
// } else {
// Get.put(TextToSpeechController()).speakText(currentInstruction);
// }
// // تركيز الكاميرا على باوند الخطوة الجديدة
// _fitToBounds(stepBounds[currentStepIndex]);
// update();
// }
// void _prepareStepData(List<Map<String, dynamic>> steps) {
// stepBounds.clear();
// stepEndPoints.clear();
// stepInstructions.clear();
// for (final s in steps) {
// // 1) instruction
// final html = (s['html_instructions'] ?? '').toString();
// stepInstructions.add(html);
// // 2) end point
// final end = s['end_location'];
// final endLatLng = LatLng(
// (end['lat'] as num).toDouble(), (end['lng'] as num).toDouble());
// stepEndPoints.add(endLatLng);
// // 3) bounds من الـ polyline (إن لم يوجد bounds جاهز من الـ API)
// List<LatLng> pts = [];
// if (s['polyline'] != null && s['polyline']['points'] != null) {
// final decoded = decodePolyline(s['polyline']['points']);
// for (var p in decoded) {
// pts.add(LatLng((p[0] as num).toDouble(), (p[1] as num).toDouble()));
// }
// } else {
// // fallback: استخدم start/end فقط
// final start = s['start_location'];
// pts.add(LatLng((start['lat'] as num).toDouble(),
// (start['lng'] as num).toDouble()));
// pts.add(endLatLng);
// }
// stepBounds.add(_boundsFromPoints(pts));
// }
// }
// getMapDestination(String origin, destination) async {
// var url =
// ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
// var response = await CRUD().getGoogleApi(link: url, payload: {});
// dataDestination = response['routes'][0]['legs'];
// 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();
// polylineCoordinatesDestination.add(LatLng(lat, lng));
// }
// // استخراج الخطوات
// routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
// Log.print('routeSteps: ${routeSteps}');
// currentStepIndex = 0;
// if (routeSteps.isNotEmpty) {
// currentInstruction =
// _parseInstruction(routeSteps[0]['html_instructions']);
// Log.print('currentInstruction: ${currentInstruction}');
// Get.isRegistered<TextToSpeechController>()
// ? Get.find<TextToSpeechController>().speakText(currentInstruction)
// : Get.put(TextToSpeechController()).speakText(currentInstruction);
// }
// update();
// // دالة مساعدة لتنظيف التعليمات
// if (polyLinesDestination.isNotEmpty) {
// // clearPolyline();
// var polyline = Polyline(
// polylineId: PolylineId(response["routes"][0]["summary"]),
// points: polylineCoordinatesDestination,
// width: 10,
// color: AppColor.redColor,
// );
// polyLinesDestination.add(polyline);
// // rideConfirm = false;
// update();
// } else {
// var polyline = Polyline(
// polylineId: PolylineId(response["routes"][0]["summary"]),
// points: polylineCoordinatesDestination,
// width: 10,
// color: AppColor.redColor,
// );
// // final dataBounds = response["routes"][0]["bounds"];
// // updateCameraFromBoundsAfterGetMap(dataBounds);
// // polyLinesDestination.add(polyline);
// // rideConfirm = false;
// // Define the northeast and southwest coordinates
// update();
// }
// }
void updateCameraFromBoundsAfterGetMap(dynamic response) {
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, 140);
mapController!.animateCamera(cameraUpdate);
}
void changePassengerInfoWindow() {
isPassengerInfoWindow = !isPassengerInfoWindow;
passengerInfoWindowHeight = isPassengerInfoWindow == true ? 200 : 0;
update();
}
double mpg = 0;
calculateConsumptionFuel() {
mpg = Get.find<HomeCaptainController>().fuelPrice /
12; //todo in register car add mpg in box
update();
}
argumentLoading() async {
passengerLocation = Get.arguments['passengerLocation'];
passengerDestination = Get.arguments['passengerDestination'];
duration = Get.arguments['Duration'];
totalCost = Get.arguments['totalCost'];
passengerId = Get.arguments['passengerId'];
driverId = Get.arguments['driverId'];
distance = Get.arguments['Distance'];
passengerName = Get.arguments['name'];
passengerEmail = Get.arguments['email'];
totalPricePassenger = Get.arguments['totalPassenger'];
passengerPhone = Get.arguments['phone'];
walletChecked = Get.arguments['WalletChecked'];
tokenPassenger = Get.arguments['tokenPassenger'];
direction = Get.arguments['direction'];
durationToPassenger = Get.arguments['DurationToPassenger'];
rideId = Get.arguments['rideId'];
durationOfRideValue = Get.arguments['durationOfRideValue'];
paymentAmount = Get.arguments['paymentAmount'];
paymentMethod = Get.arguments['paymentMethod'];
isHaveSteps = Get.arguments['isHaveSteps'];
step0 = Get.arguments['step0'];
step1 = Get.arguments['step1'];
step2 = Get.arguments['step2'];
step3 = Get.arguments['step3'];
step4 = Get.arguments['step4'];
passengerWalletBurc = Get.arguments['passengerWalletBurc'];
timeOfOrder = Get.arguments['timeOfOrder'];
carType = Get.arguments['carType'];
kazan = Get.arguments['kazan'];
startNameLocation = Get.arguments['startNameLocation'];
endNameLocation = Get.arguments['endNameLocation'];
// Parse to double
latlng(passengerLocation, passengerDestination);
String lat = Get.find<LocationController>().myLocation.latitude.toString();
String lng = Get.find<LocationController>().myLocation.longitude.toString();
String origin = '$lat,$lng';
// Set the origin and destination coordinates for the Google Maps directions request.
Future.delayed(const Duration(seconds: 1));
getRoute(
origin: Get.find<LocationController>().myLocation,
destination: latLngPassengerLocation,
routeColor: Colors.yellow // أو أي لون
);
// getRoute(
// origin: latLngPassengerLocation,
// destination: latLngPassengerDestination,
// routeColor: Colors.blue // أو أي لون
// );
// getMap(origin, passengerLocation);
// isHaveSteps == 'haveSteps'
// ? (
// await getMapDestination(step0, step1),
// await getMapDestination(step1, step2),
// step3 == '' ? await getMapDestination(step2, step3) : () {},
// step4 == '' ? await getMapDestination(step3, step4) : () {},
// )
// : await getMapDestination(passengerLocation, passengerDestination);
update();
}
latlng(String passengerLocation, passengerDestination) {
double latPassengerLocation =
double.parse(passengerLocation.toString().split(',')[0]);
double lngPassengerLocation =
double.parse(passengerLocation.toString().split(',')[1]);
double latPassengerDestination =
double.parse(passengerDestination.toString().split(',')[0]);
double lngPassengerDestination =
double.parse(passengerDestination.toString().split(',')[1]);
latLngPassengerLocation =
LatLng(latPassengerLocation, lngPassengerLocation);
latLngPassengerDestination =
LatLng(latPassengerDestination, lngPassengerDestination);
}
late Duration durationToAdd;
int hours = 0;
int minutes = 0;
late String carType;
late String kazan;
late String startNameLocation;
late String endNameLocation;
Future<void> runGoogleMapDirectly() async {
if (box.read(BoxName.googlaMapApp) == true) {
if (Platform.isAndroid) {
Bubble().startBubbleHead(sendAppToBackground: true);
}
await openGoogleMapFromDriverToPassenger();
}
}
@override
void onInit() async {
mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
// Get the passenger location from the arguments.
await argumentLoading();
Get.put(FirebaseMessagesController());
runGoogleMapDirectly();
addCustomCarIcon();
addCustomPassengerIcon();
addCustomStartIcon();
addCustomEndIcon();
// updateMarker();
// updateLocation();
startTimerToShowPassengerInfoWindowFromDriver();
durationToAdd = Duration(seconds: int.parse(duration));
hours = durationToAdd.inHours;
minutes = (durationToAdd.inMinutes % 60).round();
calculateConsumptionFuel();
updateLocation();
// cancelCheckRidefromPassenger();
// checkIsDriverNearPassenger();
super.onInit();
}
Timer? _navigationTimer;
Future<void> startListeningStepNavigation() async {
_posSub?.cancel();
_navigationTimer?.cancel();
Position? pos;
_posSub = Geolocator.getPositionStream(
locationSettings: LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 5,
// حدّث كل ~5 متر لتقليل الاهتزاز)
),
).listen((position) {
// خزّن آخر موقع معروف، لكن لا تعالجه فوراً
myLocation = LatLng(position.latitude, position.longitude);
heading = position.heading;
update();
});
_navigationTimer = Timer.periodic(
Duration(
seconds: box.read(BoxName.updateInterval) ?? 5,
), (timer) {
// تأكد أن لدينا موقع صالح قبل المعالجة
if (myLocation.latitude != 0) {
// استدعِ دالة المعالجة الثقيلة هنا
onLocationUpdated(pos!);
}
});
// _posSub = Geolocator.getPositionStream(
// locationSettings: const LocationSettings(
// accuracy: LocationAccuracy.high,
// distanceFilter: 5, // حدّث كل ~5 متر لتقليل الاهتزاز
// ),
// ).listen((pos) => _onLocationTick(LatLng(pos.latitude, pos.longitude)));
}
void stopListeningStepNavigation() {
_posSub?.cancel();
_posSub = null;
}
}