Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps

This commit is contained in:
Hamza-Ayed
2026-06-01 23:36:27 +03:00
parent 118781fd66
commit 97945aa362
76 changed files with 19806 additions and 10822 deletions

View File

@@ -0,0 +1,89 @@
#!/bin/bash
ORIG_FILE="lib/controller/home/map_passenger_controller.dart"
ALL_FILES="lib/controller/home/map/location_search_controller.dart lib/controller/home/map/map_engine_controller.dart lib/controller/home/map/map_screen_binding.dart lib/controller/home/map/map_socket_controller.dart lib/controller/home/map/nearby_drivers_controller.dart lib/controller/home/map/ride_lifecycle_controller.dart lib/controller/home/map/ui_interactions_controller.dart"
echo "Extracting methods from original controller..."
# Methods typically start with spaces and have patterns like:
# returnType methodName( or methodName(
# Let's extract words that precede ( on lines that don't start with keywords (if, for, while, switch, catch, etc.)
# We will use awk to parse.
METHODS=$(cat "$ORIG_FILE" | awk '
# Skip single-line comments
/\/\// { next }
# Skip imports and class declarations
/import/ || /class/ { next }
# Find lines with "("
/\(/ {
# Replace anything inside parentheses and curly braces to simplify
gsub(/\(.*\)/, "()")
# Find word before "()"
for (i = 1; i <= NF; i++) {
if ($i ~ /[a-zA-Z0-9_]+\(\)/) {
name = $i
sub(/\(\)/, "", name)
# Remove any leading modifiers like async, Future, static, etc.
# Only keep valid identifiers that are not control keywords
if (name !~ /^(if|for|while|switch|catch|super|await|print|assert|dynamic|void|return|with|override|get|set|else|try|final|const|var|late|static|factory|new|abstract|covariant|external|operator|part|required|typedef|yield)$/ && name ~ /^[a-zA-Z_][a-zA-Z0-9_]*$/) {
print name
}
}
}
}' | sort -u)
echo "Extracting fields/variables from original controller..."
# Fields are usually declared inside the class at the beginning of lines or indented.
# e.g., RxBool isSearching = false.obs; or String? rideId;
VARS=$(cat "$ORIG_FILE" | awk '
/\/\// { next }
/import/ || /class/ { next }
# Lines ending with ";" or containing "=" followed by ";"
/;/ {
# Extract words that look like declarations.
# We look for typical type names or var/final followed by variable name
for (i = 1; i < NF; i++) {
if ($i ~ /^(var|final|const|late|RxBool|RxInt|RxDouble|RxString|RxList|RxMap|RxSet|Rx|String|int|double|bool|List|Map|Set|Timer|LatLng|Position|IntaleqMapController)$/) {
# The next field might be the variable name, or it might have a type like String?
name = $(i+1)
# Remove trailing ?, ;, =
sub(/\?/, "", name)
sub(/;/, "", name)
sub(/=/, "", name)
if (name ~ /^[a-zA-Z_][a-zA-Z0-9_]*$/) {
print name
}
}
}
}' | sort -u)
echo "Checking split files for methods..."
echo "--- MISSING METHODS ---"
MISSING_METHODS_COUNT=0
# Create a temporary file with all split contents to search efficiently
cat $ALL_FILES > lib/controller/home/temp_split_combined.txt
for method in $METHODS; do
# Search for this method name as a whole word in split controllers
FOUND=$(grep -w "$method" lib/controller/home/temp_split_combined.txt 2>/dev/null)
if [ -z "$FOUND" ]; then
echo " - $method"
MISSING_METHODS_COUNT=$((MISSING_METHODS_COUNT+1))
fi
done
echo "Total missing methods: $MISSING_METHODS_COUNT"
echo ""
echo "Checking split files for variables/fields..."
echo "--- MISSING VARIABLES ---"
MISSING_VARS_COUNT=0
for var in $VARS; do
FOUND=$(grep -w "$var" lib/controller/home/temp_split_combined.txt 2>/dev/null)
if [ -z "$FOUND" ]; then
echo " - $var"
MISSING_VARS_COUNT=$((MISSING_VARS_COUNT+1))
fi
done
echo "Total missing variables: $MISSING_VARS_COUNT"
# Clean up temp file
rm lib/controller/home/temp_split_combined.txt

View File

@@ -0,0 +1,104 @@
import sys
import re
def parse_stream(stream_text):
# Splits the stream by our custom file delimiters
files = {}
parts = re.split(r'=== FILE: (.*?) ===\n', stream_text)
# The first part is the original monolithic file
if parts:
files['original'] = parts[0]
for i in range(1, len(parts), 2):
filename = parts[i]
content = parts[i+1] if i+1 < len(parts) else ""
files[filename] = content
return files
def strip_comments(text):
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
text = re.sub(r'//.*', '', text)
return text
def extract_declarations(text):
clean = strip_comments(text)
# Matches method/function declarations inside a class in Dart
# e.g., void myMethod(..., Future<void> myMethod(..., myMethod(..., get myProp, set myProp
# We look for word followed by ( or get/set followed by word.
method_decl_pattern = re.compile(
r'(?:[a-zA-Z0-9_<>\?\[\]]+(?:\s+[a-zA-Z0-9_<>\?\[\]]+)*\s+)?([a-zA-Z0-9_]+)\s*\([^\)]*\)\s*(?:async)?\s*(?:=>|\{)'
)
methods = set()
for match in method_decl_pattern.finditer(clean):
method_name = match.group(1)
if method_name not in keywords and not method_name.isdigit():
methods.add(method_name)
# Also extract getters and setters
getset_pattern = re.compile(r'\b(?:get|set)\s+([a-zA-Z0-9_]+)\b')
for match in getset_pattern.finditer(clean):
name = match.group(1)
if name not in keywords:
methods.add(name)
# Extract variables/fields declarations
var_decl_pattern = re.compile(
r'\b(?:var|final|const|late|RxBool|RxInt|RxDouble|RxString|RxList|RxMap|RxSet|Rx|String|int|double|bool|List|Map|Set|Timer|LatLng|Position|IntaleqMapController)\??\s+([a-zA-Z0-9_]+)\b'
)
variables = set()
for match in var_decl_pattern.finditer(clean):
var_name = match.group(1)
if var_name not in keywords and not var_name.isdigit():
variables.add(var_name)
return methods, variables
keywords = {
'if', 'for', 'while', 'switch', 'catch', 'super', 'await', 'print',
'assert', 'dynamic', 'void', 'return', 'with', 'override', 'get', 'set',
'class', 'import', 'extends', 'implements', 'mixin', 'this', 'else', 'try',
'final', 'const', 'var', 'late', 'static', 'factory', 'new', 'abstract',
'covariant', 'external', 'operator', 'part', 'required', 'typedef', 'yield'
}
def main():
stream_text = sys.stdin.read()
files = parse_stream(stream_text)
orig_content = files.get('original', '')
split_contents = {k: v for k, v in files.items() if k != 'original'}
orig_methods, orig_vars = extract_declarations(orig_content)
# Combined declarations in split files
split_methods = set()
split_vars = set()
for filename, content in split_contents.items():
m, v = extract_declarations(content)
split_methods.update(m)
split_vars.update(v)
missing_methods = sorted(orig_methods - split_methods)
missing_vars = sorted(orig_vars - split_vars)
print("--- PRECISE MISSING METHODS ---")
print(f"Total original methods/getters/setters: {len(orig_methods)}")
print(f"Total defined in split controllers: {len(split_methods)}")
print(f"Total missing: {len(missing_methods)}")
for m in missing_methods:
print(f" - {m}")
print("\n--- PRECISE MISSING VARIABLES/FIELDS ---")
print(f"Total original variables: {len(orig_vars)}")
print(f"Total defined in split controllers: {len(split_vars)}")
print(f"Total missing: {len(missing_vars)}")
for v in missing_vars:
print(f" - {v}")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,103 @@
Extracting methods from original controller...
Extracting fields/variables from original controller...
Checking split files for methods...
--- MISSING METHODS ---
- _applyLowEndModeIfNeeded
- _buildOsrmWaypointCoords
- _calculateDistance
- _checkAndRecalculateIfDeviated
- _fillDriverDataLocally
- _haversineKm
- _initMinimalIcons
- _initializePolygons
- _isActiveRideState
- _kmToLatDelta
- _kmToLngDelta
- _onDriverArrivedWithSocket
- _onRideCancelledWithSocket
- _onRideStartedWithSocket
- _relevanceScore
- _restorePolyline
- _stageNiceToHave
- _stagePricingAndState
- _startMasterTimer
- _startMasterTimerWithInterval
- _startPollingFallback
- _stopDriverLocationPolling
- _updateDriverMarker
- cancelRide
- detectPerfMode
- getAIKey
- getMapPointsForAllMethods
- getPassengerLocationUniversity
- handleActiveRideOnStartup
- isDriversDataValid
- onChangedPassengerCount
- onChangedPassengersChoose
- showDrawingBottomSheet
- showNoDriversDialog
- startSearchingTimer
Total missing methods: 35
Checking split files for variables/fields...
--- MISSING VARIABLES ---
- _isStateProcessing
- _isUsingFallback
- _maxReconnectAttempts
- apiDistanceMeters
- c
- carInfo
- carsOrder
- coordDestination
- currentCarType
- currentDriverLocation
- currentLocationOfDrivers
- currentRideId
- currentTimeSearchingCaptainWindow
- dInfo
- dLat
- datadriverCarsLocationToPassengerAfterApplied
- distanceOfTrip
- driverCarPlate
- driverLocationToPassenger
- driverOrderStatus
- durationByPassenger
- endLocation
- fName
- finalReason
- headingList
- increaseFeeFormKey
- isDriversTokensSend
- isFirstWaypoint
- isInUniversity
- isSaaSRequest
- kmInDegree
- lName
- latDest
- latestPosition
- lngDest
- lowPerf
- messagesFormKey
- originCoords
- pLower
- passengerLocationStringUnvirsity
- previousLocationOfDrivers
- progressTimerRideBeginVip
- qLower
- rLat1
- rLat2
- ram
- rideData
- sdk
- selectedPassengerCount
- startLng
- startLocation
- stringElapsedTimeRideBegin
- tax
- totalPassengerBalashDiscount
- totalPassengerComfortDiscount
- totalPassengerElectricDiscount
- totalPassengerLadyDiscount
- totalPassengerRaihGaiDiscount
- totalPassengerSpeedDiscount
Total missing variables: 59

View File

@@ -0,0 +1,15 @@
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,
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,809 @@
import 'dart:async';
import 'dart:math' show cos, max, min, pi, pow, sqrt;
import 'dart:typed_data';
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:image/image.dart' as img;
import '../../../constant/colors.dart';
// contains global 'box'
import '../../../print.dart';
import '../../../views/home/map_widget.dart/cancel_raide_page.dart';
import 'location_search_controller.dart';
import 'nearby_drivers_controller.dart';
import 'ride_lifecycle_controller.dart';
import '../points_for_rider_controller.dart';
import '../../../constant/univeries_polygon.dart';
class MapEngineController extends GetxController {
IntaleqMapController? mapController;
bool isStyleLoaded = false;
bool isIconsLoaded = false;
Set<Marker> markers = {};
Set<Polyline> polyLines = {};
List<LatLng> polylineCoordinates = [];
Set<Polygon> polygons = {};
Set<Circle> circles = {};
LatLngBounds? lastComputedBounds;
bool mapType = false;
bool mapTrafficON = false;
bool isMarkersShown = false;
String markerIcon = "marker_icon";
String tripIcon = "trip_icon";
String startIcon = "start_icon";
String endIcon = "end_icon";
String carIcon = "car_icon";
String motoIcon = "moto_icon";
String ladyIcon = "lady_icon";
double height = 150;
double heightMenu = 0;
double widthMenu = 0;
double heightPickerContainer = 90;
double heightPointsPageForRider = 0;
double mainBottomMenuMapHeight = Get.height * .2;
double wayPointSheetHeight = 0;
bool heightMenuBool = false;
bool isPickerShown = false;
bool isPointsPageForRider = false;
bool isBottomSheetShown = false;
bool reloadStartApp = false;
bool isCancelRidePageShown = false;
bool isCashConfirmPageShown = false;
bool isPaymentMethodPageShown = false;
bool isRideFinished = false;
bool rideConfirm = false;
bool isMainBottomMenuMap = true;
bool isWayPointSheet = false;
bool isWayPointStopsSheet = false;
bool isWayPointStopsSheetUtilGetMap = false;
double heightBottomSheetShown = 0;
double cashConfirmPageShown = 250;
double widthMapTypeAndTraffic = 50;
double paymentPageShown = Get.height * .6;
bool isAnotherOreder = false;
bool isWhatsAppOrder = false;
Map<String, Timer> _animationTimers = {};
final int updateIntervalMs = 100;
final double minMovementThreshold = 1.0;
void onMapCreated(IntaleqMapController controller) {
mapController = controller;
update();
}
void onStyleLoaded() async {
Log.print('🗺️ Intaleq Map Style Loaded. Initializing...');
isStyleLoaded = true;
await _loadMapIcons();
final locationSearch = Get.find<LocationSearchController>();
Get.find<RideLifecycleController>().reinit();
if (mapController != null) {
if (markers.isNotEmpty && lastComputedBounds != null) {
await _safeAnimateCameraBounds(lastComputedBounds);
} else {
mapController!.animateCamera(
CameraUpdate.newLatLng(locationSearch.passengerLocation),
);
}
}
update();
}
Future<void> _safeAnimateCameraBounds(LatLngBounds? bounds,
{double left = 60,
double top = 60,
double right = 60,
double bottom = 60}) async {
if (bounds == null || mapController == null) return;
try {
if (bounds.northeast.latitude == bounds.southwest.latitude &&
bounds.northeast.longitude == bounds.southwest.longitude) {
Log.print(
'⚠️ _safeAnimateCameraBounds: Bounds are a single point, zooming to point instead.');
await mapController
?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 15));
return;
}
await Future.delayed(const Duration(milliseconds: 200));
await mapController?.animateCamera(
CameraUpdate.newLatLngBounds(
bounds,
left: left,
top: top,
right: right,
bottom: bottom,
),
);
} catch (e) {
Log.print('❌ _safeAnimateCameraBounds CRASH PREVENTED: $e');
try {
await mapController
?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 14));
} catch (_) {}
}
}
Future<void> _loadMapIcons() async {
isIconsLoaded = false;
for (int i = 0; i < 15; i++) {
if (mapController != null && isStyleLoaded) break;
await Future.delayed(const Duration(milliseconds: 200));
}
if (mapController == null || !isStyleLoaded) {
Log.print(
'⚠️ _loadMapIcons: mapController or style not ready. Icons may not load.');
}
await _addMapImage(startIcon, 'assets/images/A.png');
await _addMapImage(endIcon, 'assets/images/b.png');
await _addMapImage(carIcon, 'assets/images/car.png');
await _addMapImage(motoIcon, 'assets/images/moto.png');
await _addMapImage(ladyIcon, 'assets/images/lady.png');
await _addMapImage('picker_icon', 'assets/images/picker.png');
await _addMapImage('orange_marker', 'assets/images/moto1.png');
await _addMapImage('violet_marker', 'assets/images/lady1.png');
isIconsLoaded = true;
markers = markers.map((m) => m.copyWith()).toSet();
update();
if (Get.isRegistered<NearbyDriversController>()) {
Get.find<NearbyDriversController>()
.getCarsLocationByPassengerAndReloadMarker();
}
}
Future<void> _addMapImage(String id, String path) async {
try {
final ByteData bytes = await rootBundle.load(path);
final size = _getImageSize(id);
if (size != null && (id == carIcon || id == motoIcon || id == ladyIcon)) {
final resized = await _resizeImage(bytes.buffer.asUint8List(), size);
await mapController?.addImage(id, resized);
Log.print(
'delimited: successfully added resized map image: $id (${size}x${size})');
} else {
await mapController?.addImage(id, bytes.buffer.asUint8List());
Log.print('delimited: successfully added map image: $id');
}
} catch (e) {
Log.print('❌ Error loading map icon $id: $e');
}
}
int? _getImageSize(String id) {
if (id == carIcon || id == motoIcon || id == ladyIcon) return 120;
return null;
}
Future<Uint8List> _resizeImage(Uint8List bytes, int size) async {
return await compute((Uint8List data) {
final image = img.decodeImage(data);
if (image == null) return data;
final resized = img.copyResize(image, width: size, height: size);
return Uint8List.fromList(img.encodePng(resized));
}, bytes);
}
void clearPolyline() {
polyLines.clear();
update();
}
LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
const double earthRadius = 6378137.0;
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;
minLat = max(-90.0, minLat);
maxLat = min(90.0, maxLat);
minLng = (minLng + 180) % 360 - 180;
maxLng = (maxLng + 180) % 360 - 180;
if (minLng > maxLng) {
double temp = minLng;
minLng = maxLng;
maxLng = temp;
}
return LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
);
}
Future<void> playRouteAnimation(
List<LatLng> coords, LatLngBounds? bounds) async {
const List<Color> segmentColors = [
Color(0xFF109642), // Green
Color(0xFFF59E0B), // Amber
Color(0xFF7C3AED), // Purple
Color(0xFFEF4444), // Red
];
Set<Polyline> newPolylines = {};
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.activeMenuWaypointCount > 0) {
List<int> splitIndices = [];
for (int w = 0; w < locationSearch.activeMenuWaypointCount; w++) {
final wp = locationSearch.menuWaypoints[w];
if (wp == null) continue;
int bestIdx = 0;
double bestDist = double.infinity;
for (int j = 0; j < coords.length; j++) {
final dx = coords[j].latitude - wp.latitude;
final dy = coords[j].longitude - wp.longitude;
final d = dx * dx + dy * dy;
if (d < bestDist) {
bestDist = d;
bestIdx = j;
}
}
splitIndices.add(bestIdx);
}
splitIndices.sort();
List<int> boundaries = [0, ...splitIndices, coords.length - 1];
for (int s = 0; s < boundaries.length - 1; s++) {
int from = boundaries[s];
int to = boundaries[s + 1] + 1;
if (to > coords.length) to = coords.length;
if (from >= to - 1) continue;
final segCoords = coords.sublist(from, to);
if (segCoords.length < 2) continue;
final color = segmentColors[s % segmentColors.length];
newPolylines.add(Polyline(
polylineId: PolylineId('segment_$s'),
points: segCoords,
color: color,
width: 6,
));
}
} else {
newPolylines.add(Polyline(
polylineId: const PolylineId('route_primary'),
points: coords,
color: AppColor.primaryColor,
width: 6,
));
}
polyLines = newPolylines;
update();
Log.print(
'🗺️ Drawing ${markers.length} markers + ${polyLines.length} polylines on map');
if (bounds != null) {
await _safeAnimateCameraBounds(bounds);
}
}
void _fitCameraToPoints(LatLng p1, LatLng p2) async {
if (mapController == null) return;
if (p1.latitude == p2.latitude && p1.longitude == p2.longitude) {
try {
mapController?.animateCamera(CameraUpdate.newLatLngZoom(p1, 17));
} catch (e) {
Log.print("Error animating to single point: $e");
}
return;
}
double minLat = min(p1.latitude, p2.latitude);
double maxLat = max(p1.latitude, p2.latitude);
double minLng = min(p1.longitude, p2.longitude);
double maxLng = max(p1.longitude, p2.longitude);
if ((maxLat - minLat).abs() < 0.002 && (maxLng - minLng).abs() < 0.002) {
try {
mapController?.animateCamera(CameraUpdate.newLatLngZoom(p1, 16));
} catch (e) {
Log.print("Error animating to single point: $e");
}
return;
}
double padding = 50.0;
try {
await mapController?.animateCamera(
CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
),
left: padding,
top: padding,
right: padding,
bottom: padding,
),
);
} catch (e) {
Log.print("Error animating bounds: $e");
try {
LatLng center = LatLng((minLat + maxLat) / 2, (minLng + maxLng) / 2);
mapController?.animateCamera(CameraUpdate.newLatLngZoom(center, 14));
} catch (_) {}
}
}
void fitCameraToPoints(LatLng p1, LatLng p2) {
_fitCameraToPoints(p1, p2);
}
void clearMarkersExceptStartEndAndDriver() {
const String currentDriverMarkerId = 'assigned_driver_marker';
markers.removeWhere((marker) {
String id = marker.markerId.value;
if (id == 'start') return false;
if (id == 'end') return false;
if (id == currentDriverMarkerId) return false;
return true;
});
update();
}
void clearMarkersExceptStartEnd() {
markers.removeWhere((marker) {
String id = marker.markerId.value;
return id != 'start' && id != 'end';
});
update();
}
void _updateMarkerPosition(
LatLng newPosition, double newHeading, String icon) {
const String markerId = 'driverToPassengers';
final mId = MarkerId(markerId);
final existingMarker = markers.cast<Marker?>().firstWhere(
(m) => m?.markerId == mId,
orElse: () => null,
);
if (existingMarker != null) {
_smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
} else {
markers = {
...markers,
Marker(
markerId: mId,
position: newPosition,
rotation: newHeading,
icon: InlqBitmap.fromStyleImage(icon),
anchor: const Offset(0.5, 0.5),
),
};
update();
}
mapController?.animateCamera(CameraUpdate.newLatLng(newPosition));
}
void updateMarkerPosition(
LatLng newPosition, double newHeading, String icon) {
_updateMarkerPosition(newPosition, newHeading, icon);
}
void _smoothlyUpdateMarker(
Marker oldMarker, LatLng newPosition, double newHeading, String icon) {
double distance = Geolocator.distanceBetween(
oldMarker.position.latitude,
oldMarker.position.longitude,
newPosition.latitude,
newPosition.longitude);
if (distance < 2.0) return;
final MarkerId markerIdKey = oldMarker.markerId;
_animationTimers[markerIdKey.value]?.cancel();
int ticks = 0;
const int totalSteps = 20;
const int stepDuration = 50;
double latStep =
(newPosition.latitude - oldMarker.position.latitude) / totalSteps;
double lngStep =
(newPosition.longitude - oldMarker.position.longitude) / totalSteps;
double headingStep = (newHeading - oldMarker.rotation) / totalSteps;
LatLng currentPos = oldMarker.position;
double currentHeading = oldMarker.rotation;
_animationTimers[markerIdKey.value] =
Timer.periodic(const Duration(milliseconds: stepDuration), (timer) {
ticks++;
currentPos =
LatLng(currentPos.latitude + latStep, currentPos.longitude + lngStep);
currentHeading += headingStep;
final updatedMarker = oldMarker.copyWith(
position: currentPos,
rotation: currentHeading,
icon: InlqBitmap.fromStyleImage(icon),
);
markers = {
...markers.where((m) => m.markerId != markerIdKey),
updatedMarker,
};
if (mapController != null) {
mapController!.animateCamera(CameraUpdate.newLatLng(currentPos));
}
update();
if (ticks >= totalSteps) {
timer.cancel();
_animationTimers.remove(markerIdKey.value);
}
});
}
// تحديث موقع العلامة (Marker) واتجاهها بسلاسة على الخريطة.
// تحسب الدالة المسافة بين الموقع الحالي والجديد؛ وإذا كانت أكبر من مترين،
// تقوم بتقسيم الحركة والدوران إلى 20 خطوة متباعدة بـ 50 مللي ثانية (إجمالي ثانية واحدة).
// يتم تحديث موضع العلامة وتحريك الكاميرا تدريجياً لتبدو حركة السيارة انسيابية.
void smoothlyUpdateMarker(
Marker oldMarker, LatLng newPosition, double newHeading, String icon) {
_smoothlyUpdateMarker(oldMarker, newPosition, newHeading, icon);
}
void changeBottomSheetShown({bool? forceValue}) {
if (forceValue != null) {
isBottomSheetShown = forceValue;
} else {
isBottomSheetShown = !isBottomSheetShown;
}
heightBottomSheetShown = isBottomSheetShown == true ? 250 : 0;
update();
}
void changeCashConfirmPageShown() {
isCashConfirmPageShown = !isCashConfirmPageShown;
final rideLife = Get.find<RideLifecycleController>();
rideLife.isCashSelectedBeforeConfirmRide = true;
cashConfirmPageShown = isCashConfirmPageShown == true ? 250 : 0;
update();
rideLife.update();
}
void changePaymentMethodPageShown() {
isPaymentMethodPageShown = !isPaymentMethodPageShown;
paymentPageShown = isPaymentMethodPageShown == true ? Get.height * .6 : 0;
update();
}
void changeMapType() {
mapType = !mapType;
update();
}
void changeMapTraffic() {
mapTrafficON = !mapTrafficON;
update();
}
void changeisAnotherOreder(bool val) {
isAnotherOreder = val;
update();
}
void changeIsWhatsAppOrder(bool val) {
isWhatsAppOrder = val;
update();
}
void changeCancelRidePageShow() {
showCancelRideBottomSheet();
isCancelRidePageShown = !isCancelRidePageShown;
update();
if (Get.isRegistered<RideLifecycleController>()) {
Get.find<RideLifecycleController>().update();
}
}
void getDrawerMenu() {
heightMenuBool = !heightMenuBool;
widthMapTypeAndTraffic = heightMenuBool == true ? 0 : 50;
heightMenu = heightMenuBool == true ? 80 : 0;
widthMenu = heightMenuBool == true ? 110 : 0;
update();
}
void changeMainBottomMenuMap() {
if (isWayPointStopsSheetUtilGetMap == true) {
changeWayPointSheet();
} else {
isMainBottomMenuMap = !isMainBottomMenuMap;
mainBottomMenuMapHeight =
isMainBottomMenuMap == true ? Get.height * .22 : Get.height * .6;
isWayPointSheet = false;
if (heightMenuBool == true) {
getDrawerMenu();
}
Get.find<RideLifecycleController>().initilizeGetStorage();
update();
}
}
void downPoints() {
if (Get.find<WayPointController>().wayPoints.length < 2) {
isWayPointStopsSheetUtilGetMap = false;
isWayPointSheet = false;
wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
update();
}
update();
}
void changeWayPointSheet() {
isWayPointSheet = !isWayPointSheet;
wayPointSheetHeight = isWayPointSheet == false ? 0 : Get.height * .45;
update();
}
void changeWayPointStopsSheet() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.wayPointIndex > -1) {
isWayPointStopsSheet = true;
isWayPointStopsSheetUtilGetMap = true;
}
isWayPointStopsSheet = !isWayPointStopsSheet;
wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0;
update();
}
void changeHeightPlaces() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.placesDestination.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightStartPlaces() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.placesStart.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightPlacesAll(int index) {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.placeListResponseAll[index].isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightPlaces1() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.wayPoint1.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightPlaces2() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.wayPoint2.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightPlaces3() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.wayPoint3.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void changeHeightPlaces4() {
final locationSearch = Get.find<LocationSearchController>();
if (locationSearch.wayPoint4.isEmpty) {
height = 0;
update();
} else {
height = 150;
update();
}
}
void hidePlaces() {
height = 0;
update();
}
void changePickerShown() {
isPickerShown = !isPickerShown;
heightPickerContainer = isPickerShown == true ? 150 : 90;
update();
}
void _initializePolygons() {
List<List<LatLng>> universityPolygons =
UniversitiesPolygons.universityPolygons;
for (int i = 0; i < universityPolygons.length; i++) {
Polygon polygon = Polygon(
polygonId: PolygonId('univ_$i'),
points: universityPolygons[i],
fillColor: Colors.blueAccent.withOpacity(0.2),
strokeColor: Colors.blueAccent,
strokeWidth: 2,
);
polygons.add(polygon);
}
update();
}
void _applyLowEndModeIfNeeded() {
// Placeholder comment from original
}
Future<void> _initMinimalIcons() async {
// Icons are loaded dynamically
}
Future<void> _playRouteAnimation(
List<LatLng> coords, LatLngBounds? bounds) async {
const List<Color> segmentColors = [
Color(0xFF109642), // Green
Color(0xFFF59E0B), // Amber
Color(0xFF7C3AED), // Purple
Color(0xFFEF4444), // Red
];
Set<Polyline> newPolylines = {};
final locSearch = Get.find<LocationSearchController>();
if (locSearch.activeMenuWaypointCount > 0) {
List<int> splitIndices = [];
for (int w = 0; w < locSearch.activeMenuWaypointCount; w++) {
final wp = locSearch.menuWaypoints[w];
if (wp == null) continue;
int bestIdx = 0;
double bestDist = double.infinity;
for (int j = 0; j < coords.length; j++) {
final dx = coords[j].latitude - wp.latitude;
final dy = coords[j].longitude - wp.longitude;
final d = dx * dx + dy * dy;
if (d < bestDist) {
bestDist = d;
bestIdx = j;
}
}
splitIndices.add(bestIdx);
}
splitIndices.sort();
List<int> boundaries = [0, ...splitIndices, coords.length - 1];
for (int s = 0; s < boundaries.length - 1; s++) {
int from = boundaries[s];
int to = boundaries[s + 1] + 1;
if (to > coords.length) to = coords.length;
if (from >= to - 1) continue;
final segCoords = coords.sublist(from, to);
if (segCoords.length < 2) continue;
final color = segmentColors[s % segmentColors.length];
newPolylines.add(Polyline(
polylineId: PolylineId('segment_$s'),
points: segCoords,
color: color,
width: 6,
));
}
} else {
newPolylines.add(Polyline(
polylineId: const PolylineId('route_primary'),
points: coords,
color: AppColor.primaryColor,
width: 6,
));
}
polyLines = newPolylines;
update();
Log.print(
'🗺️ Drawing ${markers.length} markers + ${polyLines.length} polylines on map');
update();
if (bounds != null) {
await _safeAnimateCameraBounds(bounds);
}
}
void reset() {
isPickerShown = false;
isPointsPageForRider = false;
isBottomSheetShown = false;
isCancelRidePageShown = false;
isCashConfirmPageShown = false;
isPaymentMethodPageShown = false;
isRideFinished = false;
rideConfirm = false;
isMainBottomMenuMap = true;
isWayPointSheet = false;
isWayPointStopsSheet = false;
isWayPointStopsSheetUtilGetMap = false;
heightBottomSheetShown = 0;
mainBottomMenuMapHeight = Get.height * 0.22;
wayPointSheetHeight = 0;
markers.clear();
polyLines.clear();
polylineCoordinates.clear();
_animationTimers.forEach((key, timer) => timer.cancel());
_animationTimers.clear();
update();
}
@override
void onClose() {
_animationTimers.forEach((key, timer) => timer.cancel());
_animationTimers.clear();
mapController = null;
super.onClose();
}
}

