25-12-1/1
This commit is contained in:
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -480,7 +480,7 @@ class MainBottomMenuMap extends StatelessWidget {
|
||||
|
||||
controller.placeDestinationController.clear();
|
||||
|
||||
controller.showBottomSheet1();
|
||||
// controller.showBottomSheet1();
|
||||
|
||||
controller.changeMainBottomMenuMap();
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user