25-12-1/1

This commit is contained in:
Hamza-Ayed
2025-12-01 07:53:52 +03:00
parent 1a0bf1ee32
commit 11dfe94bbb
49 changed files with 19013 additions and 15915 deletions

View File

@@ -1,286 +1,412 @@
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/links.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/firebase/firbase_messge.dart';
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart'; // لتنسيق الأرقام
import '../../../constant/box_name.dart';
import '../../../controller/firebase/notification_service.dart';
import '../../../controller/functions/launch.dart';
import '../../../main.dart';
import '../../widgets/my_textField.dart';
class ApplyOrderWidget extends StatelessWidget {
ApplyOrderWidget({super.key});
final firebaseMessagesController =
Get.isRegistered<FirebaseMessagesController>()
? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController());
const ApplyOrderWidget({super.key});
@override
Widget build(BuildContext context) {
Color _parseColor(String colorHex) {
// دالة لتحويل كود اللون الهيكس إلى لون
Color parseColor(String colorHex) {
if (colorHex.isEmpty) return Colors.grey;
String processedHex = colorHex.replaceFirst('#', '0xff').trim();
return Color(int.parse(processedHex.startsWith('0xff')
? processedHex
: '0xff$processedHex'));
try {
String processedHex = colorHex.replaceFirst('#', '').trim();
if (processedHex.length == 6) processedHex = 'FF$processedHex';
return Color(int.parse('0x$processedHex'));
} catch (e) {
return Colors.grey;
}
}
return GetBuilder<MapPassengerController>(builder: (controller) {
Get.put(
FirebaseMessagesController()); // Ensure FirebaseMessagesController is initialized
if (controller.statusRide == 'Apply' && !controller.isSearchingWindow) {
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
// More modern BoxDecoration
color: Theme.of(context).cardColor,
borderRadius:
const BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black12)],
),
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildPriceInfo(context, controller),
const SizedBox(height: 16),
_buildDriverInfoCard(context, controller, _parseColor),
],
),
return Obx(() {
final controller = Get.find<MapPassengerController>();
final bool isVisible =
controller.currentRideState.value == RideState.driverApplied ||
controller.currentRideState.value == RideState.driverArrived;
return AnimatedPositioned(
duration: const Duration(milliseconds: 500),
curve: Curves.elasticOut, // تأثير حركي أجمل
bottom: isVisible ? 0 : -Get.height * 0.6,
left: 0,
right: 0,
child: Container(
// height: Get.height * 0.38, // زيادة الارتفاع قليلاً للتصميم الجديد
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
boxShadow: [
BoxShadow(
blurRadius: 20,
spreadRadius: 2,
color: Colors.black.withOpacity(0.15),
offset: const Offset(0, -2),
)
],
),
);
} else {
return const SizedBox();
}
padding: const EdgeInsets.fromLTRB(20, 10, 20, 20),
child: GetBuilder<MapPassengerController>(
builder: (c) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// مقبض صغير في الأعلى
Container(
width: 40,
height: 5,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
),
const SizedBox(height: 15),
// السعر والعنوان
_buildPriceHeader(context, c),
const SizedBox(height: 15),
// كرت المعلومات الرئيسي (سائق + سيارة)
_buildMainInfoCard(context, c, parseColor),
const SizedBox(height: 15),
// أزرار الاتصال
_buildContactButtonsRow(context, c),
const SizedBox(height: 15),
// شريط الوقت
c.currentRideState.value == RideState.driverArrived
? const DriverArrivePassengerAndWaitMinute()
: const TimeDriverToPassenger(),
],
);
},
),
),
);
});
}
Widget _buildPriceInfo(
// ---------------------------------------------------------------------------
// 1. قسم السعر (مع التنسيق الجديد)
// ---------------------------------------------------------------------------
Widget _buildPriceHeader(
BuildContext context, MapPassengerController controller) {
return InkWell(
onTap: () {
String message;
if (box.read(BoxName.carType) == 'Speed' ||
box.read(BoxName.carType) == 'Awfar Car' ||
box.read(BoxName.carType) == 'Delivery') {
message =
'This ride type does not allow changes to the destination or additional stops'
.tr;
} else {
message =
'This ride type allows changes, but the price may increase'.tr;
}
Get.snackbar(
'This price is'.tr +
' ${controller.totalPassenger.toStringAsFixed(2)}'.tr,
message,
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 2),
backgroundColor:
AppColor.yellowColor.withOpacity(0.8), // More subtle background
);
},
child: Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'The driver accepted your order for'.tr} ',
style: AppStyle.title),
TextSpan(
text: controller.totalPassenger.toStringAsFixed(2),
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold, color: AppColor.redColor),
// تنسيق الرقم (مثلاً: 60,000)
final formatter = NumberFormat("#,###");
String formattedPrice = formatter.format(controller.totalPassenger);
return Column(
children: [
Text(
'Driver Accepted Request'.tr,
style: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
),
const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
formattedPrice,
style: AppStyle.title.copyWith(
fontSize: 28,
fontWeight: FontWeight.w900,
color: AppColor.primaryColor,
),
TextSpan(text: ' ${'LE'.tr}', style: AppStyle.title),
],
),
const SizedBox(width: 5),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'SYP'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
),
],
),
],
);
}
// ---------------------------------------------------------------------------
// 2. كرت المعلومات الرئيسي (السائق + السيارة 3D)
// ---------------------------------------------------------------------------
Widget _buildMainInfoCard(BuildContext context,
MapPassengerController controller, Color Function(String) parseColor) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor, // لون خلفية فاتح
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.grey.withOpacity(0.1)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// الجزء الأيسر: معلومات السائق
Expanded(
child: Row(
children: [
// صورة السائق
Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: AppColor.primaryColor, width: 2),
),
child: CircleAvatar(
radius: 26,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
onBackgroundImageError: (exception, stackTrace) =>
const Icon(Icons.person, size: 26, color: Colors.grey),
),
),
const SizedBox(width: 12),
// الاسم والتقييم والسيارة نص
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.driverName,
style: AppStyle.title.copyWith(
fontSize: 16, fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.star, color: Colors.amber, size: 16),
const SizedBox(width: 4),
Text(
controller.driverRate,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
const SizedBox(height: 4),
Text(
'${controller.model}${controller.licensePlate}',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
// الجزء الأيمن: أيقونة السيارة الـ 3D
_build3DCarIcon(controller, parseColor),
],
),
);
}
// ---------------------------------------------------------------------------
// 3. أيقونة السيارة الـ 3D (الدائرة والظلال والخلفية الذكية)
// ---------------------------------------------------------------------------
Widget _build3DCarIcon(
MapPassengerController controller, Color Function(String) parseColor) {
Color carColor = parseColor(controller.colorHex);
// تحديد سطوع لون السيارة لتحديد لون الخلفية
// إذا كانت السيارة فاتحة (أكثر من 0.6)، الخلفية تكون غامقة، والعكس
bool isCarLight = carColor.computeLuminance() > 0.6;
// ألوان الخلفية للدائرة
Color bgGradientStart =
isCarLight ? Colors.blueGrey.shade700 : Colors.grey.shade100;
Color bgGradientEnd =
isCarLight ? Colors.blueGrey.shade900 : Colors.grey.shade300;
Color borderColor = isCarLight ? Colors.blueGrey.shade600 : Colors.white;
return Container(
width: 75,
height: 75,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
shape: BoxShape.circle,
// تدرج لوني للخلفية لتبدو 3D
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [bgGradientStart, bgGradientEnd],
),
border: Border.all(color: borderColor, width: 2),
// ظلال لرفع الدائرة عن السطح
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: const Offset(4, 4),
),
BoxShadow(
color: Colors.white.withOpacity(isCarLight ? 0.1 : 0.8),
blurRadius: 10,
offset: const Offset(-4, -4),
),
],
),
child: Center(
child: ColorFiltered(
colorFilter: ColorFilter.mode(carColor, BlendMode.srcIn),
child: Image.asset(
box.read(BoxName.carType) == 'Scooter' ||
box.read(BoxName.carType) == 'Pink Bike'
? 'assets/images/moto.png'
: 'assets/images/car3.png',
fit: BoxFit.contain,
),
textAlign: TextAlign.center,
),
),
);
}
Widget _buildDriverInfoCard(BuildContext context,
MapPassengerController controller, Color Function(String) parseColor) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade200),
// ---------------------------------------------------------------------------
// 4. أزرار الاتصال (بتصميم جديد)
// ---------------------------------------------------------------------------
Widget _buildContactButtonsRow(
BuildContext context, MapPassengerController controller) {
return Row(
children: [
Expanded(
child: _buildActionButton(
label: 'Message'.tr,
icon: Icons.chat_bubble_outline_rounded,
color: AppColor.blueColor,
onTap: () => _showContactOptionsDialog(context, controller),
),
),
const SizedBox(width: 15),
Expanded(
child: _buildActionButton(
label: 'Call'.tr,
icon: Icons.phone_rounded,
color: AppColor.greenColor,
onTap: () {
HapticFeedback.heavyImpact();
makePhoneCall(controller.driverPhone);
},
),
),
],
);
}
Widget _buildActionButton({
required String label,
required IconData icon,
required Color color,
required VoidCallback onTap,
}) {
return ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
backgroundColor: color.withOpacity(0.1),
foregroundColor: color,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: Column(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(box.read(BoxName.carType.toString()),
style:
AppStyle.title.copyWith(fontWeight: FontWeight.w500)),
Row(
children: [
_buildCarDetails(context, controller),
const SizedBox(width: 10),
_buildCarImage(controller, parseColor),
],
),
],
),
),
const Divider(height: 1, thickness: 1, color: Colors.grey),
Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildDriverAvatarAndInfo(controller),
_buildContactButtons(context, controller),
],
),
),
Padding(
padding:
const EdgeInsets.only(left: 12.0, right: 12.0, bottom: 12.0),
child: controller.isDriverArrivePassenger
? const DriverArrivePassengerAndWaitMinute()
: const TimeDriverToPassenger(),
Icon(icon, size: 20),
const SizedBox(width: 8),
Text(
label,
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
);
}
Widget _buildCarDetails(
BuildContext context, MapPassengerController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(controller.model.toString(), style: AppStyle.title),
Text(controller.licensePlate.toString(),
style: Theme.of(context).textTheme.bodyMedium),
Text(controller.carColor.toString(),
style: Theme.of(context).textTheme.bodyMedium),
],
);
}
Widget _buildCarImage(
MapPassengerController controller, Color Function(String) parseColor) {
return ColorFiltered(
colorFilter:
ColorFilter.mode(parseColor(controller.colorHex), BlendMode.srcIn),
child: Image.asset(
box.read(BoxName.carType) == 'Scooter' ||
box.read(BoxName.carType) == 'Pink Bike'
? 'assets/images/moto.png'
: 'assets/images/car3.png',
height: 60,
),
);
}
Widget _buildDriverAvatarAndInfo(MapPassengerController controller) {
return Row(
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
onBackgroundImageError: (exception, stackTrace) =>
const Icon(Icons.person, size: 30, color: AppColor.blueColor),
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(controller.driverName,
style: AppStyle.title.copyWith(fontWeight: FontWeight.w500)),
Text('${controller.driverRate}',
style: const TextStyle(fontSize: 16, color: Colors.grey)),
],
),
],
);
}
Widget _buildContactButtons(
BuildContext context, MapPassengerController controller) {
return Row(
children: [
IconButton(
onPressed: () => _showContactOptionsDialog(context, controller),
icon: const Icon(Icons.message, color: AppColor.blueColor, size: 28),
),
IconButton(
onPressed: () {
HapticFeedback.heavyImpact();
makePhoneCall(controller.driverPhone);
},
icon: const Icon(Icons.call, color: AppColor.greenColor, size: 28),
),
],
);
}
// --- النوافذ المنبثقة للرسائل (نفس المنطق القديم) ---
void _showContactOptionsDialog(
BuildContext context, MapPassengerController controller) {
Get.defaultDialog(
title: 'Contact Options'.tr,
content: SizedBox(
width: 300,
height: Get.height * .4,
child: ListView(
// shrinkWrap: true,
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Quick Message'.tr, style: AppStyle.title),
const SizedBox(height: 15),
..._buildPredefinedMessages(controller),
const SizedBox(height: 8),
const Divider(height: 30),
_buildCustomMessageInput(controller, context),
SizedBox(
height: MediaQuery.of(context).viewInsets.bottom), // للكيبورد
],
),
),
isScrollControlled: true,
);
}
List<Widget> _buildPredefinedMessages(MapPassengerController controller) {
const messages = [
'Hello, I\'m at the agreed-upon location',
'My location is correct. You can search for me using the navigation app',
'I\'m waiting for you',
"How much longer will you be?",
];
return messages
.map((message) => Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: ElevatedButton(
onPressed: () {
// firebaseMessagesController.sendNotificationToDriverMAP(
// 'message From passenger',
// message.tr,
// controller.driverToken.toString(),
// [],
// 'ding',
// );
NotificationService.sendNotification(
target: controller.driverToken.toString(),
title: 'message From passenger',
body: message.tr, // Make sure to translate the message
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
);
padding: const EdgeInsets.only(bottom: 10.0),
child: InkWell(
onTap: () {
_sendMessage(controller, message.tr);
Get.back();
},
child: Text(message.tr),
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 15),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(Icons.quickreply_rounded,
size: 18, color: Colors.grey),
const SizedBox(width: 10),
Expanded(
child: Text(message.tr, style: AppStyle.subtitle)),
],
),
),
),
))
.toList();
@@ -291,45 +417,59 @@ class ApplyOrderWidget extends StatelessWidget {
return Row(
children: [
Expanded(
child: Form(
key: controller.messagesFormKey,
child: MyTextForm(
controller: controller.messageToDriver,
label: 'Send a custom message'.tr,
hint: 'Type your message'.tr,
type: TextInputType.text,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(25),
),
child: Form(
key: controller.messagesFormKey,
child: TextFormField(
controller: controller.messageToDriver,
decoration: InputDecoration(
hintText: 'Type your message...'.tr,
border: InputBorder.none,
),
),
),
),
),
IconButton(
onPressed: () {
if (controller.messagesFormKey.currentState!.validate()) {
// firebaseMessagesController.sendNotificationToDriverMAP(
// 'message From passenger',
// controller.messageToDriver.text,
// controller.driverToken,
// [],
// 'ding',
// );
NotificationService.sendNotification(
target: controller.driverToken.toString(),
title: 'message From passenger',
body: controller.messageToDriver.text,
isTopic: false, // Important: this is a token
tone: 'ding',
driverList: [],
);
controller.messageToDriver.clear();
Get.back();
}
},
icon: const Icon(Icons.send),
const SizedBox(width: 10),
CircleAvatar(
backgroundColor: AppColor.primaryColor,
child: IconButton(
onPressed: () {
if (controller.messagesFormKey.currentState!.validate()) {
_sendMessage(controller, controller.messageToDriver.text);
controller.messageToDriver.clear();
Get.back();
}
},
icon: const Icon(Icons.send_rounded, color: Colors.white, size: 20),
),
),
],
);
}
void _sendMessage(MapPassengerController controller, String text) {
NotificationService.sendNotification(
category: 'message From passenger',
target: controller.driverToken.toString(),
title: 'Message From passenger'.tr,
body: text,
isTopic: false,
tone: 'ding',
driverList: [],
);
}
}
// -----------------------------------------------------------------------------
// مؤشرات الانتظار والوقت (نفس المنطق مع تحسين بسيط في التصميم)
// -----------------------------------------------------------------------------
class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
const DriverArrivePassengerAndWaitMinute({Key? key}) : super(key: key);
@@ -338,35 +478,31 @@ class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
return GetBuilder<MapPassengerController>(builder: (controller) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Driver is waiting'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
Text(
controller.stringRemainingTimeDriverWaitPassenger5Minute,
style: const TextStyle(
fontWeight: FontWeight.bold, color: AppColor.redColor),
),
],
),
const SizedBox(height: 6),
ClipRRect(
borderRadius: BorderRadius.circular(15),
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
backgroundColor: AppColor.accentColor.withOpacity(0.3),
backgroundColor: Colors.grey[200],
color: controller.remainingTimeDriverWaitPassenger5Minute < 60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 20,
minHeight: 8,
value:
controller.progressTimerDriverWaitPassenger5Minute.toDouble(),
),
),
const SizedBox(height: 4),
Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Driver is waiting at pickup.'.tr} ',
style: AppStyle.subtitle),
TextSpan(
text: controller
.stringRemainingTimeDriverWaitPassenger5Minute,
style: AppStyle.title),
],
),
textAlign: TextAlign.center,
),
),
],
);
});
@@ -379,47 +515,37 @@ class TimeDriverToPassenger extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
return controller.isDriverInPassengerWay == false ||
controller.timeToPassengerFromDriverAfterApplied > 0
? Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: LinearProgressIndicator(
backgroundColor: AppColor.accentColor.withOpacity(0.3),
color: controller
.remainingTimeToPassengerFromDriverAfterApplied <
60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 20,
value: controller
.progressTimerToPassengerFromDriverAfterApplied
.toDouble()
.clamp(0.0, 1.0),
),
),
const SizedBox(height: 4),
Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Driver is on the way'.tr} ',
style: AppStyle.subtitle,
),
TextSpan(
text: controller.stringRemainingTimeToPassenger,
style: AppStyle.title,
),
],
),
textAlign: TextAlign.center,
),
),
],
)
: const SizedBox();
if (controller.timeToPassengerFromDriverAfterApplied <= 0) {
return const SizedBox();
}
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Driver arriving in'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
Text(
controller.stringRemainingTimeToPassenger,
style: const TextStyle(
fontWeight: FontWeight.bold, color: AppColor.primaryColor),
),
],
),
const SizedBox(height: 6),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
backgroundColor: Colors.grey[200],
color: AppColor.primaryColor,
minHeight: 8,
value: controller.progressTimerToPassengerFromDriverAfterApplied
.toDouble()
.clamp(0.0, 1.0),
),
),
],
);
});
}
}

