26-1-20/1

This commit is contained in:
Hamza-Ayed
2026-01-20 10:11:10 +03:00
parent 374f9e9bf3
commit 3c0ae4cf2f
53 changed files with 89652 additions and 6861 deletions

View File

@@ -0,0 +1,142 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/services.dart';
class MarkerGenerator {
// دالة لرسم ماركر يحتوي على نص (للوقت والمسافة)
static Future<BitmapDescriptor> createCustomMarkerBitmap({
required String title,
required String subtitle,
required Color color,
required IconData iconData,
}) async {
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
// إعدادات القياسات
const double width = 220.0;
const double height = 110.0;
const double circleRadius = 25.0;
// 1. رسم المربع (Info Box)
final Paint paint = Paint()..color = color;
final RRect rRect = RRect.fromRectAndRadius(
const Rect.fromLTWH(0, 0, width, 60),
const Radius.circular(15),
);
// ظل خفيف
canvas.drawShadow(Path()..addRRect(rRect), Colors.black, 5.0, true);
canvas.drawRRect(rRect, paint);
// 2. رسم مثلث صغير أسفل المربع (Arrow Tail)
final Path path = Path();
path.moveTo(width / 2 - 10, 60);
path.lineTo(width / 2, 75);
path.lineTo(width / 2 + 10, 60);
path.close();
canvas.drawPath(path, paint);
// 3. رسم الدائرة (مكان الأيقونة)
canvas.drawCircle(const Offset(width / 2, 85), circleRadius, paint);
// 4. رسم الأيقونة داخل الدائرة
TextPainter iconPainter = TextPainter(textDirection: TextDirection.ltr);
iconPainter.text = TextSpan(
text: String.fromCharCode(iconData.codePoint),
style: TextStyle(
fontSize: 30.0,
fontFamily: iconData.fontFamily,
color: Colors.white,
),
);
iconPainter.layout();
iconPainter.paint(
canvas,
Offset((width - iconPainter.width) / 2, 85 - (iconPainter.height / 2)),
);
// 5. رسم النصوص (العنوان والوصف) داخل المربع
// العنوان (مثلاً: المدة)
TextPainter titlePainter = TextPainter(
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
);
titlePainter.text = TextSpan(
text: title,
style: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
);
titlePainter.layout(minWidth: width);
titlePainter.paint(canvas, const Offset(0, 8));
// الوصف (مثلاً: المسافة)
TextPainter subTitlePainter = TextPainter(
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
);
subTitlePainter.text = TextSpan(
text: subtitle,
style: const TextStyle(
fontSize: 16.0,
color: Colors.white70,
),
);
subTitlePainter.layout(minWidth: width);
subTitlePainter.paint(canvas, const Offset(0, 32));
// تحويل الرسم إلى صورة
final ui.Image image = await pictureRecorder.endRecording().toImage(
width.toInt(),
(height + 20).toInt(), // مساحة إضافية
);
final ByteData? data =
await image.toByteData(format: ui.ImageByteFormat.png);
return BitmapDescriptor.fromBytes(data!.buffer.asUint8List());
}
// دالة خاصة لرسم ماركر السائق (دائرة وخلفها سهم)
static Future<BitmapDescriptor> createDriverMarker() async {
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
const double size = 100.0;
final Paint paint = Paint()..color = const Color(0xFF2E7D32); // أخضر غامق
final Paint borderPaint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 4.0;
// الدائرة
canvas.drawCircle(const Offset(size / 2, size / 2), size / 2.5, paint);
canvas.drawCircle(
const Offset(size / 2, size / 2), size / 2.5, borderPaint);
// رسم السهم (Arrow Up)
TextPainter iconPainter = TextPainter(textDirection: TextDirection.ltr);
iconPainter.text = TextSpan(
text: String.fromCharCode(Icons.navigation.codePoint), // سهم ملاحة
style: TextStyle(
fontSize: 40.0,
fontFamily: Icons.navigation.fontFamily,
color: Colors.white,
),
);
iconPainter.layout();
iconPainter.paint(
canvas,
Offset((size - iconPainter.width) / 2, (size - iconPainter.height) / 2),
);
final ui.Image image = await pictureRecorder
.endRecording()
.toImage(size.toInt(), size.toInt());
final ByteData? data =
await image.toByteData(format: ui.ImageByteFormat.png);
return BitmapDescriptor.fromBytes(data!.buffer.asUint8List());
}
}

View File

