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:35:29 +03:00
parent 8f555691b9
commit cbf693c804
56 changed files with 6091 additions and 1217 deletions

View File

@@ -23,49 +23,59 @@ class GoogleDriverMap extends StatelessWidget {
final double mapPaddingBottom = MediaQuery.of(context).size.height * 0.3;
return GetBuilder<MapDriverController>(
builder: (controller) => IntaleqMap(
apiKey: AK.mapAPIKEY,
onMapCreated: (mapController) {
controller.onMapCreated(mapController);
},
mapType: Get.isRegistered<SettingController>()
? (Get.find<SettingController>().isMapDarkMode
? IntaleqMapType.normal
: IntaleqMapType.light)
: IntaleqMapType.light,
zoomControlsEnabled: false,
initialCameraPosition: CameraPosition(
target: locationController.myLocation,
zoom: 17,
bearing: locationController.heading,
tilt: 60,
builder: (controller) => Listener(
onPointerDown: (_) => controller.onUserMapInteraction(),
child: IntaleqMap(
apiKey: AK.mapAPIKEY,
onMapCreated: (mapController) {
controller.onMapCreated(mapController);
},
mapType: Get.isRegistered<SettingController>()
? (Get.find<SettingController>().isMapDarkMode
? IntaleqMapType.normal
: IntaleqMapType.light)
: IntaleqMapType.light,
zoomControlsEnabled: false,
initialCameraPosition: CameraPosition(
target: controller.smoothedLocation ?? locationController.myLocation,
zoom: 17,
bearing: controller.smoothedHeading,
tilt: 60,
),
// padding: EdgeInsets.only(bottom: 50, top: Get.height * 0.7),
// minMaxZoomPreference: const MinMaxZoomPreference(8, 18),
myLocationEnabled: false,
myLocationButtonEnabled: false,
compassEnabled: false,
polylines: controller.polyLines.toSet(),
markers: {
// 🔥 Car icon — always visible, moves with GPS location on map.
// MarkerId matches exactly with updateMarker() in controller.
Marker(
markerId: const MarkerId('MyLocation'),
position: controller.smoothedLocation ?? controller.myLocation,
rotation: controller.smoothedHeading,
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
zIndex: 100,
),
if (!controller.isRideStarted &&
controller.latLngPassengerLocation.latitude != 0)
Marker(
markerId: const MarkerId('start'),
position: controller.latLngPassengerLocation,
icon: controller.startIcon,
),
if (controller.latLngPassengerDestination.latitude != 0 ||
controller.latLngPassengerDestination.longitude != 0)
Marker(
markerId: const MarkerId('end'),
position: controller.latLngPassengerDestination,
icon: controller.endIcon,
),
},
),
// padding: EdgeInsets.only(bottom: 50, top: Get.height * 0.7),
// minMaxZoomPreference: const MinMaxZoomPreference(8, 18),
myLocationEnabled: false,
myLocationButtonEnabled: true,
compassEnabled: true,
polylines: controller.polyLines.toSet(),
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: controller.smoothedLocation ?? locationController.myLocation,
rotation: controller.smoothedHeading,
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
),
Marker(
markerId: MarkerId('start'.tr),
position: controller.latLngPassengerLocation,
icon: controller.startIcon,
),
Marker(
markerId: MarkerId('end'.tr),
position: controller.latLngPassengerDestination,
icon: controller.endIcon,
),
},
),
);
}

View File