View File

@@ -0,0 +1,25 @@
import 'package:get/get.dart';
import 'map_socket_controller.dart';
import 'map_engine_controller.dart';
import 'location_search_controller.dart';
import 'nearby_drivers_controller.dart';
import 'ride_lifecycle_controller.dart';
import 'ui_interactions_controller.dart';
class MapScreenBinding extends Bindings {
@override
void dependencies() {
// 1. WebSocket Controller: Permanent and immediate
Get.put(MapSocketController());
// 2. Core Controllers (initialized when the screen opens or on demand)
Get.lazyPut(() => MapEngineController());
Get.lazyPut(() => LocationSearchController());
Get.lazyPut(() => NearbyDriversController());
// 3. Lifecycle and UI Interaction Controllers
Get.lazyPut(() => RideLifecycleController());
Get.lazyPut(() => UiInteractionsController(), fenix: true);
}
}

View File

@@ -0,0 +1,326 @@
import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:socket_io_client/socket_io_client.dart' as io_client;
import 'package:intaleq_maps/intaleq_maps.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../main.dart'; // contains global 'box'
import '../../../print.dart';
import 'ride_lifecycle_controller.dart';
import 'nearby_drivers_controller.dart';
import 'map_engine_controller.dart';
class MapSocketController extends GetxController {
late io_client.Socket socket;
bool isSocketConnected = false;
bool _isSocketInitialized = false;
Timer? _heartbeatTimer;
DateTime? _lastSocketLocationTime;
int _socketLocationUpdatesCount = 0;
Timer? _watchdogTimer;
DateTime? get lastDriverLocationTime => _lastSocketLocationTime;
int get socketLocationUpdatesCount => _socketLocationUpdatesCount;
void initConnectionWithSocket() {
if (isSocketConnected) return;
String passengerId = box.read(BoxName.passengerID).toString();
Log.print("🔌 Initializing Socket for Passenger: $passengerId");
socket = io_client.io(
AppLink.serverSocket,
io_client.OptionBuilder()
.setTransports(['websocket'])
.disableAutoConnect()
.setQuery({'id': passengerId})
.setReconnectionAttempts(20)
.setReconnectionDelay(2000)
.setReconnectionDelayMax(10000)
.enableReconnection()
.setTimeout(20000)
.setExtraHeaders({'Connection': 'Upgrade'})
.build(),
);
_isSocketInitialized = true;
socket.connect();
socket.onConnect((_) {
Log.print("✅ Socket Connected Successfully");
isSocketConnected = true;
_startHeartbeat();
final rideLifecycle = Get.find<RideLifecycleController>();
if (rideLifecycle.rideId != 'yet' && rideLifecycle.driverId.isNotEmpty) {
socket.emit('subscribe_driver_location', {
'ride_id': rideLifecycle.rideId,
'driver_id': rideLifecycle.driverId,
});
Log.print("📡 Re-subscribed to driver location after connect");
}
update();
});
socket.onDisconnect((_) {
Log.print("⚠️ Socket Disconnected — Auto-Reconnect will handle it");
isSocketConnected = false;
final rideLifecycle = Get.find<RideLifecycleController>();
if (rideLifecycle.isActiveRideState()) {
Log.print("🔄 Enabling Fast Polling Fallback (4s) until reconnect...");
rideLifecycle.startMasterTimerWithInterval(4);
}
update();
});
socket.onReconnect((_) {
Log.print("🔁 Socket Reconnected Successfully!");
isSocketConnected = true;
_startHeartbeat();
final rideLifecycle = Get.find<RideLifecycleController>();
if (rideLifecycle.rideId != 'yet' && rideLifecycle.driverId.isNotEmpty) {
socket.emit('subscribe_driver_location', {
'ride_id': rideLifecycle.rideId,
'driver_id': rideLifecycle.driverId,
});
Log.print("📡 Re-subscribed to driver location after reconnect");
}
if (rideLifecycle.isActiveRideState()) {
Log.print("✅ Socket back online — stopping Fast Polling Fallback");
rideLifecycle.cancelMasterTimer();
}
update();
});
socket.onReconnectAttempt((attemptNumber) {
Log.print("🔄 Socket Reconnect Attempt #$attemptNumber...");
});
socket.onError((error) {
Log.print("❌ Socket Error: $error");
isSocketConnected = false;
});
socket.on('connect_error', (error) {
Log.print("❌ Socket Connect Error: $error");
isSocketConnected = false;
// في الإصدار 1.0.2 أحياناً auto-reconnect لا يعمل بعد connect_error
// نتأكد يدوياً من إعادة الاتصال
Future.delayed(const Duration(seconds: 3), () {
if (!isSocketConnected && _isSocketInitialized) {
Log.print("🔄 Manual reconnect after connect_error...");
try {
socket.connect();
} catch (e) {
Log.print("Manual reconnect error: $e");
}
}
});
});
socket.on('ride_status_change', (data) {
Log.print("📩 Socket Event: ride_status_change -> $data");
_handleRideStatusChangeWithSocket(data);
});
socket.on('driver_location_update', (data) {
handleDriverLocationUpdate(data);
});
}
void _startHeartbeat() {
_heartbeatTimer?.cancel();
_heartbeatTimer = Timer.periodic(const Duration(seconds: 15), (timer) {
if (isSocketConnected && socket.connected) {
socket.emit('heartbeat',
{'passenger_id': box.read(BoxName.passengerID).toString()});
}
});
}
bool isSocketHealthy() {
if (!isSocketConnected) return false;
if (_lastSocketLocationTime == null) return false;
final diff = DateTime.now().difference(_lastSocketLocationTime!).inSeconds;
return diff < 20;
}
void _handleRideStatusChangeWithSocket(dynamic data) {
if (data == null || data['status'] == null) return;
String newStatus = data['status'].toString().toLowerCase();
Log.print("🔔 Socket Status Update: $newStatus");
final rideLifecycle = Get.find<RideLifecycleController>();
Map<String, dynamic>? driverInfo;
if (data['driver_info'] != null && data['driver_info'] is Map) {
driverInfo = Map<String, dynamic>.from(data['driver_info']);
}
switch (newStatus) {
case 'accepted':
case 'apply':
case 'applied':
rideLifecycle.processRideAcceptance(
driverData: driverInfo, source: "Socket");
break;
case 'arrived':
rideLifecycle.processDriverArrival("Socket");
break;
case 'started':
case 'begin':
rideLifecycle.processRideBegin(source: "Socket");
break;
case 'finished':
case 'ended':
_onRideFinishedWithSocket(data);
break;
case 'cancelled':
rideLifecycle.processRideCancelledByDriver(data, source: "Socket");
break;
case 'no_drivers_found':
rideLifecycle.showNoDriverDialog();
break;
}
}
void _onRideFinishedWithSocket(dynamic data) {
Log.print("🏁 Ride Finished (Socket)");
final rideLifecycle = Get.find<RideLifecycleController>();
var rawList = data['DriverList'];
List<dynamic> listToSend = [];
if (rawList != null) {
if (rawList is List) {
listToSend = rawList;
} else if (rawList is String) {
try {
listToSend = jsonDecode(rawList);
} catch (e) {
Log.print("Error decoding DriverList: $e");
}
}
}
if (listToSend.isEmpty && data['price'] != null) {
listToSend = [
rideLifecycle.driverId,
rideLifecycle.rideId,
rideLifecycle.driverToken,
data['price'].toString()
];
}
rideLifecycle.processRideFinished(listToSend, source: "Socket");
}
void handleDriverLocationUpdate(dynamic data) {
if (!isSocketConnected || data == null) return;
_lastSocketLocationTime = DateTime.now();
_socketLocationUpdatesCount++;
final rideLifecycle = Get.find<RideLifecycleController>();
if (rideLifecycle.driverId.isEmpty &&
(data['driver_id'] ?? data['driverId']) != null) {
rideLifecycle.driverId =
(data['driver_id'] ?? data['driverId']).toString();
}
if (_socketLocationUpdatesCount >= 3 &&
rideLifecycle.locationPollingTimer != null) {
Log.print("✅ Socket delivering locations reliably. Stopping polling.");
rideLifecycle.stopDriverLocationPolling();
}
try {
double lat = double.tryParse(
(data['latitude'] ?? data['lat'])?.toString() ?? '0') ??
0;
double lng = double.tryParse(
(data['longitude'] ?? data['lng'])?.toString() ?? '0') ??
0;
double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0;
if (lat == 0 || lng == 0) return;
LatLng newPos = LatLng(lat, lng);
final nearbyDrivers = Get.find<NearbyDriversController>();
if (nearbyDrivers.driverCarsLocationToPassengerAfterApplied.isEmpty) {
nearbyDrivers.driverCarsLocationToPassengerAfterApplied.add(newPos);
} else {
nearbyDrivers.driverCarsLocationToPassengerAfterApplied[0] = newPos;
}
double speed = double.tryParse(data['speed']?.toString() ?? '0') ?? 0;
rideLifecycle.checkAndRecalculateIfDeviated(
newPos,
heading: heading,
speed: speed,
);
final mapEngine = Get.find<MapEngineController>();
if (mapEngine.mapController != null) {
double zoom = 16.5;
if (speed > 0) {
zoom = 17.0 - ((speed - 10) / 70) * 2.5;
zoom = zoom.clamp(14.5, 17.0);
}
mapEngine.mapController!
.animateCamera(CameraUpdate.newLatLngZoom(newPos, zoom));
}
final dynamic distanceValue =
data['distance_m'] ?? data['distance_meters'] ?? data['distance'];
final double? distanceMeters =
double.tryParse(distanceValue?.toString() ?? '');
final int? etaSeconds = data['eta_seconds'] == null
? null
: int.tryParse(data['eta_seconds'].toString());
final bool hasServerMetrics = (etaSeconds != null && etaSeconds > 0) ||
(distanceMeters != null && distanceMeters > 0);
if (hasServerMetrics) {
rideLifecycle.updateDriverRouteMetrics(
etaSeconds: etaSeconds != null && etaSeconds > 0 ? etaSeconds : null,
distanceMeters: distanceMeters,
);
}
rideLifecycle.updateDriverMarker(newPos, heading);
rideLifecycle.updateRemainingRoute(newPos, updateEta: !hasServerMetrics);
rideLifecycle.update();
} catch (e) {
Log.print('Error in handleDriverLocationUpdate: $e');
}
}
void disposeRideSocket() {
_heartbeatTimer?.cancel();
_watchdogTimer?.cancel();
if (_isSocketInitialized) {
socket.disconnect();
socket.dispose();
isSocketConnected = false;
_isSocketInitialized = false;
Log.print("🔌 Socket Disposed");
}
}
@override
void onClose() {
disposeRideSocket();
super.onClose();
}
}

