5972 lines
210 KiB
Dart
5972 lines
210 KiB
Dart
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);
|
||
});
|
||
|
||
// خذ أول 10–15 للعرض (اختياري)، أو اعرض الكل
|
||
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,
|
||
});
|
||
}
|