View File

@@ -515,7 +515,7 @@ class Details extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'${'Distance is'.tr} ${controller.data[0]['distance']['text']}',
'${'Distance is'.tr} ${controller.distance.toStringAsFixed(2)} KM',
style: AppStyle.title,
),
Text(

View File

@@ -7,6 +7,7 @@ import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/home/profile/passenger_profile_page.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:intl/intl.dart';
import 'dart:ui';
import '../../../constant/info.dart';
@@ -79,9 +80,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
return GetBuilder<MapPassengerController>(builder: (controller) {
_prepareCarTypes(controller);
if (!(controller.data.isNotEmpty &&
controller.isBottomSheetShown &&
controller.rideConfirm == false)) {
if (!(controller.isBottomSheetShown) && controller.rideConfirm == false) {
return const SizedBox.shrink();
}
// Added a BackdropFilter for a modern glassmorphism effect
@@ -351,7 +350,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
color: AppColor.primaryColor, size: 16),
const SizedBox(width: 4),
Text(
'${controller.distance} ${'KM'.tr}',
'${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
style: AppStyle.subtitle.copyWith(
color: AppColor.primaryColor,
fontWeight: FontWeight.w600,
@@ -418,30 +417,54 @@ class CarDetailsTypeToChoose extends StatelessWidget {
// --- LOGIC METHODS (UNCHANGED) ---
// 1. قم بإضافة هذا السطر في أعلى الملف
String _getPassengerPriceText(
CarType carType, MapPassengerController mapPassengerController) {
// الخطوة 1: احصل على السعر كـ double أولاً
double rawPrice;
switch (carType.carType) {
case 'Comfort':
return mapPassengerController.totalPassengerComfort.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerComfort;
break;
case 'Speed':
return mapPassengerController.totalPassengerSpeed.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerSpeed;
break;
case 'Electric':
return mapPassengerController.totalPassengerElectric.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerElectric;
break;
case 'Awfar Car':
return mapPassengerController.totalPassengerBalash.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerBalash;
break;
case 'Scooter':
return mapPassengerController.totalPassengerScooter.toStringAsFixed(1);
case 'Pink Bike': // دمج الحالات المتشابهة
rawPrice = mapPassengerController.totalPassengerScooter;
break;
case 'Van':
return mapPassengerController.totalPassengerVan.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerVan;
break;
case 'Lady':
return mapPassengerController.totalPassengerLady.toStringAsFixed(1);
case 'Pink Bike':
return mapPassengerController.totalPassengerScooter.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerLady;
break;
case 'Rayeh Gai':
return mapPassengerController.totalPassengerRayehGai.toStringAsFixed(1);
rawPrice = mapPassengerController.totalPassengerRayehGai;
break;
default:
return '...';
return '...'; // إذا كان نوع السيارة غير معروف
}
// الخطوة 2: قم بإزالة الكسور العشرية
// .round() ستحول 65000.00 إلى 65000
final int roundedPrice = rawPrice.round();
// الخطوة 3: أنشئ "مُنسّق" ليضيف فواصل الآلاف
// NumberFormat.decimalPattern() يستخدم إعدادات اللغة الافتراضية للجهاز
// لوضع الفاصلة (,) أو النقطة (.) حسب الدولة
final formatter = NumberFormat.decimalPattern();
// الخطوة 4: قم بتنسيق الرقم الصحيح
// سيحول 65000 إلى "65,000"
return formatter.format(roundedPrice);
}
void _showCarDetailsDialog(

View File

@@ -137,9 +137,9 @@ class CashConfirmPageShown extends StatelessWidget {
onPressed: () {
// --- نفس منطقك القديم بالضبط ---
controller.changeCashConfirmPageShown();
controller.isSearchingWindow = true;
controller.confirmRideForAllDriverAvailable();
controller.update();
// controller.isSearchingWindow = true;
controller.startSearchingForDriver();
// controller.update();
},
);
}

View File

@@ -168,8 +168,8 @@ class _SearchFieldState extends State<_SearchField> {
icon: Icon(Icons.location_on_outlined,
color: AppColor.accentColor, size: 30),
tooltip: widget.controller.isAnotherOreder
? 'Pick destination on map'
: 'Pick on map',
? 'Pick destination on map'.tr
: 'Pick on map'.tr,
),
],
),

View File

@@ -7,8 +7,8 @@ import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/location_controller.dart';
import '../../../controller/home/device_tier.dart';
// import '../../../controller/functions/location_controller.dart'; // Un-comment if needed
// import '../../../controller/home/device_tier.dart'; // Removed to rely on Controller logic
import '../../../controller/home/map_passenger_controller.dart';
import '../../widgets/mycircular.dart';
import '../../widgets/mydialoug.dart';
@@ -17,7 +17,6 @@ class GoogleMapPassengerWidget extends StatelessWidget {
GoogleMapPassengerWidget({super.key});
final WayPointController wayPointController = Get.put(WayPointController());
final LocationController locationController = Get.find<LocationController>();
@override
Widget build(BuildContext context) {
@@ -32,24 +31,26 @@ class GoogleMapPassengerWidget extends StatelessWidget {
child: GoogleMap(
onMapCreated: controller.onMapCreated,
// ✅ حدود الكاميرا كما هي
// ✅ Camera Bounds
cameraTargetBounds: CameraTargetBounds(controller.boundsdata),
// ✅ Zoom أهدأ للأجهزة الضعيفة
// ✅ Performance: Smoother zoom limits for low-end devices
minMaxZoomPreference: controller.lowPerf
? const MinMaxZoomPreference(6, 17)
: const MinMaxZoomPreference(6, 18),
onLongPress: (argument) {
// ✅ Destination Selection on Long Press
onLongPress: (LatLng argument) {
MyDialog().getDialog('Are you want to go to this site'.tr, '',
() async {
controller.clearPolyline();
// Ensure we have car data available before routing
if (controller.dataCarsLocationByPassenger != null) {
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${argument.latitude},${argument.longitude}',
);
Get.back();
Get.back(); // Close Dialog
await controller.bottomSheet();
controller.showBottomSheet1();
} else {
@@ -59,52 +60,21 @@ class GoogleMapPassengerWidget extends StatelessWidget {
.tr,
'',
colorText: AppColor.redColor,
duration: const Duration(seconds: 11),
instantInit: true,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 5),
backgroundColor: AppColor.secondaryColor,
icon: const Icon(Icons.error, color: AppColor.redColor),
titleText: Text('Error'.tr,
style: const TextStyle(color: AppColor.redColor)),
messageText: Text(
'We Are Sorry That we dont have cars in your Location!'
.tr,
style: AppStyle.title),
icon: const Icon(Icons.error),
shouldIconPulse: true,
maxWidth: double.infinity,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
borderRadius: 8,
borderColor: AppColor.redColor,
borderWidth: 2,
backgroundColor: AppColor.secondaryColor,
leftBarIndicatorColor: AppColor.redColor,
boxShadows: [
BoxShadow(
color: Colors.black.withOpacity(0.25),
blurRadius: 4,
spreadRadius: 2,
offset: const Offset(0, 4),
),
],
backgroundGradient: const LinearGradient(
colors: [AppColor.redColor, AppColor.accentColor],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
isDismissible: true,
showProgressIndicator: false,
dismissDirection: DismissDirection.up,
snackStyle: SnackStyle.GROUNDED,
forwardAnimationCurve: Curves.easeInToLinear,
reverseAnimationCurve: Curves.easeInOut,
animationDuration: const Duration(milliseconds: 4000),
barBlur: 8,
overlayColor: AppColor.primaryColor.withOpacity(0.5),
);
}
});
},
// ✅ Hide UI elements on tap
onTap: (argument) {
controller.hidePlaces();
},
@@ -114,71 +84,66 @@ class GoogleMapPassengerWidget extends StatelessWidget {
zoom: controller.lowPerf ? 14.5 : 15,
),
// ✅ ماركرز (احرص أن الأيقونات محجّمة ومخزّنة Cache في الكنترولر)
// ✅ Markers
markers: controller.markers.toSet(),
// ✅ بوليغونز كما هي
// ✅ Polygons (e.g., University/Country borders)
polygons: controller.polygons,
// ✅ Polyline مُبسّطة للأجهزة الضعيفة (الكنترولر يجهّز مجموعة مبسطة عند lowPerf)
// ✅ Polylines: Switch to lighter version if lowPerf is detected
polylines: controller.lowPerf
? controller.polyLinesLight
.toSet() // <- استخدم مجموعة خفيفة
? controller.polyLinesLight.toSet()
: controller.polyLines.toSet(),
// ✅ دوائر خفيفة على الأجهزة الضعيفة
// circles: {
// Circle(
// circleId: const CircleId('circle_id'),
// center: controller.passengerLocation,
// radius: controller.lowPerf ? 80 : 100,
// fillColor:
// Colors.blue.withOpacity(controller.lowPerf ? 0.2 : 0.3),
// strokeColor: Colors.blue,
// strokeWidth: controller.lowPerf ? 1 : 2,
// ),
// },
// ✅ الوضع الخفيف: liteMode + تعطيل الطبقات المكلفة + خريطة Normal
// ✅ Map Type: Switch to Normal map on low-end devices to save RAM
mapType: controller.lowPerf
? MapType.normal
: (controller.mapType
? MapType.satellite
: MapType.terrain),
: MapType
.normal), // Changed terrain default to normal for better performance
// ✅ UI Settings for Performance
myLocationButtonEnabled: false,
// ⚠️ liteMode (Android فقط): فعّله على الأجهزة الضعيفة
// liteModeEnabled: controller.lowPerf,
liteModeEnabled: Platform.isAndroid ? isLowEnd() : false,
trafficEnabled: controller.mapTrafficON && !isLowEnd(),
buildingsEnabled: !isLowEnd(),
// ✅ تقليل الكلفة الرسومية
mapToolbarEnabled: false,
rotateGesturesEnabled: isLowEnd() ? false : true,
tiltGesturesEnabled: false, // تعطيل الميلان لتقليل الحمل
tiltGesturesEnabled:
false, // Disable tilt to save GPU resources
// ✅ Throttle لحركة الكاميرا على الأجهزة الضعيفة
onCameraMove: (position) {
// Lite Mode (Static image) only on very low-end Androids if needed,
// but usually handled by lowPerf logic in mapType/Traffic
liteModeEnabled: Platform.isAndroid && controller.lowPerf,
trafficEnabled: controller.mapTrafficON && !controller.lowPerf,
buildingsEnabled: !controller.lowPerf,
rotateGesturesEnabled:
!controller.lowPerf, // Disable rotation on low-end
// ✅ Camera Movement Logic
onCameraMove: (CameraPosition position) {
// 1. Always update current view target (for pickers)
controller.newMyLocation = position.target;
// 2. Handle Drag-to-Select for specific states
if (controller.startLocationFromMap == true) {
controller.newStartPointLocation = position.target;
} else if (controller.passengerStartLocationFromMap == true) {
controller.newStartPointLocation = position.target;
}
// 3. Handle Waypoints Dragging
int waypointsLength =
Get.find<WayPointController>().wayPoints.length;
if (waypointsLength > 0 &&
controller.wayPointIndex >= 0 &&
controller.wayPointIndex <
controller.placesCoordinate.length) {
controller.placesCoordinate[controller.wayPointIndex] =
'${position.target.latitude},${position.target.longitude}';
}
// 4. Throttle heavy calculations (Reverse Geocoding / API calls)
if (controller.lowPerf) {
controller.onCameraMoveThrottled(position);
} else {
// منطقك الحالي
int waypointsLength =
Get.find<WayPointController>().wayPoints.length;
int index = controller.wayPointIndex;
if (waypointsLength > 0) {
controller.placesCoordinate[index] =
'${position.target.latitude},${position.target.longitude}';
}
if (controller.startLocationFromMap == true) {
controller.newStartPointLocation = position.target;
} else if (controller.passengerStartLocationFromMap ==
true) {
controller.newStartPointLocation = position.target;
}
controller.newMyLocation = position.target;
}
},

View File

@@ -76,11 +76,11 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
tooltip: 'VIP Waiting Page',
onPressed: () => Get.to(() => VipWaittingPage()),
),
_buildMapActionButton(
icon: Octicons.ellipsis,
tooltip: 'test',
onPressed: () => Get.to(() => TestPage()),
),
// _buildMapActionButton(
// icon: Octicons.ellipsis,
// tooltip: 'test',
// onPressed: () => Get.to(() => TestPage()),
// ),
],
),
),
@@ -129,18 +129,22 @@ class TestPage extends StatelessWidget {
body: Center(
child: TextButton(
onPressed: () async {
var token = (box.read(BoxName.tokenFCM).toString());
Log.print(
'box.read(BoxName.tokenFCM).toString(): ${box.read(BoxName.tokenFCM).toString()}');
// 'e-EE5Z5Fn0x5s6EYbtgT6f:APA91bHBTxkbdljuvDF0iPhso58r7fCwGh-WcYh3CYfUJEShUKFcQf496Xc5E6LHqRFKfOQBxYrWSdLO8d9gLbL-IdgyDuZ7jNUjzvrcV_YmagDtgz7-UNw';
// 'fdN1o8akwURHj47wvShC4T:APA91bFm-mFfFjdCbHsDReN0MzPE1hiaHKtPJnzayMec6LiInjzk6YCX41SeF0T1FE7Z6d4Hjy1AkZhLIeebSgX4RrodzwSwZSH0kboTQEfqkrjrk4xw9aM';
NotificationService.sendNotification(
target: token,
title: 'Hi ,I will go now'.tr,
body: 'A passenger is waiting for you.',
isTopic: false, // Important: this is a token
tone: 'ding',
);
// var token = (box.read(BoxName.tokenFCM).toString());
// Log.print(
// 'box.read(BoxName.tokenFCM).toString(): ${box.read(BoxName.tokenFCM).toString()}');
// // 'e-EE5Z5Fn0x5s6EYbtgT6f:APA91bHBTxkbdljuvDF0iPhso58r7fCwGh-WcYh3CYfUJEShUKFcQf496Xc5E6LHqRFKfOQBxYrWSdLO8d9gLbL-IdgyDuZ7jNUjzvrcV_YmagDtgz7-UNw';
// // 'fdN1o8akwURHj47wvShC4T:APA91bFm-mFfFjdCbHsDReN0MzPE1hiaHKtPJnzayMec6LiInjzk6YCX41SeF0T1FE7Z6d4Hjy1AkZhLIeebSgX4RrodzwSwZSH0kboTQEfqkrjrk4xw9aM';
// NotificationService.sendNotification(
// target:
// 'eznj5vRWRnqwKNtKJBaYNg:APA91bHhJ2DJ1KQa3KRx6wQtX8BkFHq6I_-dXGxT16p6pnV5AwI0bWOeiTJOI35VfTBaK4YSCKmAB4SsRnpARK0MTJ96xtpPmwAKfkvsZFga8OoGMeb3PmA',
// title: 'Order',
// body: 'endNameAddress',
// isTopic: false,
// tone: 'tone1',
// category: 'Order', // استخدام الفئة الثابتة
// driverList: []);
// RideState.driverApplied;
// Get.find<MapPassengerController>().Ride
},
child: Text(
"Text Button",

View File

@@ -480,7 +480,7 @@ class MainBottomMenuMap extends StatelessWidget {
controller.placeDestinationController.clear();
controller.showBottomSheet1();
// controller.showBottomSheet1();
controller.changeMainBottomMenuMap();
},

View File

@@ -1,3 +1,288 @@
// import 'package:flutter/material.dart';
// import 'package:flutter_font_icons/flutter_font_icons.dart';
// import 'package:get/get.dart';
// import 'package:Intaleq/constant/box_name.dart';
// import 'package:Intaleq/controller/profile/profile_controller.dart';
// import 'package:Intaleq/main.dart';
// import 'package:Intaleq/views/home/profile/complaint_page.dart';
// import '../../../constant/colors.dart';
// import '../../../constant/links.dart';
// import '../../../constant/style.dart';
// import '../../../controller/functions/audio_record1.dart';
// import '../../../controller/functions/launch.dart';
// import '../../../controller/functions/toast.dart';
// import '../../../controller/home/map_passenger_controller.dart';
// // --- الويدجت الرئيسية بالتصميم الجديد ---
// class RideBeginPassenger extends StatelessWidget {
// const RideBeginPassenger({super.key});
// @override
// Widget build(BuildContext context) {
// // --- نفس منطق استدعاء الكنترولرز ---
// final ProfileController profileController = Get.put(ProfileController());
// final AudioRecorderController audioController =
// Get.put(AudioRecorderController());
// return GetBuilder<MapPassengerController>(builder: (controller) {
// // --- نفس شرط الإظهار الخاص بك ---
// if (controller.statusRide != 'Begin') {
// return const SizedBox.shrink();
// }
// return Positioned(
// left: 0,
// right: 0,
// bottom: 0,
// child: Container(
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// borderRadius: const BorderRadius.only(
// topLeft: Radius.circular(24),
// topRight: Radius.circular(24),
// ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.2),
// blurRadius: 20,
// offset: const Offset(0, -5),
// ),
// ],
// ),
// child: Padding(
// padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // مقبض السحب (Handle)
// Container(
// width: 40,
// height: 5,
// decoration: BoxDecoration(
// color: AppColor.writeColor.withOpacity(0.3),
// borderRadius: BorderRadius.circular(12),
// ),
// ),
// const SizedBox(height: 12),
// // --- 1. قسم معلومات السائق ---
// _buildDriverInfo(controller),
// const Divider(height: 24, thickness: 0.5),
// // --- 2. قسم تقدم الرحلة ---
// _buildTripProgress(controller),
// const SizedBox(height: 16),
// // --- 3. قسم الإجراءات والأمان ---
// _buildActionButtons(
// context, controller, profileController, audioController),
// ],
// ),
// ),
// ),
// );
// });
// }
// // --- ويدجت مساعدة لعرض معلومات السائق بشكل منظم ---
// Widget _buildDriverInfo(MapPassengerController controller) {
// return Row(
// children: [
// CircleAvatar(
// radius: 28,
// backgroundImage: NetworkImage(
// '${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
// ),
// const SizedBox(width: 12),
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(controller.driverName,
// style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
// const SizedBox(height: 2),
// Text(
// '${controller.make} ${controller.model} • ${box.read(BoxName.carType)}',
// style: AppStyle.subtitle
// .copyWith(color: AppColor.writeColor.withOpacity(0.7)),
// ),
// ],
// ),
// ),
// const SizedBox(width: 12),
// Column(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
// decoration: BoxDecoration(
// color: AppColor.writeColor.withOpacity(0.1),
// borderRadius: BorderRadius.circular(6),
// ),
// child: Text(
// controller.licensePlate,
// style: AppStyle.subtitle
// .copyWith(fontWeight: FontWeight.bold, letterSpacing: 1.5),
// ),
// ),
// const SizedBox(height: 4),
// Row(
// children: [
// Text(controller.driverRate,
// style: AppStyle.subtitle
// .copyWith(fontWeight: FontWeight.bold)),
// const SizedBox(width: 2),
// const Icon(Icons.star_rounded,
// color: AppColor.yellowColor, size: 16),
// ],
// ),
// ],
// )
// ],
// );
// }
// // --- ويدجت مساعدة لعرض شريط التقدم ---
// Widget _buildTripProgress(MapPassengerController controller) {
// return Column(
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text('Time to Destination'.tr, style: AppStyle.subtitle),
// Text(controller.stringRemainingTimeRideBegin,
// style: AppStyle.subtitle.copyWith(
// fontWeight: FontWeight.bold, color: AppColor.primaryColor)),
// ],
// ),
// const SizedBox(height: 8),
// ClipRRect(
// borderRadius: BorderRadius.circular(10),
// child: LinearProgressIndicator(
// backgroundColor: AppColor.primaryColor.withOpacity(0.2),
// color: controller.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor
// : AppColor.greenColor,
// minHeight: 10,
// value: controller.progressTimerRideBegin.toDouble(),
// ),
// ),
// ],
// );
// }
// // --- ويدجت مساعدة لعرض أزرار الإجراءات ---
// Widget _buildActionButtons(
// BuildContext context,
// MapPassengerController controller,
// ProfileController profileController,
// AudioRecorderController audioController) {
// return Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// _buildActionButton(
// icon: Icons.sos_rounded,
// label: 'SOS'.tr,
// color: AppColor.redColor,
// onTap: () async {
// // --- نفس منطقك القديم ---
// if (box.read(BoxName.sosPhonePassenger) == null) {
// await profileController.updatField(
// 'sosPhone', TextInputType.phone);
// box.write(BoxName.sosPhonePassenger,
// profileController.prfoileData['sosPhone']);
// } else {
// makePhoneCall('112');
// }
// }),
// _buildActionButton(
// icon: FontAwesome.whatsapp,
// label: 'WhatsApp'.tr,
// color: AppColor.greenColor,
// onTap: () async {
// // --- نفس منطقك القديم ---
// if (box.read(BoxName.sosPhonePassenger) == null ||
// box.read(BoxName.sosPhonePassenger) == 'sos') {
// await profileController.updatField(
// 'sosPhone', TextInputType.phone);
// box.write(BoxName.sosPhonePassenger,
// profileController.prfoileData['sosPhone']);
// } else {
// final phoneNumber =
// box.read(BoxName.sosPhonePassenger).toString();
// final phone = controller.formatSyrianPhoneNumber(phoneNumber);
// controller.sendWhatsapp(phone); //
// }
// }),
// _buildActionButton(
// icon: Icons.share_location_outlined, // أيقونة جديدة ومناسبة
// label: 'Share'.tr, // اسم جديد وواضح
// color: AppColor.blueColor,
// onTap: () async {
// // نفس الوظيفة السابقة التي كانت تحت اسم "Video Call"
// await controller.getTokenForParent();
// }),
// _buildActionButton(
// icon: audioController.isRecording
// ? Icons.mic_off_rounded
// : Icons.mic_none_rounded,
// label: audioController.isRecording ? 'Stop'.tr : 'Record'.tr,
// color: AppColor.primaryColor,
// onTap: () async {
// // --- نفس منطقك القديم ---
// if (audioController.isRecording == false) {
// await audioController.startRecording();
// Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
// } else {
// await audioController.stopRecording();
// Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
// }
// },
// ),
// _buildActionButton(
// icon: Icons.note_add_outlined,
// label: 'Complaint'.tr,
// color: AppColor.yellowColor,
// onTap: () {
// // --- نفس منطقك القديم ---
// Get.to(() => ComplaintPage(), transition: Transition.downToUp);
// }),
// ],
// );
// }
// // --- ويدجت مساعدة لبناء زر إجراء فردي ---
// Widget _buildActionButton(
// {required IconData icon,
// required String label,
// required Color color,
// required VoidCallback onTap}) {
// return InkWell(
// onTap: onTap,
// borderRadius: BorderRadius.circular(12),
// child: Padding(
// padding: const EdgeInsets.all(4.0),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: color.withOpacity(0.1),
// shape: BoxShape.circle,
// ),
// child: Icon(icon, color: color, size: 26),
// ),
// const SizedBox(height: 6),
// Text(label, style: AppStyle.subtitle.copyWith(fontSize: 12)),
// ],
// ),
// ),
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
@@ -5,6 +290,7 @@ import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/controller/profile/profile_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/home/profile/complaint_page.dart';
import 'package:intl/intl.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
@@ -14,67 +300,80 @@ import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class RideBeginPassenger extends StatelessWidget {
const RideBeginPassenger({super.key});
@override
Widget build(BuildContext context) {
// --- نفس منطق استدعاء الكنترولرز ---
final ProfileController profileController = Get.put(ProfileController());
final AudioRecorderController audioController =
Get.put(AudioRecorderController());
return GetBuilder<MapPassengerController>(builder: (controller) {
// --- نفس شرط الإظهار الخاص بك ---
if (controller.statusRide != 'Begin') {
return const SizedBox.shrink();
}
return Obx(() {
final controller = Get.find<MapPassengerController>();
return Positioned(
// شرط الإظهار: تظهر فقط عندما تكون الرحلة جارية
final bool isVisible =
controller.currentRideState.value == RideState.inProgress &&
controller.isStartAppHasRide == false;
;
return AnimatedPositioned(
duration: const Duration(milliseconds: 500),
curve: Curves.easeOutBack, // حركة أكثر سلاسة
bottom: isVisible ? 0 : -Get.height * 0.6,
left: 0,
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
color: AppColor.secondaryColor,
color: Colors.white, // خلفية بيضاء لنظافة التصميم
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
color: Colors.black.withOpacity(0.15),
blurRadius: 25,
spreadRadius: 5,
offset: const Offset(0, -5),
),
],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
padding: const EdgeInsets.fromLTRB(20, 12, 20, 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// مقبض السحب (Handle)
// مقبض السحب
Container(
width: 40,
width: 50,
height: 5,
decoration: BoxDecoration(
color: AppColor.writeColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(12),
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
const SizedBox(height: 12),
const SizedBox(height: 20),
// --- 1. قسم معلومات السائق ---
_buildDriverInfo(controller),
const Divider(height: 24, thickness: 0.5),
// الصف العلوي: معلومات السائق + السعر المثبت
_buildDriverAndPriceSection(controller),
// --- 2. قسم تقدم الرحلة ---
const SizedBox(height: 20),
// الصف الأوسط: لوحة السيارة الواقعية + نوع السيارة
_buildCarInfoSection(controller),
const SizedBox(height: 20),
// شريط التقدم والوقت
_buildTripProgress(controller),
const SizedBox(height: 16),
// --- 3. قسم الإجراءات والأمان ---
const SizedBox(height: 20),
const Divider(thickness: 1, color: Color(0xFFEEEEEE)),
const SizedBox(height: 10),
// الأزرار
_buildActionButtons(
context, controller, profileController, audioController),
],
@@ -85,86 +384,227 @@ class RideBeginPassenger extends StatelessWidget {
});
}
// --- ويدجت مساعدة لعرض معلومات السائق بشكل منظم ---
Widget _buildDriverInfo(MapPassengerController controller) {
// ويدجت معلومات السائق والسعر
Widget _buildDriverAndPriceSection(MapPassengerController controller) {
return Row(
children: [
CircleAvatar(
radius: 28,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
// صورة السائق
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: AppColor.primaryColor, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
),
),
const SizedBox(width: 12),
const SizedBox(width: 15),
// الاسم والتقييم
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(controller.driverName,
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 2),
Text(
'${controller.make} ${controller.model}${box.read(BoxName.carType)}',
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor.withOpacity(0.7)),
controller.driverName,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w800,
fontSize: 18,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.star_rounded,
color: AppColor.yellowColor, size: 18),
const SizedBox(width: 4),
Text(
controller.driverRate,
style: AppStyle.subtitle.copyWith(
fontWeight: FontWeight.bold, color: Colors.grey[600]),
),
],
),
],
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColor.writeColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
// السعر المثبت (تصميم كارد للسعر)
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.primaryColor.withOpacity(0.2)),
),
child: Column(
children: [
Text('Total'.tr,
style: TextStyle(fontSize: 10, color: Colors.grey[600])),
Text(
'${NumberFormat('#,###').format(controller.totalPassenger)} 💰',
style: const TextStyle(
fontFamily: 'Roboto',
fontWeight: FontWeight.w900,
fontSize: 18,
color: AppColor.primaryColor,
),
),
child: Text(
controller.licensePlate,
style: AppStyle.subtitle
.copyWith(fontWeight: FontWeight.bold, letterSpacing: 1.5),
),
),
const SizedBox(height: 4),
Row(
children: [
Text(controller.driverRate,
style: AppStyle.subtitle
.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(width: 2),
const Icon(Icons.star_rounded,
color: AppColor.yellowColor, size: 16),
],
),
],
)
],
),
),
],
);
}
// --- ويدجت مساعدة لعرض شريط التقدم ---
// ويدجت معلومات السيارة ولوحة الأرقام
Widget _buildCarInfoSection(MapPassengerController controller) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF9F9F9),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey[200]!),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// نوع السيارة وموديلها
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${controller.make} ${controller.model}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
child: Text(
box.read(BoxName.carType) == 'Speed'
? 'Fixed Price'
.tr // سيظهر "سعر ثابت" (تأكد من إضافتها لملف الترجمة)
: box.read(BoxName.carType) ?? 'Car',
style: const TextStyle(fontSize: 12, color: Colors.black54),
),
),
],
),
// -------------------------------------------
// تصميم لوحة السيارة الواقعي (Realistic Plate)
// -------------------------------------------
Container(
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 2),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.black, width: 2), // إطار أسود
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(2, 2)),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// الشريط الأزرق الجانبي (مثل اللوحات الدولية)
Container(
width: 15,
height: 35, // ارتفاع اللوحة
decoration: const BoxDecoration(
color: Color(0xFF003399), // أزرق غامق
borderRadius: BorderRadius.only(
topLeft: Radius.circular(2),
bottomLeft: Radius.circular(2),
),
),
child: const Center(
child: Text(
"SY", // رمز الدولة (مثال)
style: TextStyle(
color: Colors.white,
fontSize: 8,
fontWeight: FontWeight.bold),
),
),
),
const SizedBox(width: 8),
// رقم اللوحة
Text(
controller.licensePlate, // رقم اللوحة من الكونترولر
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
letterSpacing: 2.0, // تباعد الأحرف لتبدو كأرقام محفورة
fontFamily: 'monospace', // خط ثابت العرض ليشبه اللوحات
color: Colors.black,
),
),
const SizedBox(width: 8),
],
),
),
],
),
);
}
Widget _buildTripProgress(MapPassengerController controller) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Time to Destination'.tr, style: AppStyle.subtitle),
Text(controller.stringRemainingTimeRideBegin,
style: AppStyle.subtitle.copyWith(
fontWeight: FontWeight.bold, color: AppColor.primaryColor)),
Row(
children: [
const Icon(Icons.access_time_filled,
size: 16, color: Colors.grey),
const SizedBox(width: 5),
Text('Arriving in'.tr,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
],
),
Text(
controller.stringRemainingTimeRideBegin,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppColor.primaryColor,
),
),
],
),
const SizedBox(height: 8),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
backgroundColor: Colors.grey[200],
color: controller.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 10,
: AppColor.primaryColor,
minHeight: 8,
value: controller.progressTimerRideBegin.toDouble(),
),
),
@@ -172,21 +612,21 @@ class RideBeginPassenger extends StatelessWidget {
);
}
// --- ويدجت مساعدة لعرض أزرار الإجراءات ---
Widget _buildActionButtons(
BuildContext context,
MapPassengerController controller,
ProfileController profileController,
AudioRecorderController audioController) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// زر SOS بتصميم تحذيري
_buildActionButton(
icon: Icons.sos_rounded,
label: 'SOS'.tr,
color: AppColor.redColor,
label: 'SOS',
iconColor: Colors.white,
bgColor: AppColor.redColor,
onTap: () async {
// --- نفس منطقك القديم ---
if (box.read(BoxName.sosPhonePassenger) == null) {
await profileController.updatField(
'sosPhone', TextInputType.phone);
@@ -196,12 +636,14 @@ class RideBeginPassenger extends StatelessWidget {
makePhoneCall('112');
}
}),
// زر واتساب
_buildActionButton(
icon: FontAwesome.whatsapp,
label: 'WhatsApp'.tr,
color: AppColor.greenColor,
label: 'WhatsApp',
iconColor: Colors.white,
bgColor: const Color(0xFF25D366),
onTap: () async {
// --- نفس منطقك القديم ---
if (box.read(BoxName.sosPhonePassenger) == null ||
box.read(BoxName.sosPhonePassenger) == 'sos') {
await profileController.updatField(
@@ -211,75 +653,105 @@ class RideBeginPassenger extends StatelessWidget {
} else {
final phoneNumber =
box.read(BoxName.sosPhonePassenger).toString();
final phone = controller.formatSyrianPhoneNumber(phoneNumber);
controller.sendWhatsapp(phone); //
controller.sendWhatsapp(phone);
}
}),
// زر المشاركة
_buildActionButton(
icon: Icons.share_location_outlined, // أيقونة جديدة ومناسبة
label: 'Share'.tr, // اسم جديد وواضح
color: AppColor.blueColor,
icon: Icons.share_location_rounded,
label: 'Share',
iconColor: AppColor.primaryColor,
bgColor: AppColor.primaryColor.withOpacity(0.1),
onTap: () async {
// نفس الوظيفة السابقة التي كانت تحت اسم "Video Call"
await controller.getTokenForParent();
await controller.shareTripWithFamily();
}),
_buildActionButton(
icon: audioController.isRecording
? Icons.mic_off_rounded
: Icons.mic_none_rounded,
label: audioController.isRecording ? 'Stop'.tr : 'Record'.tr,
color: AppColor.primaryColor,
onTap: () async {
// --- نفس منطقك القديم ---
if (audioController.isRecording == false) {
await audioController.startRecording();
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
} else {
await audioController.stopRecording();
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
}
// زر التسجيل
GetBuilder<AudioRecorderController>(
init: audioController,
builder: (audioCtx) {
return _buildActionButton(
icon: audioCtx.isRecording ? Icons.stop : Icons.mic,
label: audioCtx.isRecording ? 'Stop' : 'Record',
iconColor:
audioCtx.isRecording ? Colors.white : AppColor.primaryColor,
bgColor: audioCtx.isRecording
? AppColor.redColor
: AppColor.primaryColor.withOpacity(0.1),
isRecordingAnimation: audioCtx.isRecording,
onTap: () async {
if (audioCtx.isRecording == false) {
await audioCtx.startRecording();
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
} else {
await audioCtx.stopRecording();
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
}
},
);
},
),
// زر الشكوى
_buildActionButton(
icon: Icons.note_add_outlined,
label: 'Complaint'.tr,
color: AppColor.yellowColor,
icon: Icons.report_gmailerrorred_rounded,
label: 'Report'.tr,
iconColor: Colors.grey[700]!,
bgColor: Colors.grey[200]!,
onTap: () {
// --- نفس منطقك القديم ---
Get.to(() => ComplaintPage(), transition: Transition.downToUp);
}),
],
);
}
// --- ويدجت مساعدة لبناء زر إجراء فردي ---
Widget _buildActionButton(
{required IconData icon,
required String label,
required Color color,
required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 26),
Widget _buildActionButton({
required IconData icon,
required String label,
required Color iconColor,
required Color bgColor,
required VoidCallback onTap,
bool isRecordingAnimation = false,
}) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(15),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: 50,
height: 50,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(15),
border: isRecordingAnimation
? Border.all(color: AppColor.redColor, width: 2)
: null,
boxShadow: [
BoxShadow(
color: bgColor.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 3),
),
],
),
const SizedBox(height: 6),
Text(label, style: AppStyle.subtitle.copyWith(fontSize: 12)),
],
child: Icon(icon, color: iconColor, size: 24),
),
),
),
const SizedBox(height: 6),
Text(
label.tr,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
),
],
);
}
}

