Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps
This commit is contained in:
@@ -48,10 +48,11 @@ class PassengerLocationMapPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
|
||||
// 🔥 [Fix] argumentLoading ضرورية هنا للعودة للرحلة من صفحة الهوم
|
||||
// (عند العودة لا يُستدعى onInit() لأن الكنترولر موجود مسبقاً)
|
||||
// الحماية من التكرار موجودة داخل argumentLoading بواسطة _isRouteRequested flag
|
||||
mapDriverController.argumentLoading();
|
||||
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
|
||||
// 2. فرض التحديث لكل المعرفات (IDs) لضمان ظهورها
|
||||
// لأن argumentLoading قد تستدعي update() العادية التي لا تؤثر على هؤلاء
|
||||
mapDriverController
|
||||
.update(['PassengerInfo', 'DriverEndBar', 'SosConnect']);
|
||||
}
|
||||
@@ -152,7 +153,6 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
if (controller.currentInstruction.isEmpty) return const SizedBox();
|
||||
|
||||
return TweenAnimationBuilder<double>(
|
||||
|
||||
tween: Tween(begin: 0.0, end: 1.0),
|
||||
duration: const Duration(milliseconds: 500),
|
||||
builder: (context, value, child) {
|
||||
@@ -165,7 +165,8 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardColor.withOpacity(0.95), // Adaptive background
|
||||
color: theme.cardColor
|
||||
.withOpacity(0.95), // Adaptive background
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
@@ -173,7 +174,8 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
blurRadius: 15,
|
||||
offset: const Offset(0, 5)),
|
||||
],
|
||||
border: Border.all(color: theme.dividerColor.withOpacity(0.1)),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor.withOpacity(0.1)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -193,7 +195,7 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
Text(
|
||||
"${"NEXT STEP".tr} (${controller.distanceToNextStep})",
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.hintColor,
|
||||
@@ -204,8 +206,7 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
Text(
|
||||
controller.currentInstruction,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.2),
|
||||
fontWeight: FontWeight.w600, height: 1.2),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -213,7 +214,6 @@ class InstructionsOfRoads extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
// فاصل عمودي
|
||||
Container(
|
||||
width: 1,
|
||||
@@ -279,10 +279,11 @@ class CancelWidget extends StatelessWidget {
|
||||
color: Theme.of(context).cardColor.withOpacity(0.9),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Theme.of(context).shadowColor.withOpacity(0.1), blurRadius: 8)
|
||||
BoxShadow(
|
||||
color: Theme.of(context).shadowColor.withOpacity(0.1),
|
||||
blurRadius: 8)
|
||||
],
|
||||
),
|
||||
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
@@ -388,7 +389,6 @@ class PricesWindow extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -416,7 +416,6 @@ class PricesWindow extends StatelessWidget {
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
|
||||
@@ -75,7 +75,7 @@ class HomeCaptain extends StatelessWidget {
|
||||
final LocationController locationController =
|
||||
Get.put(LocationController(), permanent: true);
|
||||
final HomeCaptainController homeCaptainController =
|
||||
Get.put(HomeCaptainController());
|
||||
Get.put(HomeCaptainController(), permanent: true);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 للقيمة الأصلية
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +229,8 @@ class _OrderOverlayState extends State<OrderOverlay>
|
||||
// بيانات أساسية
|
||||
'driver_id': driverId,
|
||||
'status': 'Apply',
|
||||
'passengerLocation': _getData(0),
|
||||
'passengerDestination': _getData(1),
|
||||
'passengerLocation': '${_getData(0)},${_getData(1)}',
|
||||
'passengerDestination': '${_getData(3)},${_getData(4)}',
|
||||
'Duration': _getData(4),
|
||||
'totalCost': _getData(26),
|
||||
'Distance': _getData(5),
|
||||
|
||||
Reference in New Issue
Block a user