@@ -1,10 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:just_audio/just_audio.dart';
import 'package:sefer_driver/constant/api_key.dart';
import 'package:sefer_driver/models/overlay_service.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart';
import '../../../../controller/firebase/firbase_messge.dart';
@@ -294,6 +296,14 @@ class _OrderOverlayState extends State<OrderOverlay>
'ding',
'',
);
// 3. الخطوة الأهم: فتح التطبيق وإغلاق النافذة
try {
// استدعاء الميثود التي تم تحديثها في الخطوة 1
await OverlayMethodChannel.bringToForeground();
} catch (e) {
_log("Failed to bring app to foreground: $e");
}
await _closeOverlay();
} else {
_log("Failed to update order status on server: $res");
@@ -309,6 +319,12 @@ class _OrderOverlayState extends State<OrderOverlay>
_log(
"A critical error occurred during server update: $e\nStackTrace: $s");
if (mounted) setState(() => buttonsEnabled = true);
_log("Error in accept order: $e");
await _closeOverlay();
// حتى في حال الخطأ، نحاول فتح التطبيق ليرى السائق ما حدث
await OverlayMethodChannel.bringToForeground();
return;
}
}
@@ -342,7 +358,7 @@ class _OrderOverlayState extends State<OrderOverlay>
_log("Driver ID is null, cannot refuse order");
return;
}
_crud.post(link: AppLink.addDriverOrder, payload: {
CRUD().post(link: AppLink.addDriverOrder, payload: {
'driver_id': driverId,
'order_id': orderID,
'status': 'Refused'
@@ -492,6 +508,11 @@ class _OrderOverlayState extends State<OrderOverlay>
Widget _buildPrimaryInfo() {
final order = orderData!;
// FIX: Parse the price to a number safely before formatting
// This handles cases where order.price is a String like "173"
final num priceValue = num.tryParse(order.price.toString()) ?? 0;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
@@ -508,10 +529,8 @@ class _OrderOverlayState extends State<OrderOverlay>
Expanded(
flex: 3,
child: _buildHighlightInfo(
// التعديل هنا 👇
"${NumberFormat('#,##0').format(order.price)} ل.س",
// أو يمكنك استخدام "SYP" بدلاً من "ل.س"
// FIX: Use the parsed priceValue here
"${NumberFormat('#,##0').format(priceValue)} ل.س",
"السعر".tr,
Icons.monetization_on_rounded,
AppColors.priceHighlight,
@@ -522,7 +541,8 @@ class _OrderOverlayState extends State<OrderOverlay>
Expanded(
flex: 2,
child: _buildHighlightInfo(
"${order.tripDistanceKm.toStringAsFixed(1)} كم",
// Ensure tripDistanceKm is treated safely too
"${(num.tryParse(order.tripDistanceKm.toString()) ?? 0).toStringAsFixed(1)} كم",
"المسافة".tr,
Icons.straighten_rounded,
AppColors.accent,

View File

@@ -1,450 +1,411 @@
import 'dart:convert';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math' as math;
import '../../../../constant/colors.dart';
import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../../../controller/firebase/notification_service.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../../print.dart';
import '../../../widgets/elevated_btn.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/home/captin/order_request_controller.dart';
class OrderRequestPage extends StatefulWidget {
class OrderRequestPage extends StatelessWidget {
const OrderRequestPage({super.key});
@override
State<OrderRequestPage> createState() => _OrderRequestPageState();
}
class _OrderRequestPageState extends State<OrderRequestPage> {
final OrderRequestController orderRequestController =
Get.put(OrderRequestController());
@override
Widget build(BuildContext context) {
// حقن الكنترولر
final OrderRequestController controller = Get.put(OrderRequestController());
return Scaffold(
appBar: AppBar(
title: Text('Order Request'.tr),
centerTitle: true,
),
body: GetBuilder<OrderRequestController>(
builder: (controller) {
if (controller.myList == null) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
SizedBox(
height: Get.height * 0.3,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
zoom: 14.0,
body: Directionality(
textDirection: TextDirection.rtl,
child: GetBuilder<OrderRequestController>(
builder: (controller) {
// 🔥 التعديل الأهم: التحقق من وجود أي بيانات (List أو Map)
if (controller.myList == null && controller.myMapData == null) {
return const Center(
child:
CircularProgressIndicator()); // شاشة تحميل بدلاً من فراغ
}
// 🔥 استخدام دوال الكنترولر الآمنة لجلب البيانات بدلاً من الوصول المباشر
// قمت بتحويل _safeGet إلى دالة عامة safeGet في الكنترولر (تأكد من جعلها public)
// أو سأقوم بكتابة المنطق هنا مباشرة لضمان العمل:
String getValue(int index) {
if (controller.myList != null &&
index < controller.myList!.length) {
return controller.myList![index].toString();
}
if (controller.myMapData != null &&
controller.myMapData!.containsKey(index.toString())) {
return controller.myMapData![index.toString()].toString();
}
return "";
}
final String passengerName =
getValue(8).isEmpty ? "عميل" : getValue(8);
final String startAddr =
getValue(29).isEmpty ? "موقع الانطلاق" : getValue(29);
final String endAddr =
getValue(30).isEmpty ? "الوجهة" : getValue(30);
final bool isVisa = (getValue(13) == 'true');
// منطق Speed = سعر ثابت
final bool isSpeed =
controller.tripType.toLowerCase().contains('speed');
final String carTypeLabel =
isSpeed ? "سعر ثابت" : controller.tripType;
final Color carTypeColor =
isSpeed ? Colors.red.shade700 : Colors.blue.shade700;
final IconData carIcon =
isSpeed ? Icons.local_offer : Icons.directions_car;
return Stack(
children: [
// 1. الخارطة
Positioned.fill(
bottom: 300,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(
controller.latPassenger, controller.lngPassenger),
zoom: 13.0,
),
markers: controller.markers,
polylines: controller.polylines,
zoomControlsEnabled: false,
myLocationButtonEnabled: false,
compassEnabled: false,
padding: const EdgeInsets.only(
top: 80, bottom: 20, left: 20, right: 20),
onMapCreated: (c) {
controller.onMapCreated(c);
controller.update();
},
),
myLocationButtonEnabled: true,
onMapCreated: controller.onMapCreated,
myLocationEnabled: true,
markers: {
Marker(
markerId: const MarkerId('startLocation'),
position: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
icon: controller.startIcon,
),
Marker(
markerId: const MarkerId('destinationLocation'),
position: LatLng(controller.latPassengerDestination,
controller.lngPassengerDestination),
icon: controller.endIcon,
),
},
polylines: {
Polyline(
polylineId: const PolylineId('route'),
color: AppColor.primaryColor,
width: 5,
points: controller.pointsDirection,
),
},
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
elevation: 4,
child: ListTile(
leading: Icon(
controller.myList[13].toString() == 'true'
? Icons.credit_card
: Icons.money,
color: controller.myList[13].toString() == 'true'
? AppColor.deepPurpleAccent
: AppColor.greenColor,
),
title: Text(
'Payment Method'.tr,
style: Theme.of(context).textTheme.titleMedium,
),
trailing: Text(
controller.myList[13].toString() == 'true'
? 'Visa'
: 'Cash',
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
// 2. كبسولة الوصول للراكب
Positioned(
top: 50,
left: 0,
right: 0,
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 8),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(color: Colors.black26, blurRadius: 8)
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.near_me,
color: Colors.amber, size: 16),
const SizedBox(width: 8),
Text(
"الوصول للراكب: ${controller.timeToPassenger}",
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13),
),
],
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: ListTile(
leading: const Icon(Icons.account_circle,
color: AppColor.secondaryColor),
title: Text(
controller.myList[8],
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Row(
children: [
const Icon(Icons.star,
size: 16, color: Colors.amber),
Text(
controller.myList[33].toString(),
style: const TextStyle(color: Colors.amber),
),
],
),
),
),
// 3. البطاقة السفلية
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 360,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 20,
spreadRadius: 5)
],
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.location_on,
color: AppColor.greenColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[29],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
const Divider(),
Row(
children: [
const Icon(Icons.flag,
color: AppColor.redColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[30],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
],
),
),
),
const SizedBox(height: 10),
// Card(
// elevation: 4,
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// _InfoTile(
// icon: Icons.timer,
// label:
// '${(double.parse(controller.myList[12]) / 60).toStringAsFixed(0)} ${'min'.tr}',
// ),
// _InfoTile(
// icon: Icons.directions_car,
// label:
// '${(double.parse(controller.myList[11]) / 1000).toStringAsFixed(1)} ${'km'.tr}',
// ),
// _InfoTile(
// icon: Icons.monetization_on,
// label: '${controller.myList[2]}',
// ),
// ],
// ),
// ),
// ),
// استبدل هذا الكود
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_InfoTile(
icon: Icons.timer,
label:
// استخدم الفهرس 13 للوقت (Duration)
'${((double.tryParse(controller.myList[13].toString()) ?? 0.0) / 60).toStringAsFixed(0)} ${'min'.tr}',
),
_InfoTile(
icon: Icons.directions_car,
label:
// استخدم الفهرس 14 للمسافة (Distance)
// استخدم tryParse للأمان لأن القيمة "" (نص فارغ)
'${((double.tryParse(controller.myList[14].toString()) ?? 0.0) / 1000).toStringAsFixed(1)} ${'km'.tr}',
),
_InfoTile(
icon: Icons.monetization_on,
label:
// السعر أصبح في الفهرس 4
'${controller.myList[4]}',
),
],
),
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
padding: const EdgeInsets.fromLTRB(20, 10, 20, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Accept Order'.tr,
onPressed: () async {
var res = await CRUD().post(
link:
"${AppLink.ride}/rides/updateStausFromSpeed.php",
payload: {
'id': (controller.myList[16]),
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2)))),
const SizedBox(height: 15),
if (res == 'failure') {
MyDialog().getDialog(
"This ride is already applied by another driver."
.tr,
'', () {
Get.back();
Get.back();
});
} else {
Get.put(HomeCaptainController()).changeRideId();
box.write(BoxName.statusDriverLocation, 'on');
controller.endTimer();
controller.changeApplied();
CRUD().postFromDialogue(
link: AppLink.addDriverOrder,
payload: {
'driver_id':
(controller.myList[6].toString()),
'order_id':
(controller.myList[16].toString()),
'status': 'Apply'
});
List<String> bodyToPassenger = [
controller.myList[6].toString(),
controller.myList[8].toString(),
controller.myList[9].toString(),
];
NotificationService.sendNotification(
target: controller.myList[9].toString(),
title: "Accepted Ride".tr,
body: 'your ride is Accepted'.tr,
isTopic: false, // Important: this is a token
tone: 'start',
driverList: bodyToPassenger,
category: 'Accepted Ride',
);
Get.back();
box.write(BoxName.rideArguments, {
'passengerLocation':
controller.myList[0].toString(),
'passengerDestination':
controller.myList[1].toString(),
'Duration': controller.myList[4].toString(),
'totalCost': controller.myList[26].toString(),
'Distance': controller.myList[5].toString(),
'name': controller.myList[8].toString(),
'phone': controller.myList[10].toString(),
'email': controller.myList[28].toString(),
'WalletChecked':
controller.myList[13].toString(),
'tokenPassenger':
controller.myList[9].toString(),
'direction':
'https://www.google.com/maps/dir/${controller.myList[0]}/${controller.myList[1]}/',
'DurationToPassenger':
controller.myList[15].toString(),
'rideId': (controller.myList[16].toString()),
'passengerId':
(controller.myList[7].toString()),
'driverId': (controller.myList[18].toString()),
'durationOfRideValue':
controller.myList[19].toString(),
'paymentAmount':
controller.myList[2].toString(),
'paymentMethod':
controller.myList[13].toString() == 'true'
? 'visa'
: 'cash',
'isHaveSteps': controller.myList[20].toString(),
'step0': controller.myList[21].toString(),
'step1': controller.myList[22].toString(),
'step2': controller.myList[23].toString(),
'step3': controller.myList[24].toString(),
'step4': controller.myList[25].toString(),
'passengerWalletBurc':
controller.myList[26].toString(),
'timeOfOrder': DateTime.now().toString(),
'totalPassenger':
controller.myList[2].toString(),
'carType': controller.myList[31].toString(),
'kazan': controller.myList[32].toString(),
'startNameLocation':
controller.myList[29].toString(),
'endNameLocation':
controller.myList[30].toString(),
});
Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments));
Log.print(
'box.read(BoxName.rideArguments): ${box.read(BoxName.rideArguments)}');
}
},
),
GetBuilder<OrderRequestController>(
builder: (timerController) {
final isNearEnd = timerController.remainingTime <=
5; // Define a threshold for "near end"
return Stack(
alignment: Alignment.center,
// الصف الأول: الراكب والسعر
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
CircularProgressIndicator(
value: timerController.progress,
// Set the color based on the "isNearEnd" condition
color: isNearEnd ? Colors.red : Colors.blue,
const CircleAvatar(
radius: 24,
backgroundColor: Color(0xFFF5F5F5),
child: Icon(Icons.person,
color: Colors.grey, size: 28),
),
Text(
'${timerController.remainingTime}',
style: AppStyle.number,
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(passengerName,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold)),
Row(
children: [
const Icon(Icons.star,
color: Colors.amber, size: 14),
Text(controller.passengerRating,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold)),
],
),
],
),
],
);
},
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text("${controller.tripPrice} ل.س",
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColor.primaryColor)),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: isVisa
? Colors.purple.withOpacity(0.1)
: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(4)),
child: Row(
children: [
Text(isVisa ? "فيزا" : "كاش",
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: isVisa
? Colors.purple
: Colors.green)),
const SizedBox(width: 4),
Icon(
isVisa
? Icons.credit_card
: Icons.money,
size: 14,
color: isVisa
? Colors.purple
: Colors.green),
],
),
),
],
),
],
),
MyElevatedButton(
title: 'Refuse Order'.tr,
onPressed: () async {
controller.endTimer();
// List<String> bodyToPassenger = [
// box.read(BoxName.driverID).toString(),
// box.read(BoxName.nameDriver).toString(),
// box.read(BoxName.tokenDriver).toString(),
// ];
// NotificationService.sendNotification(
// target: controller.myList[9].toString(),
// title: 'Order Under Review'.tr,
// body:
// '${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
// isTopic: false, // Important: this is a token
// tone: 'start',
// driverList: bodyToPassenger,
// category: 'Order Under Review',
// );
const SizedBox(height: 15),
// controller.refuseOrder(
// (controller.myList[16].toString()),
// );
controller.addRideToNotificationDriverString(
controller.myList[16].toString(),
controller.myList[29].toString(),
controller.myList[30].toString(),
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
'${DateTime.now().hour}:${DateTime.now().minute}',
controller.myList[2].toString(),
controller.myList[7].toString(),
'wait',
controller.myList[31].toString(),
controller.myList[33].toString(),
controller.myList[2].toString(),
controller.myList[5].toString(),
controller.myList[4].toString());
},
kolor: AppColor.redColor,
// الصف الثاني: شريط المعلومات
Container(
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 10),
decoration: BoxDecoration(
color: const Color(0xFFF8F9FA),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoItem(
carIcon, carTypeLabel, carTypeColor),
Container(
height: 20,
width: 1,
color: Colors.grey.shade300),
_buildInfoItem(Icons.route,
controller.totalTripDistance, Colors.black87),
Container(
height: 20,
width: 1,
color: Colors.grey.shade300),
_buildInfoItem(Icons.access_time_filled,
controller.totalTripDuration, Colors.black87),
],
),
),
const SizedBox(height: 20),
// الصف الثالث: العناوين
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
const Icon(Icons.my_location,
size: 18, color: Colors.green),
Expanded(
child: Container(
width: 2,
color: Colors.grey.shade300,
margin: const EdgeInsets.symmetric(
vertical: 2))),
const Icon(Icons.location_on,
size: 18, color: Colors.red),
],
),
const SizedBox(width: 15),
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Text("موقع الانطلاق",
style: TextStyle(
fontSize: 11,
color: Colors.grey)),
Text(startAddr,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600),
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
const Spacer(),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Text("الوجهة",
style: TextStyle(
fontSize: 11,
color: Colors.grey)),
Text(endAddr,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600),
maxLines: 1,
overflow: TextOverflow.ellipsis),
],
),
],
),
)
],
),
),
const SizedBox(height: 15),
// الصف الرابع: الأزرار
Row(
children: [
InkWell(
onTap: () => Get.back(),
child: Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.red.shade50,
shape: BoxShape.circle,
border:
Border.all(color: Colors.red.shade100)),
child: const Icon(Icons.close,
color: Colors.red, size: 24),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: () => controller.acceptOrder(),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
foregroundColor: Colors.white,
padding:
const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
elevation: 2,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("قبول الرحلة",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold)),
const SizedBox(width: 15),
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
value: controller.progress,
color: Colors.white,
strokeWidth: 2.5,
backgroundColor: Colors.white24),
),
const SizedBox(width: 8),
Text("${controller.remainingTime}",
style: const TextStyle(
fontSize: 14, color: Colors.white)),
],
),
),
),
],
),
],
),
],
),
),
),
],
);
},
],
);
},
),
),
);
}
}
class _InfoTile extends StatelessWidget {
final IconData icon;
final String label;
const _InfoTile({required this.icon, required this.label});
@override
Widget build(BuildContext context) {
return Column(
Widget _buildInfoItem(IconData icon, String text, Color color) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.primaryColor),
const SizedBox(height: 4),
Text(
label,
style: Theme.of(context).textTheme.bodyMedium,
),
Icon(icon, size: 16, color: color),
const SizedBox(width: 6),
Text(text,
style: TextStyle(
fontSize: 13, fontWeight: FontWeight.bold, color: color)),
],
);
}

File diff suppressed because it is too large Load Diff