View File

@@ -1,9 +1,12 @@
import 'package:Intaleq/controller/functions/launch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
// ... استيراد ملفاتك الأخرى ...
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/profile/profile_controller.dart';
@@ -14,161 +17,299 @@ class RideFromStartApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
ProfileController profileController = Get.put(ProfileController());
return GetBuilder<MapPassengerController>(builder: (controller) {
return (controller.statusRideFromStart
// || controller.statusRide == 'Begin'
)
? Positioned(
left: 10,
right: 10,
bottom: 4,
child: Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
Text(
'⏱️',
style: AppStyle.title,
),
Text(
box.read(BoxName.arrivalTime),
style: AppStyle.title,
),
],
),
),
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
Text(
'📍',
style: AppStyle.title,
),
Text(
controller.rideStatusFromStartApp['data']
['distance']
.toString(),
style: AppStyle.title,
),
],
),
),
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
Text(
'💵 ',
style: AppStyle.title,
),
Text(
controller.rideStatusFromStartApp['data']
['price'],
style: AppStyle.title,
),
],
),
),
],
final profileController = Get.put(ProfileController());
final MapPassengerController controller =
Get.find<MapPassengerController>();
return Obx(() {
final bool isRideActive =
controller.currentRideState.value == RideState.inProgress &&
controller.isStartAppHasRide == true;
if (!isRideActive) return const SizedBox();
// قراءة البيانات
final rideData = controller.rideStatusFromStartApp['data'] ?? {};
final driverId = rideData['driver_id'];
final driverName = rideData['driverName'] ?? 'Captain'.tr;
final driverRate = controller.driverRate;
final carType = rideData['carType'] ?? 'Car'.tr;
final carModel = controller.model ?? '';
final arrivalTime = box.read(BoxName.arrivalTime) ?? '--:--';
// تحديد البيانات للعرض
final displayTime = controller.stringRemainingTimeRideBegin.isNotEmpty
? controller.stringRemainingTimeRideBegin
: arrivalTime;
final displayDistance = rideData['distance']?.toStringAsFixed(1) ?? 'N/A';
final displayPrice = rideData['price']?.toString() ?? 'N/A';
return Positioned(
left: 0,
right: 0,
bottom: 0, // ملتصق بالأسفل تماماً
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: const BoxDecoration(
color: Colors.white, // خلفية بيضاء نظيفة
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 15.0,
spreadRadius: 5.0,
offset: Offset(0, -5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// 1. مقبض صغير للدلالة على السحب (تصميم جمالي)
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 15),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
),
// 2. حالة الرحلة + معلومات السائق
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// صورة السائق
Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
shape: BoxShape.circle,
border:
Border.all(color: AppColor.primaryColor, width: 2),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
child: CircleAvatar(
radius: 28,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/$driverId.jpg'),
),
),
const SizedBox(width: 12),
// الاسم والسيارة
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
// '',
// ),
'https://ride.mobile-app.store/portrate_captain_image/${controller.rideStatusFromStartApp['data']['driver_id']}.jpg'),
),
Text(
'${controller.rideStatusFromStartApp['data']['driverName']}',
style: AppStyle.title,
driverName,
style: AppStyle.title.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Column(
const SizedBox(height: 4),
Row(
children: [
Icon(Icons.star,
color: AppColor.yellowColor, size: 16),
const SizedBox(width: 4),
Text(
'${controller.rideStatusFromStartApp['data']['rateDriver']} 📈',
style: AppStyle.title,
driverRate,
style: AppStyle.title.copyWith(
fontSize: 13, fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
Container(width: 1, height: 12, color: Colors.grey),
const SizedBox(width: 8),
Text(
'${controller.rideStatusFromStartApp['data']['carType']}',
style: AppStyle.title,
"$carType - $carModel",
style: AppStyle.title.copyWith(
fontSize: 13, color: Colors.grey[600]),
),
],
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null) {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
controller
.sendSMS(box.read(BoxName.sosPhonePassenger));
}
},
icon: const Icon(
Icons.sos_rounded,
color: AppColor.redColor,
),
),
IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null ||
box.read(BoxName.sosPhonePassenger) == 'sos') {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
String phoneNumber = box
.read(BoxName.sosPhonePassenger)
.toString();
// phoneNumber = phoneNumber.replaceAll('0', '');
var phone =
// '+${box.read(BoxName.countryCode)}${box.read(BoxName.sosPhonePassenger)}';
'${box.read(BoxName.sosPhonePassenger)}';
controller.sendWhatsapp(phone);
}
},
icon: const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
),
],
)
),
// حالة الرحلة (نص ملون)
_buildStatusBadge(controller.currentRideState.value),
],
),
const SizedBox(height: 20),
// 3. شريط المعلومات (وقت، مسافة، سعر)
Container(
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 10),
decoration: BoxDecoration(
color: AppColor.grayColor
.withOpacity(0.1), // خلفية رمادية خفيفة جداً
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoColumn(
Icons.access_time_filled, displayTime, "Time".tr),
_buildVerticalDivider(),
_buildInfoColumn(Icons.location_on, "$displayDistance KM",
"Distance".tr),
_buildVerticalDivider(),
_buildInfoColumn(
Icons.attach_money, "$displayPrice SYP", "Price".tr),
],
),
),
)
: const SizedBox();
const SizedBox(height: 20),
// 4. أزرار التحكم (SOS & Share)
Row(
children: [
// زر المشاركة (يأخذ مساحة أكبر)
Expanded(
flex: 2,
child: ElevatedButton.icon(
onPressed: () => _checkAndCall(
controller.sendWhatsapp, profileController),
icon:
const Icon(FontAwesome.whatsapp, color: Colors.white),
label: Text("Share Trip".tr,
style: const TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.greenColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
),
),
const SizedBox(width: 10),
// زر الاستغاثة SOS
Expanded(
flex: 1,
child: ElevatedButton.icon(
onPressed: () =>
makePhoneCall(box.read(BoxName.sosPhonePassenger)),
icon: const Icon(Icons.sos, color: Colors.white),
label: Text("SOS".tr,
style: const TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.redColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
),
),
],
),
],
),
),
);
});
}
// ------------------------- Widgets Helper Methods -------------------------
Widget _buildStatusBadge(RideState status) {
String text;
Color color;
if (status == RideState.inProgress) {
text = 'On Trip'.tr;
color = AppColor.primaryColor;
} else if (status == RideState.driverArrived) {
text = 'Arrived'.tr;
color = AppColor.greenColor;
} else {
text = 'Coming'.tr;
color = AppColor.yellowColor;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.5)),
),
child: Text(
text,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
);
}
Widget _buildInfoColumn(IconData icon, String value, String label) {
return Column(
children: [
Icon(icon,
color: AppColor.secondaryColor,
size: 22), // افترضت أن السكندري لون داكن، أو استخدم Primary
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w800,
fontSize: 15,
color: Colors.black87,
),
),
Text(
label,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
),
),
],
);
}
Widget _buildVerticalDivider() {
return Container(
height: 30,
width: 1,
color: Colors.grey[300],
);
}
// دالة المساعدة للمنطق (بقيت كما هي ولكن تم تمرير البروفايل كونترولر)
Future<void> _checkAndCall(
Function(String) action, ProfileController profileController) async {
String? sosPhone = box.read(BoxName.sosPhonePassenger);
if (sosPhone == null || sosPhone == 'sos') {
await profileController.updatField('sosPhone', TextInputType.phone);
sosPhone = profileController.prfoileData['sosPhone'];
}
if (sosPhone != null && sosPhone != 'sos') {
action(sosPhone);
} else {
Get.snackbar('Warning'.tr, 'Please set a valid SOS phone number.'.tr,
backgroundColor: AppColor.redColor, colorText: Colors.white);
}
}
}