View File

@@ -0,0 +1,475 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math' show Random, atan2, cos, pi, pow, sin, sqrt;
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import '../../../constant/links.dart';
import '../../../constant/api_key.dart';
import '../../../print.dart';
import '../../functions/crud.dart';
import 'map_engine_controller.dart';
import 'location_search_controller.dart';
import 'ride_lifecycle_controller.dart';
import '../../../models/model/locations.dart';
import 'car_location.dart';
import 'package:device_info_plus/device_info_plus.dart';
class NearbyDriversController extends GetxController {
List carsLocationByPassenger = [];
List<LatLng> driverCarsLocationToPassengerAfterApplied = [];
List<CarLocationModel> carLocationsModels = [];
String? currentDriverMarkerId;
bool lowPerf = false;
dynamic dataCarsLocationByPassenger;
bool noCarString = false;
final double minMovementThreshold = 2.0;
final Map<String, Timer> _animationTimers = {};
final List<Map<String, dynamic>> fakeCarData = [];
Future<bool> getCarsLocationByPassengerAndReloadMarker() async {
carsLocationByPassenger = [];
final locSearch = Get.find<LocationSearchController>();
if (locSearch.passengerLocation.latitude == 0 && locSearch.passengerLocation.longitude == 0) {
return false;
}
var res = await CRUD().get(
link: AppLink.getCarsLocationByPassenger,
payload: {
'lat': locSearch.passengerLocation.latitude.toString(),
'lng': locSearch.passengerLocation.longitude.toString(),
'radius': '5',
'limit': '50',
},
);
if (res == 'failure') {
noCarString = true;
dataCarsLocationByPassenger = 'failure';
update();
return false;
}
noCarString = false;
var responseData = jsonDecode(res);
dataCarsLocationByPassenger = responseData;
List driversList = [];
if (responseData['status'] == true && responseData['data'] != null) {
driversList = responseData['data'];
} else if (responseData['message'] != null) {
driversList = responseData['message'];
}
final mapEngine = Get.find<MapEngineController>();
if (driversList.isEmpty) {
carsLocationByPassenger.clear();
mapEngine.update();
return false;
}
carsLocationByPassenger.clear();
for (var i = 0; i < driversList.length; i++) {
var carData = driversList[i];
double lat = double.tryParse(carData['latitude'].toString()) ?? 0.0;
double lng = double.tryParse(carData['longitude'].toString()) ?? 0.0;
double heading = double.tryParse(carData['heading'].toString()) ?? 0.0;
if (lat == 0.0 || lng == 0.0) continue;
String driverId = (carData['driver_id'] ?? carData['id'] ?? '').toString();
if (driverId.isEmpty || driverId == 'null') continue;
_updateOrCreateMarker(
driverId,
LatLng(lat, lng),
heading,
_getIconForCar(carData),
);
}
mapEngine.update();
return true;
}
void _addFakeCarMarkers(LatLng center, int count) {
if (fakeCarData.isEmpty) {
Random random = Random();
double radiusInKm = 2.5;
for (int i = 0; i < count; i++) {
double angle = random.nextDouble() * 2 * pi;
double distance = sqrt(random.nextDouble()) * radiusInKm;
double latOffset = (distance / 111.32);
double lonOffset =
(distance / (111.32 * cos(center.latitude * pi / 180.0)));
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',
});
}
}
for (var carData in fakeCarData) {
_updateOrCreateMarker(
carData['id'].toString(),
LatLng(carData['latitude'], carData['longitude']),
carData['heading'],
_getIconForCar(carData),
);
}
}
void addFakeCarMarkers(LatLng center, int count) {
_addFakeCarMarkers(center, count);
}
Future<CarLocation?> getNearestDriverByPassengerLocation() async {
final rideLife = Get.find<RideLifecycleController>();
final locSearch = Get.find<LocationSearchController>();
if (!rideLife.rideConfirm) {
if (dataCarsLocationByPassenger != 'failure' &&
dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('message') &&
dataCarsLocationByPassenger['message'] != null &&
dataCarsLocationByPassenger['message'].length > 0) {
double nearestDistance = double.infinity;
CarLocation? nearestCar;
for (var i = 0;
i < dataCarsLocationByPassenger['message'].length;
i++) {
var carLocation = dataCarsLocationByPassenger['message'][i];
try {
final distance = Geolocator.distanceBetween(
locSearch.passengerLocation.latitude,
locSearch.passengerLocation.longitude,
double.parse(carLocation['latitude']),
double.parse(carLocation['longitude']),
);
int durationToPassenger = (distance / 1000 / 25 * 3600).round();
update();
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();
}
} catch (e) {
Log.print('Error calculating distance/duration: $e');
}
}
return nearestCar;
}
}
return null;
}
Future<CarLocation?> getNearestDriverByPassengerLocationAPIGOOGLE() async {
final rideLife = Get.find<RideLifecycleController>();
final mapEngine = Get.find<MapEngineController>();
final locSearch = Get.find<LocationSearchController>();
if (mapEngine.polyLines.isEmpty || rideLife.totalCostPassenger == 0) {
return null;
}
if (!rideLife.rideConfirm) {
double nearestDistance = double.infinity;
if (dataCarsLocationByPassenger != 'failure' &&
dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('message') &&
dataCarsLocationByPassenger['message'] != null) {
if (dataCarsLocationByPassenger['message'].length > 0) {
CarLocation? nearestCar;
for (var i = 0;
i < dataCarsLocationByPassenger['message'].length;
i++) {
var carLocation = dataCarsLocationByPassenger['message'][i];
update();
String apiUrl =
'${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}&units=metric&key=${AK.mapAPIKEY}';
var response = await CRUD().getGoogleApi(link: apiUrl, payload: {});
if (response != null && response['status'] == "OK") {
var data = response;
int distance1 =
data['rows'][0]['elements'][0]['distance']['value'];
rideLife.distanceByPassenger =
data['rows'][0]['elements'][0]['distance']['text'];
rideLife.durationToPassenger =
data['rows'][0]['elements'][0]['duration']['value'];
Duration durationFromDriverToPassenger =
Duration(seconds: rideLife.durationToPassenger.toInt());
rideLife.stringRemainingTimeToPassenger =
data['rows'][0]['elements'][0]['duration']['text'];
update();
if (distance1 < nearestDistance) {
nearestDistance = distance1.toDouble();
nearestCar = CarLocation(
distance: distance1.toDouble(),
duration: rideLife.durationToPassenger.toDouble(),
id: carLocation['driver_id'],
latitude: double.parse(carLocation['latitude']),
longitude: double.parse(carLocation['longitude']),
);
update();
}
} else {
Log.print('${response?['status']}: error Google distance matrix');
}
}
return nearestCar;
}
}
}
return null;
}
Future getCarForFirstConfirm(String carType) async {
bool foundCars = false;
int attempt = 0;
Timer.periodic(const Duration(seconds: 4), (Timer t) async {
foundCars = await getCarsLocationByPassengerAndReloadMarker();
Log.print('foundCars: $foundCars');
if (foundCars) {
t.cancel();
} else if (attempt >= 4) {
t.cancel();
if (!foundCars) {
noCarString = true;
dataCarsLocationByPassenger = 'failure';
}
update();
}
attempt++;
});
}
void startCarLocationSearch(String carType) {
int searchInterval = 5;
Log.print('searchInterval: $searchInterval');
int boundIncreaseStep = 2500;
Log.print('boundIncreaseStep: $boundIncreaseStep');
int maxAttempts = 3;
int maxBoundIncreaseStep = 6000;
int attempt = 0;
Log.print('initial attempt: $attempt');
Timer.periodic(Duration(seconds: searchInterval), (Timer timer) async {
Log.print('Current attempt: $attempt');
bool foundCars = false;
final mapEngine = Get.find<MapEngineController>();
if (attempt >= maxAttempts) {
timer.cancel();
if (foundCars == false) {
noCarString = true;
update();
}
} else if (mapEngine.reloadStartApp == true) {
Log.print('reloadStartApp: ${mapEngine.reloadStartApp}');
foundCars = await getCarsLocationByPassengerAndReloadMarker();
Log.print('foundCars: $foundCars');
if (foundCars) {
timer.cancel();
} else {
attempt++;
Log.print('Incrementing attempt to: $attempt');
if (boundIncreaseStep < maxBoundIncreaseStep) {
boundIncreaseStep += 1500;
if (boundIncreaseStep > maxBoundIncreaseStep) {
boundIncreaseStep = maxBoundIncreaseStep;
}
Log.print('New boundIncreaseStep: $boundIncreaseStep');
}
}
}
});
}
String _getIconForCar(Map<String, dynamic> carData) {
final mapEngine = Get.find<MapEngineController>();
if (carData['model'].toString().contains('دراجة')) {
return mapEngine.motoIcon;
} else if (carData['gender'] == 'Female') {
return mapEngine.ladyIcon;
} else {
return mapEngine.carIcon;
}
}
void _updateOrCreateMarker(
String markerId, LatLng newPosition, double newHeading, String icon) {
final mapEngine = Get.find<MapEngineController>();
if (!mapEngine.isIconsLoaded) {
Log.print("⚠️ Skipping drawing marker $markerId because map icons are not fully loaded yet.");
return;
}
final mId = MarkerId(markerId);
final existingMarker = mapEngine.markers.cast<Marker?>().firstWhere(
(m) => m?.markerId == mId,
orElse: () => null,
);
if (existingMarker == null) {
mapEngine.markers = {
...mapEngine.markers,
Marker(
markerId: mId,
position: newPosition,
rotation: newHeading,
icon: InlqBitmap.fromStyleImage(icon),
anchor: const Offset(0.5, 0.5),
),
};
mapEngine.update();
} else {
double distance = Geolocator.distanceBetween(
existingMarker.position.latitude,
existingMarker.position.longitude,
newPosition.latitude,
newPosition.longitude);
if (distance >= minMovementThreshold) {
_smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon);
}
}
}
void _smoothlyUpdateMarker(
Marker oldMarker, LatLng newPosition, double newHeading, String icon) {
final mapEngine = Get.find<MapEngineController>();
final MarkerId markerIdKey = oldMarker.markerId;
_animationTimers[markerIdKey.value]?.cancel();
int ticks = 0;
const int totalSteps = 20;
const int stepDuration = 50;
double latStep =
(newPosition.latitude - oldMarker.position.latitude) / totalSteps;
double lngStep =
(newPosition.longitude - oldMarker.position.longitude) / totalSteps;
double headingStep = (newHeading - oldMarker.rotation) / totalSteps;
LatLng currentPos = oldMarker.position;
double currentHeading = oldMarker.rotation;
_animationTimers[markerIdKey.value] =
Timer.periodic(const Duration(milliseconds: stepDuration), (timer) {
ticks++;
currentPos =
LatLng(currentPos.latitude + latStep, currentPos.longitude + lngStep);
currentHeading += headingStep;
final updatedMarker = oldMarker.copyWith(
position: currentPos,
rotation: currentHeading,
icon: InlqBitmap.fromStyleImage(icon),
);
mapEngine.markers = {
...mapEngine.markers.where((m) => m.markerId != markerIdKey),
updatedMarker,
};
if (mapEngine.mapController != null) {
mapEngine.mapController!.animateCamera(CameraUpdate.newLatLng(currentPos));
}
mapEngine.update();
if (ticks >= totalSteps) {
timer.cancel();
_animationTimers.remove(markerIdKey.value);
}
});
}
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;
double expectedBearing = calculateBearing(
routePoints[0].latitude,
routePoints[0].longitude,
routePoints[1].latitude,
routePoints[1].longitude,
);
double bearingDifference = (expectedBearing - actualBearing).abs();
if (bearingDifference > 30) {
Log.print("⚠️ السائق انحرف عن المسار!");
}
}
void detectStops(Position currentPosition) {
if (currentPosition.speed < 0.5) {
Log.print("🚦 السائق توقف في موقع غير متوقع!");
}
}
Future<void> detectPerfMode() async {
try {
if (GetPlatform.isAndroid) {
final info = await DeviceInfoPlugin().androidInfo;
final sdk = info.version.sdkInt;
final ram = info.availableRamSize;
lowPerf = (sdk < 28) || (ram > 0 && ram < 3 * 1024 * 1024 * 1024);
} else {
lowPerf = false;
}
} catch (_) {
lowPerf = false;
}
update();
}
@override
void onClose() {
_animationTimers.forEach((key, timer) => timer.cancel());
_animationTimers.clear();
super.onClose();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
enum RideState {
noRide, // لا يوجد رحلة جارية، عرض واجهة البحث
cancelled, // تم إلغاء الرحلة
preCheckReview, // يوجد رحلة منتهية، تحقق من التقييم
searching, // جاري البحث عن كابتن
driverApplied, // تم قبول الطلب
driverArrived, // وصل السائق
inProgress, // الرحلة بدأت بالفعل
finished, // انتهت الرحلة (سيتم تحويلها إلى preCheckReview)
}

View File

@@ -0,0 +1,436 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../constant/info.dart';
import '../../../main.dart'; // contains global 'box'
import '../../../print.dart';
import '../../../services/emergency_signal_service.dart';
import '../../../views/widgets/elevated_btn.dart';
import '../../../views/widgets/mydialoug.dart';
import '../../../views/widgets/my_textField.dart';
import '../../../views/home/map_page_passenger.dart';
import '../../../views/widgets/error_snakbar.dart';
import '../../../models/model/painter_copoun.dart';
import '../../functions/launch.dart';
import '../../firebase/local_notification.dart';
import '../../firebase/notification_service.dart';
import '../../functions/crud.dart';
import '../../functions/tts.dart';
import 'ride_lifecycle_controller.dart';
import 'location_search_controller.dart';
import 'map_engine_controller.dart';
class UiInteractionsController extends GetxController {
TextEditingController sosPhonePassengerProfile = TextEditingController();
TextEditingController whatsAppLocationText = TextEditingController();
final sosFormKey = GlobalKey<FormState>();
@override
void onInit() {
super.onInit();
EmergencySignalService.instance.startListening(() {
final rideLifecycle = Get.find<RideLifecycleController>();
if (rideLifecycle.statusRide == 'Begin' ||
rideLifecycle.statusRide == 'start') {
Log.print("🚨 Emergency shake verified! Prompting SOS...");
sosPassenger();
}
});
}
Future<void> _ensureSosNumber(Function onSuccess) async {
String? storedPhone = box.read(BoxName.sosPhonePassenger);
if (storedPhone != null && storedPhone.isNotEmpty) {
onSuccess();
return;
}
sosPhonePassengerProfile.clear();
Get.defaultDialog(
title: 'Add SOS Phone'.tr,
titleStyle: AppStyle.title,
content: Form(
key: sosFormKey,
child: Column(
children: [
MyTextForm(
controller: sosPhonePassengerProfile,
label: 'insert sos phone'.tr,
hint: 'e.g. 0912345678 (Default +963)'.tr,
type: TextInputType.phone,
),
const SizedBox(height: 10),
Text(
"Note: If no country code is entered, it will be saved as Syrian (+963).".tr,
style: TextStyle(fontSize: 12, color: Colors.grey),
textAlign: TextAlign.center,
),
],
),
),
confirm: MyElevatedButton(
title: 'Save'.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);
onSuccess();
}
},
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () => Get.back(),
kolor: AppColor.redColor,
)
);
}
void sosPassenger() {
_ensureSosNumber(() {
Get.defaultDialog(
barrierDismissible: false,
title: "Emergency SOS".tr,
titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
content: Column(
children: [
Icon(Icons.warning_amber_rounded, size: 50, color: AppColor.redColor),
const SizedBox(height: 10),
Text(
"Do you want to send an emergency message to your SOS contact?".tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
],
),
confirm: MyElevatedButton(
title: "Send SOS".tr,
kolor: AppColor.redColor,
onPressed: () {
Get.back();
_shareTripDetailsSOS();
},
),
cancel: MyElevatedButton(
title: "I'm Safe".tr,
kolor: AppColor.greenColor,
onPressed: () {
Get.back();
},
),
);
});
}
void _shareTripDetailsSOS() {
final rideLifecycle = Get.find<RideLifecycleController>();
final locSearch = Get.find<LocationSearchController>();
String message = "**Emergency SOS from Passenger:**\n";
String origin = locSearch.startNameAddress;
String destination = locSearch.endNameAddress;
message += "* ${'Origin'.tr}: $origin\n";
message += "* ${'Destination'.tr}: $destination\n";
message += "* ${'Driver Name'.tr}: ${rideLifecycle.driverName}\n";
message +=
"* ${'Car'.tr}: ${rideLifecycle.make} - ${rideLifecycle.model} - ${rideLifecycle.licensePlate}\n";
message += "* ${'Phone'.tr}: ${rideLifecycle.driverPhone}\n\n";
message +=
"${'Location'.tr}: https://www.google.com/maps/search/?api=1&query=${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}\n";
message += "Please help! Contact me as soon as possible.".tr;
launchCommunication(
'whatsapp', box.read(BoxName.sosPhonePassenger), message);
}
String formatSyrianPhone(String phone) {
phone = phone.replaceAll(' ', '').replaceAll('+', '');
if (phone.startsWith('00963')) {
phone = phone.replaceFirst('00963', '963');
}
if (phone.startsWith('0963')) {
phone = phone.replaceFirst('0963', '963');
}
if (phone.startsWith('963')) {
return phone;
}
if (phone.startsWith('09')) {
return '963' + phone.substring(1);
}
if (phone.startsWith('9') && phone.length == 9) {
return '963' + phone;
}
return phone;
}
String formatSyrianPhoneNumber(String phoneNumber) {
String trimmedPhone = phoneNumber.trim();
if (trimmedPhone.startsWith('09')) {
return '963${trimmedPhone.substring(1)}';
}
if (trimmedPhone.startsWith('963')) {
return trimmedPhone;
}
return '963$trimmedPhone';
}
void sendSMS(String to) async {
final rideLifecycle = Get.find<RideLifecycleController>();
String formattedDriverPhone =
rideLifecycle.driverPhone.replaceAll(' ', '').replaceAll('+', '');
String message =
'Hi! This is ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n I am using ${box.read(AppInformation.appName)} to ride with ${rideLifecycle.passengerName} as the driver. ${rideLifecycle.passengerName} \nis driving a ${rideLifecycle.model}\n with license plate ${rideLifecycle.licensePlate}.\n I am currently located at ${Get.find<LocationSearchController>().passengerLocation}.\n If you need to reach me, please contact the driver directly at\n\n $formattedDriverPhone.';
launchCommunication('sms', to, message);
}
void sendWhatsapp(String to) async {
final rideLifecycle = Get.find<RideLifecycleController>();
final locSearch = Get.find<LocationSearchController>();
String formattedPhone = formatSyrianPhone(to);
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} ${rideLifecycle.passengerName}${' as the driver.'.tr} ${rideLifecycle.passengerName} \n${'is driving a '.tr}${rideLifecycle.model}\n${' with license plate '.tr}${rideLifecycle.licensePlate}.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n ${rideLifecycle.driverPhone}.';
launchCommunication('whatsapp', formattedPhone, message);
}
Future<dynamic> driverArrivePassengerDialoge() {
final rideLifecycle = Get.find<RideLifecycleController>();
return Get.defaultDialog(
barrierDismissible: false,
title: 'Hi ,I Arrive your location'.tr,
titleStyle: AppStyle.title,
middleText: 'Please go to Car Driver'.tr,
middleTextStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'Ok I will go now.'.tr,
onPressed: () {
NotificationService.sendNotification(
target: rideLifecycle.driverToken.toString(),
title: 'Hi ,I will go now'.tr,
body: 'I will go now'.tr,
isTopic: false,
tone: 'ding',
driverList: [],
category: 'Hi ,I will go now',
);
Get.back();
rideLifecycle.remainingTime = 0;
rideLifecycle.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();
},
),
);
}
Future shareTripWithFamily() async {
_ensureSosNumber(() {
final rideLifecycle = Get.find<RideLifecycleController>();
String storedPhone = box.read(BoxName.sosPhonePassenger)!;
if (rideLifecycle.rideId == 'yet' || rideLifecycle.driverId.isEmpty) {
Get.snackbar("Alert".tr, "Wait for the trip to start first".tr);
return;
}
var numberPhone = formatSyrianPhoneNumber(storedPhone);
String trackingLink = rideLifecycle.generateTrackingLink(
rideLifecycle.rideId, rideLifecycle.driverId);
String message = """
مرحباً، تابع رحلتي مباشرة على تطبيق انطلق 🚗
يمكنك تتبع مسار الرحلة من هنا:
$trackingLink
السائق: ${rideLifecycle.passengerName}
السيارة: ${rideLifecycle.model} - ${rideLifecycle.licensePlate}
شكراً لاستخدامك انطلق!
"""
.tr;
String messageEn = """Hello, follow my trip live on Intaleq 🚗
Track my ride here:
$trackingLink
Driver: ${rideLifecycle.passengerName}
Car: ${rideLifecycle.model} - ${rideLifecycle.licensePlate}
Thank you for using Intaleq!
""";
String userLanguage = box.read(BoxName.lang) ?? 'ar';
message = (userLanguage == 'ar') ? message : messageEn;
Log.print("Sending WhatsApp to: $numberPhone");
launchCommunication('whatsapp', numberPhone, message);
box.write(BoxName.parentTripSelected, true);
update();
});
}
Future getTokenForParent() async {
_ensureSosNumber(() async {
String storedPhone = box.read(BoxName.sosPhonePassenger)!;
var numberPhone = formatSyrianPhoneNumber(storedPhone);
Log.print("Searching for Parent Token with Phone: $numberPhone");
var res = await CRUD()
.post(link: AppLink.getTokenParent, payload: {'phone': numberPhone});
if (res is Map<String, dynamic>) {
handleResponse(res);
} else {
try {
var decoded = jsonDecode(res);
handleResponse(decoded);
} catch (e) {
Log.print("Error parsing parent response: $res");
}
}
});
}
void handleResponse(Map<String, dynamic> res) {
final rideLifecycle = Get.find<RideLifecycleController>();
if (res['status'] == 'failure') {
if (Get.isDialogOpen ?? false) Get.back();
Get.defaultDialog(
title: "No user found".tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text(
"No passenger found for the given phone number".tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Text(
"Send Intaleq app to him".tr,
style: AppStyle.title
.copyWith(color: AppColor.greenColor, fontSize: 14),
textAlign: TextAlign.center,
)
],
),
confirm: MyElevatedButton(
title: 'Send Invite'.tr,
onPressed: () {
Get.back();
var rawPhone = box.read(BoxName.sosPhonePassenger);
if (rawPhone == null) return;
var phone = formatSyrianPhoneNumber(rawPhone);
var message = '''Dear Friend,
🚀 I have just started an exciting trip on Intaleq!
Download the app to track my ride:
👉 Android: https://play.google.com/store/apps/details?id=com.Intaleq.intaleq&hl=en-US
👉 iOS: https://apps.apple.com/st/app/intaleq-rider/id6748075179
See you there!
Intaleq Team''';
launchCommunication('whatsapp', phone, message);
},
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () {
Get.back();
},
),
);
} else if (res['status'] == 'success') {
if (Get.isDialogOpen ?? false) Get.back();
Get.snackbar("Success".tr, "The invitation was sent successfully".tr,
backgroundColor: AppColor.greenColor, colorText: Colors.white);
List tokensData = res['data'];
for (var device in tokensData) {
String tokenParent = device['token'];
NotificationService.sendNotification(
category: "Trip Monitoring",
target: tokenParent,
title: "Trip Monitoring".tr,
body: "Click to track the trip".tr,
isTopic: false,
tone: 'tone1',
driverList: [rideLifecycle.rideId, rideLifecycle.driverId],
);
box.write(BoxName.tokenParent, tokenParent);
}
box.write(BoxName.parentTripSelected, true);
}
}
@override
void onClose() {
EmergencySignalService.instance.stopListening();
super.onClose();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
import '../../constant/api_key.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../functions/crud.dart';
import '../functions/location_controller.dart';
@@ -114,14 +114,21 @@ class WayPointController extends GetxController {
void onInit() {
// Get.put(LocationController());
addWayPoints();
myLocation = Get.find<MapPassengerController>().passengerLocation;
myLocation = Get.find<RideLifecycleController>().passengerLocation;
super.onInit();
}
void reset() {
wayPoints.clear();
addWayPoints();
placeListResponse.clear();
update();
}
}
class PlaceList extends StatelessWidget {
// Get the controller instance
final controller = Get.put(WayPointController());
final controller = Get.find<WayPointController>();
@override
Widget build(BuildContext context) {

View File

@@ -0,0 +1,159 @@
--- PRECISE MISSING METHODS ---
Total original methods/getters/setters: 270
Total defined in split controllers: 270
Total missing: 53
- Column
- CupertinoDialogAction
- Future
- _applyLowEndModeIfNeeded
- _buildOsrmWaypointCoords
- _calculateDistance
- _checkAndRecalculateIfDeviated
- _fillDriverDataLocally
- _haversineKm
- _initMinimalIcons
- _initializePolygons
- _isActiveRideState
- _kmToLatDelta
- _kmToLngDelta
- _onDriverAcceptedWithSocket
- _onDriverArrivedWithSocket
- _onRideCancelledWithSocket
- _onRideStartedWithSocket
- _playRouteAnimation
- _relevanceScore
- _restorePolyline
- _retryProcess
- _stageNiceToHave
- _stagePricingAndState
- _startMasterTimer
- _startMasterTimerWithInterval
- _startPollingFallback
- _stopDriverLocationPolling
- _updateDriverMarker
- addPostFrameCallback
- cancelRide
- checkPassengerLocation
- currentDriverMarkerId
- detectPerfMode
- directions
- getAIKey
- getDirectionMap
- getDistanceFromDriverAfterAcceptedRide
- getMapPointsForAllMethods
- getPassengerLocationUniversity
- getRideStatus
- handleActiveRideOnStartup
- handleNoDriverFound
- isDriversDataValid
- lastWhere
- onChangedPassengerCount
- onChangedPassengersChoose
- processRideAcceptance
- retrySearchForDrivers
- showDrawingBottomSheet
- showNoDriversDialog
- startSearchingTimer
- wait
--- PRECISE MISSING VARIABLES/FIELDS ---
Total original variables: 626
Total defined in split controllers: 558
Total missing: 97
- EdgeInsets
- Error
- InfoWindow
- LatLngBounds
- LocationData
- R
- _buildOsrmWaypointCoords
- _calculateDistance
- _haversineKm
- _isActiveRideState
- _isStateProcessing
- _isUsingFallback
- _kmToLatDelta
- _kmToLngDelta
- _reconnectTimer
- _relevanceScore
- a
- aerialDistance
- apiDistanceMeters
- apiKey
- attemptCount
- c
- carInfo
- cardNumber
- carsOrder
- checkPassengerLocation
- commissionPct
- context
- coordDestination
- currentAttempt
- currentCarType
- currentLocationOfDrivers
- currentRideId
- currentTimeSearchingCaptainWindow
- dInfo
- dLat
- dataCarsLocationByPassenger
- datadriverCarsLocationToPassengerAfterApplied
- dest
- deviation
- distanceOfTrip
- driverCarPlate
- driverLocationToPassenger
- driverName
- driverOrderStatus
- driverPhone
- durationByPassenger
- dynamicApiUrl
- etaText
- fName
- finalReason
- firebaseMessagesController
- increaseFeeFormKey
- info
- isBeginRideFromDriverRunning
- isDrawingRoute
- isDriversDataValid
- isDriversTokensSend
- isInUniversity
- isRequestValid
- kDurationScalar
- key
- km
- kmInDegree
- lName
- latDest
- latestWaypoint
- lngDest
- lowPerf
- mapAPIKEY
- messagesFormKey
- might
- minBillableKm
- minFareSYP
- newValue
- northeast
- originCoords
- pLower
- passengerLocation
- passengerLocationStringUnvirsity
- placeName
- polylineString
- previousLocationOfDrivers
- progressTimerRideBeginVip
- promoFormKey
- qLower
- query
- rLat1
- rLat2
- ram
- rideData
- sdk
- selectedPassengerCount
- southwest
- startLng
- status
- stringElapsedTimeRideBegin

View File

@@ -85,11 +85,13 @@ class ComplaintController extends GetxController {
var uri = Uri.parse('${AppLink.server}/upload_audio.php');
var request = http.MultipartRequest('POST', uri);
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
final String fingerPrint = box.read(BoxName.deviceFpEncrypted)?.toString() ?? '';
var mimeType = lookupMimeType(audioFile.path);
// ** التعديل: تم استخدام نفس هيدر التوثيق **
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': fingerPrint,
});
request.files.add(
await http.MultipartFile.fromPath(

View File

@@ -4,8 +4,7 @@ import 'dart:convert';
import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/links.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:Intaleq/views/widgets/my_scafold.dart';
@@ -15,6 +14,7 @@ import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../constant/style.dart';
import '../functions/crud.dart';
import '../functions/encrypt_decrypt.dart';
@@ -42,7 +42,7 @@ class VipOrderController extends GetxController {
Future<void> fetchOrder() async {
try {
isLoading.value = true;
var mapPassengerController = Get.find<MapPassengerController>();
var mapPassengerController = Get.find<RideLifecycleController>();
var res = await CRUD().get(
link: AppLink.getMishwari,
@@ -239,7 +239,7 @@ class VipWaittingPage extends StatelessWidget {
title: "Cancel Trip".tr,
kolor: AppColor.redColor,
onPressed: () {
Get.find<MapPassengerController>().cancelVip(
Get.find<RideLifecycleController>().cancelVip(
data['token'].toString(),
data['id'].toString(),
);
@@ -272,7 +272,7 @@ class VipWaittingPage extends StatelessWidget {
title: "Send to Driver Again".tr,
kolor: AppColor.greenColor,
onPressed: () {
Get.find<MapPassengerController>()
Get.find<RideLifecycleController>()
.sendToDriverAgain(data['token']);
vipOrderController.fetchOrder();
},
@@ -292,7 +292,7 @@ class VipWaittingPage extends StatelessWidget {
kolor: AppColor.greenColor,
onPressed: () {
final mapPassengerController =
Get.find<MapPassengerController>();
Get.find<RideLifecycleController>();
mapPassengerController.make = data['make'];
mapPassengerController.licensePlate =
data['car_plate'];