@@ -2,11 +2,19 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/style.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import '../../../../controller/voice_call_controller.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/functions/location_controller.dart';
import '../../../../main.dart';
import '../../../widgets/error_snakbar.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../constant/links.dart';
import '../../../widgets/my_textField.dart';
class PassengerInfoWindow extends StatelessWidget {
const PassengerInfoWindow({super.key});
@@ -132,12 +140,19 @@ class PassengerInfoWindow extends StatelessWidget {
Icon(Icons.location_on,
size: 14, color: Colors.grey[600]),
const SizedBox(width: 4),
Text(
'${controller.distance} km',
style: TextStyle(
color: Colors.grey[700],
fontSize: 13,
fontWeight: FontWeight.w600),
// 🔥 [Fix Overflow] Flexible لمنع الـ overflow + تحويل المسافة
// السيرفر يُرجع المسافة بالأمتار (5864.022)
Flexible(
child: Text(
_formatDistanceDisplay(
controller.distance),
style: TextStyle(
color: Colors.grey[700],
fontSize: 13,
fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
const SizedBox(width: 10),
Icon(Icons.access_time_filled,
@@ -172,8 +187,8 @@ class PassengerInfoWindow extends StatelessWidget {
await controller.driverCallPassenger();
if (canCall) {
makePhoneCall(
controller.passengerPhone.toString());
_showCallSelectionDialog(
context, controller);
} else {
// هنا ممكن تظهر رسالة: تم منع الاتصال بسبب كثرة الإلغاءات
mySnackeBarError(
@@ -194,6 +209,26 @@ class PassengerInfoWindow extends StatelessWidget {
color: Colors.green, size: 22),
),
),
const SizedBox(width: 8),
InkWell(
onTap: () =>
_showMessageOptions(context, controller),
borderRadius: BorderRadius.circular(50),
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.grey.shade100,
shape: BoxShape.circle,
border:
Border.all(color: Colors.grey.shade300),
),
child: Icon(
MaterialCommunityIcons
.message_text_outline,
color: AppColor.primaryColor,
size: 22),
),
),
],
),
],
@@ -372,13 +407,40 @@ class PassengerInfoWindow extends StatelessWidget {
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
onPressed: () {
MyDialog().getDialog(
"Start Trip?".tr,
"Ensure the passenger is in the car.".tr,
() async {
await controller.startRideFromDriver();
Get.back();
},
// 🔥 [Fix Start-Ride] استخدام Get.defaultDialog بدلاً من MyDialog
// لأن MyDialog يستخدم Navigator.of(context, rootNavigator: true).pop()
// الذي يتعارض مع Get.dialog() المستخدم في startRideFromDriver()
// وقد يُسبب Get.back() اللاحق إغلاق صفحة الماب بدلاً من الـ loading dialog
Get.defaultDialog(
title: "Start Trip?".tr,
titleStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
middleText: "Ensure the passenger is in the car.".tr,
barrierDismissible: true,
radius: 14,
confirm: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF27AE60),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
onPressed: () async {
// نُغلق الديالوج بـ Get.back() لضمان أن GetX يعرف أنه أُغلق
Get.back();
// ثم نُنفذ startRideFromDriver الذي يستخدم Get.dialog و Get.back بأمان
await controller.startRideFromDriver();
},
child: Text('Start'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
),
cancel: TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr,
style: const TextStyle(color: Colors.grey)),
),
);
},
icon: const Icon(Icons.play_circle_fill_rounded),
@@ -389,4 +451,167 @@ class PassengerInfoWindow extends StatelessWidget {
);
}
}
void _showCallSelectionDialog(
BuildContext context, MapDriverController controller) {
Get.dialog(
Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Call Options'.tr,
style: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(
'Choose how you want to call the passenger'.tr,
style: const TextStyle(color: Colors.grey, fontSize: 14),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green.withOpacity(0.1),
child: const Icon(Icons.phone_android_rounded,
color: Colors.green),
),
title: Text('Standard Call'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text('Uses cellular network'.tr,
style: const TextStyle(fontSize: 12)),
onTap: () {
Get.back();
makePhoneCall(controller.passengerPhone.toString());
},
),
const Divider(),
ListTile(
leading: CircleAvatar(
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
child: Icon(Icons.wifi_calling_3_rounded,
color: AppColor.primaryColor),
),
title: Text('Free Call'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text('Voice call over internet'.tr,
style: const TextStyle(fontSize: 12)),
onTap: () {
Get.back();
final voiceCtrl = Get.find<VoiceCallController>();
final driverId = box.read(BoxName.driverID).toString();
voiceCtrl.startCall(
rideIdVal: controller.rideId,
driverId: driverId,
passengerId: controller.passengerId,
remoteNameVal: controller.passengerName ?? "Passenger",
);
},
),
],
),
),
),
);
}
void _showMessageOptions(
BuildContext context, MapDriverController controller) {
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Quick Messages'.tr,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
_buildQuickMessageItem("Where are you, sir?".tr, controller),
_buildQuickMessageItem("I've arrived.".tr, controller),
const Divider(),
Row(
children: [
Expanded(
child: TextField(
controller: controller.messageToPassenger,
decoration:
InputDecoration(hintText: 'Type a message...'.tr),
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () {
_sendMessage(controller, controller.messageToPassenger.text,
'cancel');
controller.messageToPassenger.clear();
Get.back();
},
),
],
),
],
),
),
);
}
Widget _buildQuickMessageItem(String text, MapDriverController controller) {
return ListTile(
title: Text(text),
onTap: () {
_sendMessage(controller, text, 'ding');
Get.back();
},
);
}
void _sendMessage(
MapDriverController controller, String body, String tone) async {
try {
await CRUD().post(
link: AppLink.sendChatMessage,
payload: {
'ride_id': controller.rideId.toString(),
'sender_id': box.read(BoxName.driverID).toString(),
'receiver_id': controller.passengerId.toString(),
'sender_type': 'driver',
'message_content': body,
},
);
} catch (e) {
// Ignore or log error
}
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'Driver Message'.tr,
body: body,
isTopic: false,
tone: tone,
driverList: [],
category: 'message From Driver',
);
}
}
/// تحويل المسافة من الأمتار إلى عرض مقروء
/// السيرفر يُرجع المسافة بالأمتار (مثال: 5864.022)
/// النتيجة: "5.9 km" أو "250 م"
String _formatDistanceDisplay(String rawDistance) {
final meters = double.tryParse(rawDistance) ?? 0.0;
if (meters >= 1000) {
return '${(meters / 1000).toStringAsFixed(1)} km';
} else if (meters > 0) {
return '${meters.toStringAsFixed(0)} م';
}
return rawDistance; // fallback للقيمة الأصلية
}