View File

@@ -1,11 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class SearchingCaptainWindow extends StatefulWidget {
@@ -36,62 +33,68 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) {
return AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: controller.isSearchingWindow ? 0 : -Get.height * 0.4,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
offset: const Offset(0, -5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// --- 1. أنيميشن الرادار ---
_buildRadarAnimation(controller),
const SizedBox(height: 20),
// [تعديل 1] نستخدم Obx للاستماع إلى التغييرات في حالة الرحلة
return Obx(() {
// ابحث عن الكنترولر مرة واحدة
final controller = Get.find<MapPassengerController>();
// --- 2. زر الإلغاء ---
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: () {
// --- نفس منطقك للإلغاء ---
controller.cancelRide();
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColor.writeColor,
side: BorderSide(
color: AppColor.writeColor.withOpacity(0.3)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text('Cancel Search'.tr),
),
),
],
// [تعديل 2] شرط الإظهار يعتمد الآن على حالة الرحلة مباشرة
final bool isVisible =
controller.currentRideState.value == RideState.searching;
return AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: isVisible ? 0 : -Get.height * 0.45, // زيادة الارتفاع قليلاً
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
offset: const Offset(0, -5),
),
],
),
);
},
);
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// --- 1. أنيميشن الرادار ---
_buildRadarAnimation(controller),
const SizedBox(height: 20),
// --- 2. زر الإلغاء ---
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: () {
// [تعديل 3] استدعاء دالة الإلغاء الموحدة
controller.cancelRide();
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColor.writeColor,
side:
BorderSide(color: AppColor.writeColor.withOpacity(0.3)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text('Cancel Search'.tr),
),
),
],
),
),
);
});
}
// --- ويدجت بناء أنيميشن الرادار ---
@@ -101,7 +104,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
child: Stack(
alignment: Alignment.center,
children: [
// --- دوائر الرادار المتحركة ---
// --- دوائر الرادار المتحركة (تبقى كما هي) ---
...List.generate(3, (index) {
return FadeTransition(
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(
@@ -134,6 +137,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
// [تعديل 4] النص يأتي مباشرة من الكنترولر
controller.driversStatusForSearchWindow,
style: AppStyle.headTitle.copyWith(fontSize: 20),
textAlign: TextAlign.center,
@@ -146,8 +150,32 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// --- استدعاء نفس دالة المؤقت الخاصة بك ---
buildTimerForIncrease(controller),
// --- [!! تعديل جوهري !!] ---
// لم نعد بحاجة لـ buildTimerForIncrease
// المؤقت الرئيسي في الكنترولر هو من يقرر متى يعرض الحوار
// وهذا الجزء من الواجهة أصبح "غبياً" (لا يحتوي على منطق)
// الكنترولر سيستدعي _showIncreaseFeeDialog مباشرة
SizedBox(
height: 40,
width: 40,
child: Stack(
fit: StackFit.expand,
children: [
CircularProgressIndicator(
strokeWidth: 3,
color: AppColor.primaryColor,
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
),
Center(
child: Icon(
Icons.search,
color: AppColor.writeColor.withOpacity(0.8),
),
),
],
),
),
],
),
],
@@ -156,128 +184,285 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
}
}
// --- نفس دالة المؤقت الخاصة بك مع تعديلات شكلية بسيطة ---
Widget buildTimerForIncrease(MapPassengerController mapPassengerController) {
return StreamBuilder<int>(
stream: Stream.periodic(const Duration(seconds: 1))
.map((_) => ++mapPassengerController.currentTimeSearchingCaptainWindow),
initialData: 0,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data! > 30) {
// --- عرض زر زيادة الأجرة بنفس منطقك القديم ---
return TextButton(
onPressed: () =>
_showIncreaseFeeDialog(context, mapPassengerController),
child: Text(
"No one accepted? Try increasing the fare.".tr,
style: AppStyle.title.copyWith(
color: AppColor.primaryColor,
decoration: TextDecoration.underline),
textAlign: TextAlign.center,
),
);
}
// --- [!! تعديل جوهري !!] ---
// تم حذف دالة `buildTimerForIncrease` بالكامل.
// تم حذف دالة `_showIncreaseFeeDialog` من هذا الملف.
// لماذا؟ لأن الكنترولر الآن هو المسؤول الوحيد عن إظهار الحوار.
// دالة `_showIncreaseFeeDialog` موجودة بالفعل داخل `map_passenger_controller.dart`
// وسيتم استدعاؤها من `_handleRideState` عند انتهاء مهلة الـ 90 ثانية.
final double progress = (snapshot.data ?? 0).toDouble() / 30.0;
// // --- الويدجت الرئيسية بالتصميم الجديد ---
// class SearchingCaptainWindow extends StatefulWidget {
// const SearchingCaptainWindow({super.key});
return SizedBox(
height: 40,
width: 40,
child: Stack(
fit: StackFit.expand,
children: [
CircularProgressIndicator(
value: progress,
strokeWidth: 3,
color: AppColor.primaryColor,
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
),
Center(
child: Text(
'${snapshot.data ?? 0}',
style: AppStyle.title.copyWith(
color: AppColor.writeColor, fontWeight: FontWeight.bold),
),
),
],
),
);
},
);
}
// @override
// State<SearchingCaptainWindow> createState() => _SearchingCaptainWindowState();
// }
// --- دالة لعرض نافذة زيادة الأجرة (مأخوذة من منطقك القديم) ---
void _showIncreaseFeeDialog(
BuildContext context, MapPassengerController mapPassengerController) {
Get.defaultDialog(
barrierDismissible: false,
title: "Increase Your Trip Fee (Optional)".tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text(
"We haven't found any drivers yet. Consider increasing your trip fee to make your offer more attractive to drivers."
.tr,
style: AppStyle.subtitle,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
mapPassengerController.increasFeeFromPassenger.text =
(mapPassengerController.totalPassenger + 3)
.toStringAsFixed(1);
mapPassengerController.update();
},
icon: const Icon(Icons.add_circle,
size: 40, color: AppColor.greenColor),
),
SizedBox(
width: 100,
child: Form(
key: mapPassengerController.increaseFeeFormKey,
child: MyTextForm(
controller: mapPassengerController.increasFeeFromPassenger,
label:
mapPassengerController.totalPassenger.toStringAsFixed(2),
hint:
mapPassengerController.totalPassenger.toStringAsFixed(2),
type: TextInputType.number,
),
),
),
IconButton(
onPressed: () {
mapPassengerController.increasFeeFromPassenger.text =
(mapPassengerController.totalPassenger - 3)
.toStringAsFixed(1);
mapPassengerController.update();
},
icon: const Icon(Icons.remove_circle,
size: 40, color: AppColor.redColor),
),
],
),
],
),
actions: [
TextButton(
child: Text("No, thanks".tr,
style: const TextStyle(color: AppColor.redColor)),
onPressed: () {
Get.back();
mapPassengerController.cancelRide();
},
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: AppColor.greenColor),
child: Text("Increase Fee".tr),
onPressed: () =>
mapPassengerController.increaseFeeByPassengerAndReOrder(),
),
],
);
}
// class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
// with SingleTickerProviderStateMixin {
// late AnimationController _animationController;
// @override
// void initState() {
// super.initState();
// _animationController = AnimationController(
// vsync: this,
// duration: const Duration(seconds: 2),
// )..repeat();
// }
// @override
// void dispose() {
// _animationController.dispose();
// super.dispose();
// }
// @override
// Widget build(BuildContext context) {
// return GetBuilder<MapPassengerController>(
// builder: (controller) {
// return AnimatedPositioned(
// duration: const Duration(milliseconds: 300),
// curve: Curves.easeInOut,
// bottom: controller.isSearchingWindow ? 0 : -Get.height * 0.4,
// left: 0,
// right: 0,
// child: Container(
// padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// borderRadius: const BorderRadius.only(
// topLeft: Radius.circular(24),
// topRight: Radius.circular(24),
// ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.2),
// blurRadius: 20,
// offset: const Offset(0, -5),
// ),
// ],
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // --- 1. أنيميشن الرادار ---
// _buildRadarAnimation(controller),
// const SizedBox(height: 20),
// // --- 2. زر الإلغاء ---
// SizedBox(
// width: double.infinity,
// child: OutlinedButton(
// onPressed: () {
// // --- نفس منطقك للإلغاء ---
// controller.changeCancelRidePageShow();
// },
// style: OutlinedButton.styleFrom(
// foregroundColor: AppColor.writeColor,
// side: BorderSide(
// color: AppColor.writeColor.withOpacity(0.3)),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12)),
// padding: const EdgeInsets.symmetric(vertical: 12),
// ),
// child: Text('Cancel Search'.tr),
// ),
// ),
// ],
// ),
// ),
// );
// },
// );
// }
// // --- ويدجت بناء أنيميشن الرادار ---
// Widget _buildRadarAnimation(MapPassengerController controller) {
// return SizedBox(
// height: 180, // ارتفاع ثابت لمنطقة الأنيميشن
// child: Stack(
// alignment: Alignment.center,
// children: [
// // --- دوائر الرادار المتحركة ---
// ...List.generate(3, (index) {
// return FadeTransition(
// opacity: Tween<double>(begin: 1.0, end: 0.0).animate(
// CurvedAnimation(
// parent: _animationController,
// curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
// ),
// ),
// child: ScaleTransition(
// scale: Tween<double>(begin: 0.3, end: 1.0).animate(
// CurvedAnimation(
// parent: _animationController,
// curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
// ),
// ),
// child: Container(
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// border: Border.all(
// color: AppColor.primaryColor.withOpacity(0.7),
// width: 2,
// ),
// ),
// ),
// ),
// );
// }),
// // --- المحتوى في المنتصف ---
// Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// controller.driversStatusForSearchWindow,
// style: AppStyle.headTitle.copyWith(fontSize: 20),
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 8),
// Text(
// 'Searching for the nearest captain...'.tr,
// style: AppStyle.subtitle
// .copyWith(color: AppColor.writeColor.withOpacity(0.7)),
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 16),
// // --- استدعاء نفس دالة المؤقت الخاصة بك ---
// buildTimerForIncrease(controller),
// ],
// ),
// ],
// ),
// );
// }
// }
// // --- نفس دالة المؤقت الخاصة بك مع تعديلات شكلية بسيطة ---
// Widget buildTimerForIncrease(MapPassengerController mapPassengerController) {
// return StreamBuilder<int>(
// stream: Stream.periodic(const Duration(seconds: 1))
// .map((_) => ++mapPassengerController.currentTimeSearchingCaptainWindow),
// initialData: 0,
// builder: (context, snapshot) {
// if (snapshot.hasData && snapshot.data! > 45) {
// // --- عرض زر زيادة الأجرة بنفس منطقك القديم ---
// return TextButton(
// onPressed: () =>
// _showIncreaseFeeDialog(context, mapPassengerController),
// child: Text(
// "No one accepted? Try increasing the fare.".tr,
// style: AppStyle.title.copyWith(
// color: AppColor.primaryColor,
// decoration: TextDecoration.underline),
// textAlign: TextAlign.center,
// ),
// );
// }
// final double progress = (snapshot.data ?? 0).toDouble() / 30.0;
// return SizedBox(
// height: 40,
// width: 40,
// child: Stack(
// fit: StackFit.expand,
// children: [
// CircularProgressIndicator(
// value: progress,
// strokeWidth: 3,
// color: AppColor.primaryColor,
// backgroundColor: AppColor.primaryColor.withOpacity(0.2),
// ),
// Center(
// child: Text(
// '${snapshot.data ?? 0}',
// style: AppStyle.title.copyWith(
// color: AppColor.writeColor, fontWeight: FontWeight.bold),
// ),
// ),
// ],
// ),
// );
// },
// );
// }
// // --- دالة لعرض نافذة زيادة الأجرة (مأخوذة من منطقك القديم) ---
// void _showIncreaseFeeDialog(
// BuildContext context, MapPassengerController mapPassengerController) {
// Get.defaultDialog(
// barrierDismissible: false,
// title: "Increase Your Trip Fee (Optional)".tr,
// titleStyle: AppStyle.title,
// content: Column(
// children: [
// Text(
// "We haven't found any drivers yet. Consider increasing your trip fee to make your offer more attractive to drivers."
// .tr,
// style: AppStyle.subtitle,
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 16),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// IconButton(
// onPressed: () {
// mapPassengerController.increasFeeFromPassenger.text =
// (mapPassengerController.totalPassenger + 3)
// .toStringAsFixed(1);
// mapPassengerController.update();
// },
// icon: const Icon(Icons.add_circle,
// size: 40, color: AppColor.greenColor),
// ),
// SizedBox(
// width: 100,
// child: Form(
// key: mapPassengerController.increaseFeeFormKey,
// child: MyTextForm(
// controller: mapPassengerController.increasFeeFromPassenger,
// label:
// mapPassengerController.totalPassenger.toStringAsFixed(2),
// hint:
// mapPassengerController.totalPassenger.toStringAsFixed(2),
// type: TextInputType.number,
// ),
// ),
// ),
// IconButton(
// onPressed: () {
// mapPassengerController.increasFeeFromPassenger.text =
// (mapPassengerController.totalPassenger - 3)
// .toStringAsFixed(1);
// mapPassengerController.update();
// },
// icon: const Icon(Icons.remove_circle,
// size: 40, color: AppColor.redColor),
// ),
// ],
// ),
// ],
// ),
// actions: [
// TextButton(
// child: Text("No, thanks".tr,
// style: const TextStyle(color: AppColor.redColor)),
// onPressed: () {
// Get.back();
// // mapPassengerController.cancelRide();
// mapPassengerController.changeCancelRidePageShow();
// },
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(backgroundColor: AppColor.greenColor),
// child: Text("Increase Fee".tr),
// onPressed: () =>
// mapPassengerController.increaseFeeByPassengerAndReOrder(),
// ),
// ],
// );
// }

View File

@@ -237,7 +237,7 @@ class VipRideBeginPassenger extends StatelessWidget {
width: Get.width * .15,
child: IconButton(
onPressed: () async {
await controller.getTokenForParent();
await controller.shareTripWithFamily();
},
icon: const Icon(
AntDesign.Safety,