Update: 2026-06-10 18:11:50
This commit is contained in:
973
review_diff.patch
Normal file
973
review_diff.patch
Normal file
@@ -0,0 +1,973 @@
|
||||
diff --git a/backend/ride/rides/acceptRide.php b/backend/ride/rides/acceptRide.php
|
||||
index 85c7832..487e2bc 100755
|
||||
--- a/backend/ride/rides/acceptRide.php
|
||||
+++ b/backend/ride/rides/acceptRide.php
|
||||
@@ -121,6 +121,8 @@ try {
|
||||
c.color,
|
||||
c.color_hex,
|
||||
(SELECT ROUND(AVG(rating), 2) FROM ratingDriver WHERE driver_id = d.id) AS ratingDriver,
|
||||
+ (SELECT COUNT(*) FROM ratingDriver WHERE driver_id = d.id) AS ratingCount,
|
||||
+ (SELECT COUNT(*) FROM ride WHERE driver_id = d.id AND status IN ('Finished', 'finished')) AS completedRides,
|
||||
dt.token
|
||||
FROM driver d
|
||||
LEFT JOIN CarRegistration c ON c.driverID = d.id
|
||||
@@ -140,6 +142,16 @@ try {
|
||||
}
|
||||
$driverInfo['driverName'] = trim(($driverInfo['first_name'] ?? '') . ' ' . ($driverInfo['last_name'] ?? ''));
|
||||
$driverInfo['ratingDriver'] = $driverInfo['ratingDriver'] ?: "5.0";
|
||||
+ $ratingValue = (float) $driverInfo['ratingDriver'];
|
||||
+ $ratingCount = (int) ($driverInfo['ratingCount'] ?? 0);
|
||||
+ $completedRides = (int) ($driverInfo['completedRides'] ?? 0);
|
||||
+ if ($ratingValue >= 4.8 && $ratingCount >= 50 && $completedRides >= 100) {
|
||||
+ $driverInfo['driverTier'] = 'Professional driver';
|
||||
+ } elseif ($ratingValue >= 4.5 && $ratingCount >= 15 && $completedRides >= 30) {
|
||||
+ $driverInfo['driverTier'] = 'Trusted driver';
|
||||
+ } else {
|
||||
+ $driverInfo['driverTier'] = 'Verified driver';
|
||||
+ }
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -195,4 +207,4 @@ try {
|
||||
} catch (PDOException $e) {
|
||||
error_log("[accept_ride] CRITICAL: " . $e->getMessage());
|
||||
printFailure("Server error");
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
diff --git a/backend/ride/rides/getRideOrderID.php b/backend/ride/rides/getRideOrderID.php
|
||||
index f8532ff..7bc828e 100755
|
||||
--- a/backend/ride/rides/getRideOrderID.php
|
||||
+++ b/backend/ride/rides/getRideOrderID.php
|
||||
@@ -96,6 +96,17 @@ try {
|
||||
FROM ratingDriver
|
||||
WHERE ratingDriver.driver_id = :driverID_Sub
|
||||
) AS ratingDriver,
|
||||
+ (
|
||||
+ SELECT COUNT(*)
|
||||
+ FROM ratingDriver
|
||||
+ WHERE ratingDriver.driver_id = :driverID_Sub
|
||||
+ ) AS ratingCount,
|
||||
+ (
|
||||
+ SELECT COUNT(*)
|
||||
+ FROM ride
|
||||
+ WHERE ride.driver_id = :driverID_Sub
|
||||
+ AND ride.status IN ('Finished', 'finished')
|
||||
+ ) AS completedRides,
|
||||
|
||||
driverToken.token AS token
|
||||
|
||||
@@ -143,6 +154,16 @@ try {
|
||||
$finalData[$field] = $encryptionHelper->decryptData($finalData[$field]);
|
||||
}
|
||||
}
|
||||
+ $ratingValue = (float) ($finalData['ratingDriver'] ?: 5.0);
|
||||
+ $ratingCount = (int) ($finalData['ratingCount'] ?? 0);
|
||||
+ $completedRides = (int) ($finalData['completedRides'] ?? 0);
|
||||
+ if ($ratingValue >= 4.8 && $ratingCount >= 50 && $completedRides >= 100) {
|
||||
+ $finalData['driverTier'] = 'Professional driver';
|
||||
+ } elseif ($ratingValue >= 4.5 && $ratingCount >= 15 && $completedRides >= 30) {
|
||||
+ $finalData['driverTier'] = 'Trusted driver';
|
||||
+ } else {
|
||||
+ $finalData['driverTier'] = 'Verified driver';
|
||||
+ }
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
@@ -155,4 +176,4 @@ try {
|
||||
http_response_code(500);
|
||||
echo json_encode(["status" => "failure", "message" => "Server Error: " . $e->getMessage()]);
|
||||
}
|
||||
-?>
|
||||
\ No newline at end of file
|
||||
+?>
|
||||
diff --git a/siro_driver/lib/controller/firebase/firbase_messge.dart b/siro_driver/lib/controller/firebase/firbase_messge.dart
|
||||
index 9de8eae..d183550 100755
|
||||
--- a/siro_driver/lib/controller/firebase/firbase_messge.dart
|
||||
+++ b/siro_driver/lib/controller/firebase/firbase_messge.dart
|
||||
@@ -76,15 +76,22 @@ class FirebaseMessagesController extends GetxController {
|
||||
await fcmToken.subscribeToTopic("drivers"); // أو "users" حسب نوع المستخدم
|
||||
print("Subscribed to 'drivers' topic ✅");
|
||||
|
||||
- FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async {
|
||||
+ FirebaseMessaging.instance
|
||||
+ .getInitialMessage()
|
||||
+ .then((RemoteMessage? message) async {
|
||||
if (message != null && message.data.isNotEmpty) {
|
||||
Log.print("🔔 FCM getInitialMessage payload: ${message.data}");
|
||||
String? category = message.data['category'] ?? message.data['type'];
|
||||
- if (category == 'ORDER' || category == 'Order' || category == 'OrderVIP' || message.data.containsKey('DriverList')) {
|
||||
+ if (category == 'ORDER' ||
|
||||
+ category == 'Order' ||
|
||||
+ category == 'OrderVIP' ||
|
||||
+ message.data.containsKey('DriverList')) {
|
||||
String? myListString = message.data['DriverList'];
|
||||
if (myListString != null && myListString.isNotEmpty) {
|
||||
- await storage.write(key: 'pending_driver_list', value: myListString);
|
||||
- Log.print("💾 Saved pending driver list to secure storage from getInitialMessage");
|
||||
+ await storage.write(
|
||||
+ key: 'pending_driver_list', value: myListString);
|
||||
+ Log.print(
|
||||
+ "💾 Saved pending driver list to secure storage from getInitialMessage");
|
||||
}
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 1500), () {
|
||||
@@ -107,7 +114,6 @@ class FirebaseMessagesController extends GetxController {
|
||||
// fireBaseTitles(message);
|
||||
// }
|
||||
});
|
||||
- FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {});
|
||||
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
||||
if (message.data.isNotEmpty) {
|
||||
diff --git a/siro_driver/lib/controller/functions/background_service.dart b/siro_driver/lib/controller/functions/background_service.dart
|
||||
index e112b36..5d9bec9 100644
|
||||
--- a/siro_driver/lib/controller/functions/background_service.dart
|
||||
+++ b/siro_driver/lib/controller/functions/background_service.dart
|
||||
@@ -9,7 +9,6 @@ import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||
import 'package:socket_io_client/socket_io_client.dart' as IO;
|
||||
import 'package:flutter_overlay_window/flutter_overlay_window.dart' as Overlay;
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
-import 'package:geolocator/geolocator.dart' as geo;
|
||||
import '../../constant/box_name.dart';
|
||||
import '../firebase/local_notification.dart';
|
||||
|
||||
@@ -129,40 +128,21 @@ Future<bool> onStart(ServiceInstance service) async {
|
||||
service.stopSelf();
|
||||
});
|
||||
|
||||
- // 🔥 Location management in background isolate (Using Geolocator)
|
||||
- geo.Position? latestPos;
|
||||
-
|
||||
- // Listen to location changes continuously in the background
|
||||
- geo.Geolocator.getPositionStream(
|
||||
- locationSettings: geo.AndroidSettings(
|
||||
- accuracy: geo.LocationAccuracy.high,
|
||||
- distanceFilter: 10,
|
||||
- intervalDuration: const Duration(seconds: 10),
|
||||
- ),
|
||||
- ).listen((pos) {
|
||||
- latestPos = pos;
|
||||
- });
|
||||
-
|
||||
- // 🔥 MERCY HEARTBEAT: Send location every 2 minutes to keep driver active in 'raids'
|
||||
- Timer.periodic(const Duration(minutes: 2), (timer) async {
|
||||
- if (socket != null && socket.connected && latestPos != null) {
|
||||
- try {
|
||||
- socket.emit('update_location', {
|
||||
- 'driver_id': driverId,
|
||||
- 'lat': latestPos!.latitude,
|
||||
- 'lng': latestPos!.longitude,
|
||||
- 'heading': latestPos!.heading,
|
||||
- 'speed': latestPos!.speed * 3.6,
|
||||
- 'status': box.read(BoxName.statusDriverLocation) ?? 'on',
|
||||
- 'source': 'background_heartbeat'
|
||||
- });
|
||||
- print(
|
||||
- "💓 Background Mercy Heartbeat Sent: ${latestPos!.latitude}, ${latestPos!.longitude}");
|
||||
- } catch (e) {
|
||||
- print("❌ Background Heartbeat Error: $e");
|
||||
- }
|
||||
- }
|
||||
- });
|
||||
+ // 🚫 [Architecture Rule] NO redundant GPS stream in background service!
|
||||
+ // LocationController is the SINGLE SOURCE OF TRUTH for all location GPS updates.
|
||||
+ // It already uses location.enableBackgroundMode(enable: true) to keep the GPS
|
||||
+ // stream alive even when the app is in the background. The main socket in
|
||||
+ // LocationController handles all emitLocationToSocket() calls including heartbeat.
|
||||
+ //
|
||||
+ // The background service is ONLY responsible for:
|
||||
+ // 1. Keeping the socket connection alive for receiving 'new_ride_request'
|
||||
+ // and 'cancel_ride' events while the main isolate is paused on Android.
|
||||
+ // 2. Showing the Android Overlay UI for incoming ride requests.
|
||||
+ // 3. Notifications for iOS background state.
|
||||
+ //
|
||||
+ // Location data is not sent from the background isolate — it would conflict
|
||||
+ // with LocationController's stream and cause duplicate GPS listeners,
|
||||
+ // battery drain, and device freeze (as documented in driver_lifecycle.md).
|
||||
|
||||
Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||||
if (service is AndroidServiceInstance) {
|
||||
diff --git a/siro_driver/lib/controller/functions/location_controller.dart b/siro_driver/lib/controller/functions/location_controller.dart
|
||||
index 3367bd8..fc15ef0 100755
|
||||
--- a/siro_driver/lib/controller/functions/location_controller.dart
|
||||
+++ b/siro_driver/lib/controller/functions/location_controller.dart
|
||||
@@ -19,6 +19,7 @@ import '../firebase/local_notification.dart';
|
||||
import '../home/captin/home_captain_controller.dart';
|
||||
import '../home/captin/map_driver_controller.dart';
|
||||
import '../home/payment/captain_wallet_controller.dart';
|
||||
+import '../home/navigation/navigation_controller.dart';
|
||||
import 'background_service.dart';
|
||||
import 'crud.dart';
|
||||
|
||||
@@ -539,6 +540,16 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (Get.isRegistered<MapDriverController>()) {
|
||||
+ final mapCtrl = Get.find<MapDriverController>();
|
||||
+ mapCtrl.handleLocationUpdateFromCentral(pos, speed, heading);
|
||||
+ }
|
||||
+
|
||||
+ if (Get.isRegistered<NavigationController>()) {
|
||||
+ final navCtrl = Get.find<NavigationController>();
|
||||
+ navCtrl.handleLocationUpdateFromCentral(pos, speed, heading);
|
||||
+ }
|
||||
+
|
||||
await _saveBehaviorIfMoved(pos, now, currentSpeed: speed);
|
||||
}, onError: (e) => Log.print('❌ Location Stream Error: $e'));
|
||||
}
|
||||
diff --git a/siro_driver/lib/controller/home/captin/map_driver_controller.dart b/siro_driver/lib/controller/home/captin/map_driver_controller.dart
|
||||
index 8aa56ae..3baad9e 100755
|
||||
--- a/siro_driver/lib/controller/home/captin/map_driver_controller.dart
|
||||
+++ b/siro_driver/lib/controller/home/captin/map_driver_controller.dart
|
||||
@@ -2570,27 +2570,19 @@ class MapDriverController extends GetxController
|
||||
}
|
||||
|
||||
void _startLocationListening() {
|
||||
- _locationSubscription?.cancel();
|
||||
- _locationSubscription = geo.Geolocator.getPositionStream(
|
||||
- locationSettings: const geo.LocationSettings(
|
||||
- accuracy: geo.LocationAccuracy.bestForNavigation,
|
||||
- distanceFilter: 2,
|
||||
- ),
|
||||
- ).listen((geo.Position pos) {
|
||||
- _handleLocationUpdate(pos);
|
||||
- });
|
||||
+ // Location stream is now centralized in LocationController to prevent device hanging.
|
||||
+ // LocationController will call handleLocationUpdateFromCentral directly.
|
||||
}
|
||||
|
||||
/// [Fix C-4] تحديث myLocation في المستمع الأساسي
|
||||
- void _handleLocationUpdate(geo.Position pos) {
|
||||
- final newLoc = LatLng(pos.latitude, pos.longitude);
|
||||
+ void handleLocationUpdateFromCentral(LatLng newLoc, double posSpeed, double posHeading) {
|
||||
myLocation = newLoc; // ← [Fix C-4] تحديث الموقع الفوري
|
||||
_oldLoc = smoothedLocation ?? newLoc;
|
||||
_targetLoc = newLoc;
|
||||
|
||||
_oldHeading = smoothedHeading;
|
||||
- if (pos.speed > 0.5) {
|
||||
- _targetHeading = pos.heading;
|
||||
+ if (posSpeed > 0.5) {
|
||||
+ _targetHeading = posHeading;
|
||||
} else {
|
||||
_targetHeading = _oldHeading;
|
||||
}
|
||||
diff --git a/siro_driver/lib/controller/home/captin/order_request_controller.dart b/siro_driver/lib/controller/home/captin/order_request_controller.dart
|
||||
index f58d4f4..169ca95 100755
|
||||
--- a/siro_driver/lib/controller/home/captin/order_request_controller.dart
|
||||
+++ b/siro_driver/lib/controller/home/captin/order_request_controller.dart
|
||||
@@ -69,6 +69,7 @@ class OrderRequestController extends GetxController
|
||||
|
||||
// --- الخريطة ---
|
||||
Set<Polyline> polylines = {};
|
||||
+ bool _hasCalculatedFullJourney = false;
|
||||
|
||||
// حالة التطبيق والصوت
|
||||
bool isInBackground = false;
|
||||
@@ -219,6 +220,11 @@ class OrderRequestController extends GetxController
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Future<void> _calculateFullJourney() async {
|
||||
+ if (_hasCalculatedFullJourney) {
|
||||
+ if (mapController != null) zoomToFitRide();
|
||||
+ return;
|
||||
+ }
|
||||
+ _hasCalculatedFullJourney = true;
|
||||
// Don't block on mapController being null - we'll draw routes
|
||||
// and markers first, then zoom when controller is ready
|
||||
bool canZoom = mapController != null;
|
||||
@@ -281,7 +287,7 @@ class OrderRequestController extends GetxController
|
||||
totalTripDistance = tripResult['distance_text'];
|
||||
totalTripDuration = tripResult['duration_text'];
|
||||
polylines.add(tripResult['polyline']);
|
||||
-
|
||||
+
|
||||
// 🔥 تخزين استجابة السيرفر كاملة (بما فيها الـ points والـ instructions)
|
||||
if (tripResult['raw_response'] != null) {
|
||||
box.write('cached_trip_route', tripResult['raw_response']);
|
||||
diff --git a/siro_driver/lib/controller/home/navigation/navigation_controller.dart b/siro_driver/lib/controller/home/navigation/navigation_controller.dart
|
||||
index e607f6d..e2a4f08 100644
|
||||
--- a/siro_driver/lib/controller/home/navigation/navigation_controller.dart
|
||||
+++ b/siro_driver/lib/controller/home/navigation/navigation_controller.dart
|
||||
@@ -476,32 +476,18 @@ class NavigationController extends GetxController
|
||||
}
|
||||
|
||||
void _startLocationStream() {
|
||||
- _locationStreamSubscription?.cancel();
|
||||
- // Listen to location updates with minimum distance filter of 2 meters
|
||||
- // This provides real-time updates without the 3-4 second delay
|
||||
- _locationStreamSubscription = Geolocator.getPositionStream(
|
||||
- locationSettings: const LocationSettings(
|
||||
- accuracy: LocationAccuracy.high,
|
||||
- distanceFilter: 2, // Update every 2 meters
|
||||
- ),
|
||||
- ).listen(
|
||||
- (Position position) {
|
||||
- _handleLocationUpdate(position);
|
||||
- },
|
||||
- onError: (error) {
|
||||
- Log.print("DEBUG: Location stream error: $error");
|
||||
- },
|
||||
- );
|
||||
+ // Location stream is now centralized in LocationController to prevent device hanging.
|
||||
+ // LocationController will call handleLocationUpdateFromCentral directly.
|
||||
}
|
||||
|
||||
bool _isProcessing = false;
|
||||
- Future<void> _handleLocationUpdate(Position position) async {
|
||||
+ Future<void> handleLocationUpdateFromCentral(LatLng newLoc, double locSpeed, double locHeading) async {
|
||||
if (_isProcessing) return;
|
||||
_isProcessing = true;
|
||||
|
||||
try {
|
||||
- final newLoc = LatLng(position.latitude, position.longitude);
|
||||
- currentSpeed = position.speed * 3.6; // Convert m/s to km/h
|
||||
+ currentSpeed = locSpeed; // Convert m/s to km/h already done by location controller if needed, wait location_controller sends raw speed or km/h? It sends raw speed. So we should * 3.6
|
||||
+ currentSpeed = locSpeed * 3.6;
|
||||
|
||||
// Skip if movement is too small
|
||||
if (_lastProcessedLocation != null) {
|
||||
@@ -544,7 +530,7 @@ class NavigationController extends GetxController
|
||||
_targetLoc!.longitude,
|
||||
);
|
||||
} else {
|
||||
- _targetHeading = position.heading;
|
||||
+ _targetHeading = locHeading;
|
||||
}
|
||||
|
||||
_animController?.forward(from: 0.0);
|
||||
diff --git a/siro_rider/lib/controller/firebase/firbase_messge.dart b/siro_rider/lib/controller/firebase/firbase_messge.dart
|
||||
index e24c3c8..5b54235 100644
|
||||
--- a/siro_rider/lib/controller/firebase/firbase_messge.dart
|
||||
+++ b/siro_rider/lib/controller/firebase/firbase_messge.dart
|
||||
@@ -87,12 +87,6 @@ class FirebaseMessagesController extends GetxController {
|
||||
fireBaseTitles(message);
|
||||
}
|
||||
});
|
||||
- FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
|
||||
- // Handle background message
|
||||
- if (message.data.isNotEmpty) {
|
||||
- fireBaseTitles(message);
|
||||
- }
|
||||
- });
|
||||
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
||||
if (message.data.isNotEmpty && message.notification != null) {
|
||||
diff --git a/siro_rider/lib/controller/home/map/map_socket_controller.dart b/siro_rider/lib/controller/home/map/map_socket_controller.dart
|
||||
index 3d73bae..95fbf21 100644
|
||||
--- a/siro_rider/lib/controller/home/map/map_socket_controller.dart
|
||||
+++ b/siro_rider/lib/controller/home/map/map_socket_controller.dart
|
||||
@@ -283,7 +283,7 @@ class MapSocketController extends GetxController {
|
||||
}
|
||||
|
||||
final dynamic distanceValue =
|
||||
- data['distance_m'] ?? data['distance_meters'] ?? data['distance'];
|
||||
+ data['distance_m'] ?? data['distance_meters'];
|
||||
final double? distanceMeters =
|
||||
double.tryParse(distanceValue?.toString() ?? '');
|
||||
final int? etaSeconds = data['eta_seconds'] == null
|
||||
diff --git a/siro_rider/lib/controller/home/map/ride_lifecycle_controller.dart b/siro_rider/lib/controller/home/map/ride_lifecycle_controller.dart
|
||||
index c229ad2..c264a61 100644
|
||||
--- a/siro_rider/lib/controller/home/map/ride_lifecycle_controller.dart
|
||||
+++ b/siro_rider/lib/controller/home/map/ride_lifecycle_controller.dart
|
||||
@@ -112,6 +112,7 @@ class RideLifecycleController extends GetxController {
|
||||
late String driverId = '';
|
||||
late String make = '';
|
||||
late String model = '';
|
||||
+ late String gender = '';
|
||||
late String carColor = '';
|
||||
late String licensePlate = '';
|
||||
late String driverName = '';
|
||||
@@ -120,6 +121,9 @@ class RideLifecycleController extends GetxController {
|
||||
late String colorHex = '';
|
||||
late String carYear = '';
|
||||
late String driverRate = '5.0';
|
||||
+ late String driverRatingCount = '0';
|
||||
+ late String driverCompletedRides = '0';
|
||||
+ late String driverTier = 'Verified driver';
|
||||
late String driverToken = '';
|
||||
|
||||
double kazan = 8;
|
||||
@@ -1481,7 +1485,8 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
// إيقاف جلب السيارات المجاورة ومسحها، باستثناء السائق الذي قبل الطلب
|
||||
mapEngine.reloadStartApp = false;
|
||||
- mapEngine.markers.removeWhere((marker) => marker.markerId.value != driverId.toString());
|
||||
+ mapEngine.markers
|
||||
+ .removeWhere((marker) => marker.markerId.value != driverId.toString());
|
||||
mapEngine.update();
|
||||
|
||||
await getDriverCarsLocationToPassengerAfterApplied();
|
||||
@@ -1490,8 +1495,7 @@ class RideLifecycleController extends GetxController {
|
||||
LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last;
|
||||
Log.print(
|
||||
'[rideAppliedFromDriver] 📍 Driver at: $driverPos, Passenger at: $passengerLocation');
|
||||
- await getInitialDriverDistanceAndDuration(driverPos, passengerLocation);
|
||||
- await drawDriverPathOnly(driverPos, passengerLocation);
|
||||
+ await calculateDriverToPassengerRoute(driverPos, passengerLocation);
|
||||
mapEngine.fitCameraToPoints(driverPos, passengerLocation);
|
||||
}
|
||||
|
||||
@@ -1656,6 +1660,9 @@ class RideLifecycleController extends GetxController {
|
||||
driverToken = data['token']?.toString() ?? '';
|
||||
carYear = data['year']?.toString() ?? '';
|
||||
driverRate = data['ratingDriver']?.toString() ?? '5.0';
|
||||
+ driverRatingCount = data['ratingCount']?.toString() ?? '0';
|
||||
+ driverCompletedRides = data['completedRides']?.toString() ?? '0';
|
||||
+ driverTier = data['driverTier']?.toString() ?? 'Verified driver';
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -2221,6 +2228,15 @@ class RideLifecycleController extends GetxController {
|
||||
polyLines = polyLines
|
||||
.where((p) => !p.polylineId.value.startsWith('driver_route'))
|
||||
.toSet();
|
||||
+ polyLines = {
|
||||
+ ...polyLines,
|
||||
+ Polyline(
|
||||
+ polylineId: const PolylineId('main_route'),
|
||||
+ points: decodedPoints,
|
||||
+ color: const Color(0xFF2196F3),
|
||||
+ width: 6,
|
||||
+ )
|
||||
+ };
|
||||
} else {
|
||||
// مسح السلمات القديمة أولاً
|
||||
polyLines = polyLines
|
||||
@@ -2290,7 +2306,9 @@ class RideLifecycleController extends GetxController {
|
||||
_routeHeadingMismatchCount = 0;
|
||||
_isRecalculatingRoute = true;
|
||||
if (statusRide == 'Begin' ||
|
||||
- currentRideState.value == RideState.inProgress) {
|
||||
+ statusRide == 'Arrived' ||
|
||||
+ currentRideState.value == RideState.inProgress ||
|
||||
+ currentRideState.value == RideState.driverArrived) {
|
||||
await calculateDriverToPassengerRoute(driverPos, myDestination,
|
||||
isBeginPhase: true);
|
||||
} else {
|
||||
@@ -2504,6 +2522,8 @@ class RideLifecycleController extends GetxController {
|
||||
String icon;
|
||||
if (model.contains('دراجة') || make.contains('دراجة')) {
|
||||
icon = mapEngine.motoIcon;
|
||||
+ } else if (gender == 'Female') {
|
||||
+ icon = mapEngine.ladyIcon;
|
||||
} else {
|
||||
icon = mapEngine.carIcon;
|
||||
}
|
||||
@@ -3026,6 +3046,17 @@ class RideLifecycleController extends GetxController {
|
||||
mapEngine.playRouteAnimation(
|
||||
mapEngine.polylineCoordinates, mapEngine.lastComputedBounds);
|
||||
}
|
||||
+
|
||||
+ if (driverCarsLocationToPassengerAfterApplied.isNotEmpty &&
|
||||
+ myDestination.latitude != 0 &&
|
||||
+ myDestination.longitude != 0) {
|
||||
+ await calculateDriverToPassengerRoute(
|
||||
+ driverCarsLocationToPassengerAfterApplied.last,
|
||||
+ myDestination,
|
||||
+ isBeginPhase: true,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -3903,12 +3934,37 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
make = data['make']?.toString() ?? '';
|
||||
model = data['model']?.toString() ?? '';
|
||||
+ gender = data['gender']?.toString() ?? '';
|
||||
carColor = data['color']?.toString() ?? '';
|
||||
colorHex = data['color_hex']?.toString() ?? '';
|
||||
licensePlate = data['car_plate']?.toString() ?? '';
|
||||
carYear = data['year']?.toString() ?? '';
|
||||
|
||||
+ // المحاولة الفورية لرسم السائق إذا توفرت الإحداثيات في البيانات
|
||||
+ double lat = double.tryParse(
|
||||
+ data['latitude']?.toString() ?? data['lat']?.toString() ?? '0') ??
|
||||
+ 0;
|
||||
+ double lng = double.tryParse(data['longitude']?.toString() ??
|
||||
+ data['lng']?.toString() ??
|
||||
+ '0') ??
|
||||
+ 0;
|
||||
+ double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0;
|
||||
+
|
||||
+ if (lat != 0 && lng != 0) {
|
||||
+ LatLng initialPos = LatLng(lat, lng);
|
||||
+ if (driverCarsLocationToPassengerAfterApplied.isEmpty) {
|
||||
+ driverCarsLocationToPassengerAfterApplied.add(initialPos);
|
||||
+ } else {
|
||||
+ driverCarsLocationToPassengerAfterApplied[0] = initialPos;
|
||||
+ }
|
||||
+ // تحديث الماركر فوراً لضمان ظهوره بشكل موثوق
|
||||
+ updateDriverMarker(initialPos, heading);
|
||||
+ }
|
||||
+
|
||||
driverRate = data['ratingDriver']?.toString() ?? '5.0';
|
||||
+ driverRatingCount = data['ratingCount']?.toString() ?? '0';
|
||||
+ driverCompletedRides = data['completedRides']?.toString() ?? '0';
|
||||
+ driverTier = data['driverTier']?.toString() ?? 'Verified driver';
|
||||
driverToken = data['token']?.toString() ?? '';
|
||||
|
||||
update();
|
||||
@@ -4185,55 +4241,6 @@ class RideLifecycleController extends GetxController {
|
||||
);
|
||||
}
|
||||
|
||||
- Future<void> getDistanceFromDriverAfterAcceptedRide(
|
||||
- String origin, String destination) async {
|
||||
- String apiKey = Env.mapKeyOsm;
|
||||
- if (origin.isEmpty) {
|
||||
- origin = '${passengerLocation.latitude},${passengerLocation.longitude}';
|
||||
- }
|
||||
- var uri = Uri.parse(
|
||||
- '$dynamicApiUrl?origin=$origin&destination=$destination&steps=false&overview=false');
|
||||
- Log.print('uri: $uri');
|
||||
-
|
||||
- http.Response response;
|
||||
- Map<String, dynamic> responseData;
|
||||
-
|
||||
- try {
|
||||
- response = await http.get(
|
||||
- uri,
|
||||
- headers: {
|
||||
- 'X-API-KEY': apiKey,
|
||||
- },
|
||||
- ).timeout(const Duration(seconds: 20));
|
||||
-
|
||||
- if (response.statusCode != 200) {
|
||||
- Log.print('Error from API: ${response.statusCode}');
|
||||
- isLoading = false;
|
||||
- update();
|
||||
- return;
|
||||
- }
|
||||
- if (Get.isBottomSheetOpen ?? false) {
|
||||
- Get.back();
|
||||
- }
|
||||
- isDrawingRoute = false;
|
||||
-
|
||||
- responseData = json.decode(response.body);
|
||||
- Log.print('responseData: $responseData');
|
||||
-
|
||||
- if (responseData['status'] != 'ok') {
|
||||
- Log.print('API returned an error: ${responseData['message']}');
|
||||
- isLoading = false;
|
||||
- update();
|
||||
- return;
|
||||
- }
|
||||
- } catch (e) {
|
||||
- Log.print('Failed to get directions: $e');
|
||||
- isLoading = false;
|
||||
- update();
|
||||
- return;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
Future<void> _stageNiceToHave() async {
|
||||
Log.print('🚀 MapPassengerController: Starting _stageNiceToHave');
|
||||
|
||||
diff --git a/siro_rider/lib/controller/home/map/ui_interactions_controller.dart b/siro_rider/lib/controller/home/map/ui_interactions_controller.dart
|
||||
index 388c28e..afa97d9 100644
|
||||
--- a/siro_rider/lib/controller/home/map/ui_interactions_controller.dart
|
||||
+++ b/siro_rider/lib/controller/home/map/ui_interactions_controller.dart
|
||||
@@ -4,7 +4,6 @@ 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';
|
||||
@@ -15,19 +14,13 @@ 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();
|
||||
@@ -56,54 +49,54 @@ class UiInteractionsController extends GetxController {
|
||||
|
||||
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,
|
||||
- ),
|
||||
- ],
|
||||
+ 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,
|
||||
- )
|
||||
- );
|
||||
+ 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() {
|
||||
@@ -114,10 +107,12 @@ class UiInteractionsController extends GetxController {
|
||||
titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
|
||||
content: Column(
|
||||
children: [
|
||||
- Icon(Icons.warning_amber_rounded, size: 50, color: AppColor.redColor),
|
||||
+ 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,
|
||||
+ "Do you want to send an emergency message to your SOS contact?"
|
||||
+ .tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
diff --git a/siro_rider/lib/controller/local/translations.dart b/siro_rider/lib/controller/local/translations.dart
|
||||
index 5244054..d42c370 100644
|
||||
--- a/siro_rider/lib/controller/local/translations.dart
|
||||
+++ b/siro_rider/lib/controller/local/translations.dart
|
||||
@@ -42,6 +42,7 @@ class MyTranslation extends Translations {
|
||||
"Arrived": "وصلنا",
|
||||
"Audio Recording": "تسجيل صوتي",
|
||||
"Call": "اتصال",
|
||||
+ "Call Options": "خيارات الاتصال",
|
||||
"Call Connected": "تم فتح الاتصال",
|
||||
"Call Support": "اتصل بالدعم",
|
||||
"Call left": "مكالمات متبقية",
|
||||
@@ -49,6 +50,8 @@ class MyTranslation extends Translations {
|
||||
"Change Photo": "تغيير الصورة",
|
||||
"Captain": "الكابتن",
|
||||
"Choose from Gallery": "اختر من المعرض",
|
||||
+ "Choose how you want to call the driver":
|
||||
+ "اختر طريقة الاتصال بالكابتن",
|
||||
"Choose from contact": "اختر من جهات الاتصال",
|
||||
"Click to track the trip": "اضغط لتتبع المشوار",
|
||||
"Close panel": "إغلاق اللوحة",
|
||||
@@ -92,6 +95,9 @@ class MyTranslation extends Translations {
|
||||
"Finished": "انتهى",
|
||||
"Fixed Price": "سعر ثابت",
|
||||
"Free Call": "مكالمة مجانية",
|
||||
+ "Professional driver": "كابتن محترف",
|
||||
+ "Trusted driver": "كابتن موثوق",
|
||||
+ "Verified driver": "كابتن موثق",
|
||||
"General": "عام",
|
||||
"Grant": "منح الإذن",
|
||||
"Have a Promo Code?": "معك كود خصم؟",
|
||||
@@ -178,6 +184,7 @@ class MyTranslation extends Translations {
|
||||
"Preferences": "التفضيلات",
|
||||
"Profile photo updated": "تم تحديث صورة الغلاف",
|
||||
"Quick Message": "رسالة سريعة",
|
||||
+ "reviews": "تقييم",
|
||||
"Rating is": "التقييم هو",
|
||||
"Received empty route data.": "تم استلام بيانات طريق فارغة.",
|
||||
"Record": "تسجيل",
|
||||
@@ -211,6 +218,7 @@ class MyTranslation extends Translations {
|
||||
"Set as Work": "تحديد كالشغل",
|
||||
"Share": "مشاركة",
|
||||
"Share Trip": "مشاركة المشوار",
|
||||
+ "Standard Call": "اتصال عادي",
|
||||
"Share your experience to help us improve...":
|
||||
"شاركنا تجربتك لنحسن خدمتنا...",
|
||||
"Something went wrong. Please try again.": "صار غلط. جرب مرة تانية.",
|
||||
@@ -271,6 +279,8 @@ class MyTranslation extends Translations {
|
||||
"to arrive you.": "ليوصلك.",
|
||||
"unknown": "غير معروف",
|
||||
"wait 1 minute to recive message": "استنى دقيقة لتستلم الرسالة",
|
||||
+ "Uses cellular network": "يستخدم شبكة الهاتف",
|
||||
+ "Voice call over internet": "مكالمة صوتية عبر الإنترنت",
|
||||
"with license plate": "برقم اللوحة",
|
||||
"witout zero": "بدون صفر",
|
||||
"you must insert token code": "لازم تدخل الكود",
|
||||
@@ -16885,7 +16895,8 @@ class MyTranslation extends Translations {
|
||||
"Support is Away": "سپورٹ اب دستیاب نہیں ہے",
|
||||
"Support is currently Online": "سپورٹ اب آن لائن ہے",
|
||||
"Voice Call": "صوتی کال",
|
||||
- "We're here to help you 24/7": "ہم چوبیس گھنٹے آپ کی مدد کے لیے حاضر ہیں",
|
||||
+ "We're here to help you 24/7":
|
||||
+ "ہم چوبیس گھنٹے آپ کی مدد کے لیے حاضر ہیں",
|
||||
"Working Hours:": "کام کے اوقات:",
|
||||
"1 Passenger": "1 Passenger",
|
||||
"2 Passengers": "2 Passengers",
|
||||
@@ -18446,7 +18457,8 @@ class MyTranslation extends Translations {
|
||||
"Support is Away": "सहायता अभी उपलब्ध नहीं है",
|
||||
"Support is currently Online": "सहायता अभी ऑनलाइन है",
|
||||
"Voice Call": "वॉइस कॉल",
|
||||
- "We're here to help you 24/7": "हम आपकी सहायता के लिए 24/7 उपलब्ध हैं",
|
||||
+ "We're here to help you 24/7":
|
||||
+ "हम आपकी सहायता के लिए 24/7 उपलब्ध हैं",
|
||||
"Working Hours:": "कार्य समय:",
|
||||
"1 Passenger": "1 Passenger",
|
||||
"2 Passengers": "2 Passengers",
|
||||
diff --git a/siro_rider/lib/views/home/map_widget.dart/apply_order_widget.dart b/siro_rider/lib/views/home/map_widget.dart/apply_order_widget.dart
|
||||
index 8168f4f..a0689dc 100644
|
||||
--- a/siro_rider/lib/views/home/map_widget.dart/apply_order_widget.dart
|
||||
+++ b/siro_rider/lib/views/home/map_widget.dart/apply_order_widget.dart
|
||||
@@ -250,19 +250,23 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
// صورة السائق (أصغر)
|
||||
- Container(
|
||||
- decoration: BoxDecoration(
|
||||
- shape: BoxShape.circle,
|
||||
- border: Border.all(
|
||||
- color: AppColor.primaryColor.withOpacity(0.2), width: 2),
|
||||
- ),
|
||||
- child: CircleAvatar(
|
||||
- radius: 22, // تصغير من 28 إلى 22
|
||||
- backgroundColor: Colors.grey[200],
|
||||
- backgroundImage: NetworkImage(
|
||||
- '${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
|
||||
- onBackgroundImageError: (_, __) =>
|
||||
- const Icon(Icons.person, color: Colors.grey, size: 20),
|
||||
+ GestureDetector(
|
||||
+ onTap: () => _showDriverAvatarDialog(context, controller),
|
||||
+ child: Container(
|
||||
+ decoration: BoxDecoration(
|
||||
+ shape: BoxShape.circle,
|
||||
+ border: Border.all(
|
||||
+ color: AppColor.primaryColor.withOpacity(0.2),
|
||||
+ width: 2),
|
||||
+ ),
|
||||
+ child: CircleAvatar(
|
||||
+ radius: 22, // تصغير من 28 إلى 22
|
||||
+ backgroundColor: Colors.grey[200],
|
||||
+ backgroundImage: NetworkImage(
|
||||
+ '${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
|
||||
+ onBackgroundImageError: (_, __) =>
|
||||
+ const Icon(Icons.person, color: Colors.grey, size: 20),
|
||||
+ ),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -299,6 +303,32 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
+ const SizedBox(height: 5),
|
||||
+ Wrap(
|
||||
+ spacing: 6,
|
||||
+ runSpacing: 4,
|
||||
+ children: [
|
||||
+ _buildDriverBadge(
|
||||
+ icon: Icons.verified_rounded,
|
||||
+ text: controller.driverTier.tr,
|
||||
+ color: AppColor.primaryColor,
|
||||
+ ),
|
||||
+ if (controller.driverCompletedRides != '0')
|
||||
+ _buildDriverBadge(
|
||||
+ icon: Icons.route_rounded,
|
||||
+ text:
|
||||
+ '${controller.driverCompletedRides} ${'rides'.tr}',
|
||||
+ color: Colors.teal,
|
||||
+ ),
|
||||
+ if (controller.driverRatingCount != '0')
|
||||
+ _buildDriverBadge(
|
||||
+ icon: Icons.reviews_rounded,
|
||||
+ text:
|
||||
+ '${controller.driverRatingCount} ${'reviews'.tr}',
|
||||
+ color: Colors.amber.shade800,
|
||||
+ ),
|
||||
+ ],
|
||||
+ ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -320,6 +350,11 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
Widget _buildMicroCarIcon(
|
||||
RideLifecycleController controller, Color Function(String) parseColor) {
|
||||
Color carColor = parseColor(controller.colorHex);
|
||||
+ final String vehicleText =
|
||||
+ '${controller.model} ${controller.make}'.toLowerCase();
|
||||
+ final bool isBike = vehicleText.contains('scooter') ||
|
||||
+ vehicleText.contains('bike') ||
|
||||
+ vehicleText.contains('دراجة');
|
||||
return Container(
|
||||
height: 40, // تصغير من 50
|
||||
width: 40,
|
||||
@@ -331,7 +366,8 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
child: ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(carColor, BlendMode.srcIn),
|
||||
child: Image.asset(
|
||||
- box.read(BoxName.carType) == 'Scooter' ||
|
||||
+ isBike ||
|
||||
+ box.read(BoxName.carType) == 'Scooter' ||
|
||||
box.read(BoxName.carType) == 'Pink Bike'
|
||||
? 'assets/images/moto.png'
|
||||
: 'assets/images/car3.png',
|
||||
@@ -341,6 +377,81 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
+ Widget _buildDriverBadge({
|
||||
+ required IconData icon,
|
||||
+ required String text,
|
||||
+ required Color color,
|
||||
+ }) {
|
||||
+ return Container(
|
||||
+ padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
+ decoration: BoxDecoration(
|
||||
+ color: color.withOpacity(0.1),
|
||||
+ borderRadius: BorderRadius.circular(10),
|
||||
+ ),
|
||||
+ child: Row(
|
||||
+ mainAxisSize: MainAxisSize.min,
|
||||
+ children: [
|
||||
+ Icon(icon, size: 11, color: color),
|
||||
+ const SizedBox(width: 4),
|
||||
+ Text(
|
||||
+ text,
|
||||
+ style: TextStyle(
|
||||
+ color: color,
|
||||
+ fontSize: 10.5,
|
||||
+ fontWeight: FontWeight.w800,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ],
|
||||
+ ),
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ void _showDriverAvatarDialog(
|
||||
+ BuildContext context, RideLifecycleController controller) {
|
||||
+ final imageUrl =
|
||||
+ '${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg';
|
||||
+ Get.dialog(
|
||||
+ Dialog(
|
||||
+ insetPadding: const EdgeInsets.symmetric(horizontal: 38),
|
||||
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
|
||||
+ child: Padding(
|
||||
+ padding: const EdgeInsets.fromLTRB(18, 20, 18, 18),
|
||||
+ child: Column(
|
||||
+ mainAxisSize: MainAxisSize.min,
|
||||
+ children: [
|
||||
+ CircleAvatar(
|
||||
+ radius: 58,
|
||||
+ backgroundColor: Colors.grey[200],
|
||||
+ backgroundImage: NetworkImage(imageUrl),
|
||||
+ onBackgroundImageError: (_, __) {},
|
||||
+ ),
|
||||
+ const SizedBox(height: 14),
|
||||
+ Text(
|
||||
+ controller.driverName,
|
||||
+ textAlign: TextAlign.center,
|
||||
+ style: AppStyle.title.copyWith(
|
||||
+ fontSize: 18,
|
||||
+ fontWeight: FontWeight.w900,
|
||||
+ ),
|
||||
+ ),
|
||||
+ const SizedBox(height: 6),
|
||||
+ Text(
|
||||
+ '${controller.driverTier.tr} • ${controller.driverRate}',
|
||||
+ textAlign: TextAlign.center,
|
||||
+ style: TextStyle(
|
||||
+ color: Colors.grey[700],
|
||||
+ fontSize: 13,
|
||||
+ fontWeight: FontWeight.w600,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+ barrierDismissible: true,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
Widget _buildSlimLicensePlate(String plateNumber) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
Reference in New Issue
Block a user