View File

@@ -21,14 +21,10 @@ class SosConnect extends StatelessWidget {
return GetBuilder<MapDriverController>(
id: 'SosConnect', // Keep ID for updates
builder: (controller) {
// Check visibility logic
bool showPassengerContact =
!controller.isRideBegin && controller.isPassengerInfoWindow;
bool showSos = controller.isRideStarted;
if (!showPassengerContact && !showSos) return const SizedBox();
if (!showSos) return const SizedBox();
// REMOVED: Positioned widget
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
@@ -45,46 +41,15 @@ class SosConnect extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// === Call Button ===
if (showPassengerContact)
_buildModernActionButton(
icon: Icons.phone_in_talk,
color: Colors.white,
bgColor: AppColor.blueColor,
tooltip: 'Call Passenger',
onTap: () async {
controller.isSocialPressed = true;
bool canCall = await controller.driverCallPassenger();
if (canCall) {
makePhoneCall(controller.passengerPhone.toString());
} else {
mySnackeBarError("Policy restriction on calls".tr);
}
},
),
if (showPassengerContact) const SizedBox(height: 12),
// === Message Button ===
if (showPassengerContact)
_buildModernActionButton(
icon: MaterialCommunityIcons.message_text_outline,
color: AppColor.primaryColor,
bgColor: Colors.grey.shade100,
tooltip: 'Message Passenger',
onTap: () => _showMessageOptions(context, controller),
),
// === SOS Button ===
if (showSos)
_buildModernActionButton(
icon: MaterialIcons.warning,
color: Colors.white,
bgColor: AppColor.redColor,
tooltip: 'EMERGENCY SOS',
isPulsing: true,
onTap: () => _handleSosCall(controller),
),
_buildModernActionButton(
icon: MaterialIcons.warning,
color: Colors.white,
bgColor: AppColor.redColor,
tooltip: 'EMERGENCY SOS',
isPulsing: true,
onTap: () => _handleSosCall(controller),
),
],
),
);
@@ -140,7 +105,7 @@ class SosConnect extends StatelessWidget {
child: MyTextForm(
controller: mapDriverController.sosEmergincyNumberCotroller,
label: 'Phone Number'.tr,
hint: '01xxxxxxxxx',
hint: '0923456789',
type: TextInputType.phone,
),
),
@@ -163,71 +128,4 @@ class SosConnect extends StatelessWidget {
launchCommunication('phone', box.read(BoxName.sosPhoneDriver), '');
}
}
void _showMessageOptions(
BuildContext context, MapDriverController controller) {
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Quick Messages'.tr,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
_buildQuickMessageItem("Where are you, sir?".tr, controller),
_buildQuickMessageItem("I've arrived.".tr, controller),
const Divider(),
Row(
children: [
Expanded(
child: TextField(
controller: controller.messageToPassenger,
decoration:
InputDecoration(hintText: 'Type a message...'.tr),
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () {
_sendMessage(controller, controller.messageToPassenger.text,
'cancel');
controller.messageToPassenger.clear();
Get.back();
},
),
],
),
],
),
),
);
}
Widget _buildQuickMessageItem(String text, MapDriverController controller) {
return ListTile(
title: Text(text),
onTap: () {
_sendMessage(controller, text, 'ding');
Get.back();
},
);
}
void _sendMessage(MapDriverController controller, String body, String tone) {
NotificationService.sendNotification(
target: controller.tokenPassenger.toString(),
title: 'Driver Message'.tr,
body: body,
isTopic: false,
tone: tone,
driverList: [],
category: 'message From Driver',
);
}
}