26-1-21/1
This commit is contained in:
@@ -39,7 +39,7 @@ class MapPagePassenger extends StatelessWidget {
|
||||
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
bottom: true,
|
||||
child: Stack(
|
||||
children: [
|
||||
GoogleMapPassengerWidget(),
|
||||
@@ -89,17 +89,34 @@ class CancelRidePageShow extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => (controller.polyLines.isNotEmpty &&
|
||||
controller.statusRide != 'Begin')
|
||||
// ||
|
||||
// controller.timeToPassengerFromDriverAfterApplied == 0
|
||||
builder: (controller) {
|
||||
// نستخدم RideState Enum لأنه أدق، أو نصلح المنطق النصي
|
||||
// الشرط:
|
||||
// 1. يوجد خط مسار
|
||||
// 2. الحالة ليست "بدأت"
|
||||
// 3. الحالة ليست "انتهت"
|
||||
// 4. الحالة ليست "قيد التنفيذ" (لزيادة التأكيد)
|
||||
|
||||
// bool showCancelButton = controller.polyLines.isNotEmpty &&
|
||||
// controller.statusRide != 'Begin' && // استخدمنا &&
|
||||
// controller.statusRide != 'inProgress' &&
|
||||
// controller.statusRide != 'Finished';
|
||||
|
||||
// يمكنك أيضاً استخدام RideState ليكون أدق:
|
||||
bool showCancelButton = controller.polyLines.isNotEmpty &&
|
||||
controller.currentRideState.value != RideState.inProgress &&
|
||||
controller.currentRideState.value != RideState.finished;
|
||||
|
||||
return showCancelButton
|
||||
? Positioned(
|
||||
right: box.read(BoxName.lang) != 'ar' ? 10 : null,
|
||||
left: box.read(BoxName.lang) == 'ar' ? 10 : null,
|
||||
top: Get.height * .013,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// استدعاء دالة الإلغاء
|
||||
controller.changeCancelRidePageShow();
|
||||
// ملاحظة: تأكد أن الدالة تظهر ديالوج للتأكيد أولاً ولا تلغي فوراً
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -115,7 +132,9 @@ class CancelRidePageShow extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
))
|
||||
: const SizedBox());
|
||||
: const SizedBox();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,20 +5,18 @@ import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.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 {
|
||||
const ApplyOrderWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// دالة لتحويل كود اللون الهيكس إلى لون
|
||||
Color parseColor(String colorHex) {
|
||||
if (colorHex.isEmpty) return Colors.grey;
|
||||
try {
|
||||
@@ -39,57 +37,59 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.elasticOut, // تأثير حركي أجمل
|
||||
bottom: isVisible ? 0 : -Get.height * 0.6,
|
||||
curve: Curves.elasticOut,
|
||||
// تغيير: جعلنا الإخفاء للأسفل أقل حدة ليكون التحريك أسرع
|
||||
bottom: isVisible ? 0 : -400,
|
||||
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)),
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(25)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 20,
|
||||
spreadRadius: 2,
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
offset: const Offset(0, -2),
|
||||
spreadRadius: 1,
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
offset: const Offset(0, -3),
|
||||
)
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 20),
|
||||
// تغيير: تقليل الحواف الخارجية بشكل كبير
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||
child: GetBuilder<MapPassengerController>(
|
||||
builder: (c) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // مهم جداً: يأخذ أقل مساحة ممكنة
|
||||
children: [
|
||||
// مقبض صغير في الأعلى
|
||||
// مقبض صغير
|
||||
Container(
|
||||
width: 40,
|
||||
height: 5,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10), // تقليل المسافة
|
||||
|
||||
// السعر والعنوان
|
||||
_buildPriceHeader(context, c),
|
||||
// 1. [تغيير جوهري] دمج السعر مع الحالة في صف واحد لتوفير المساحة
|
||||
_buildCompactHeaderRow(context, c),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10), // مسافة مضغوطة
|
||||
|
||||
// كرت المعلومات الرئيسي (سائق + سيارة)
|
||||
_buildMainInfoCard(context, c, parseColor),
|
||||
// 2. كرت المعلومات المضغوط
|
||||
_buildCompactInfoCard(context, c, parseColor),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10), // مسافة مضغوطة
|
||||
|
||||
// أزرار الاتصال
|
||||
_buildContactButtonsRow(context, c),
|
||||
// 3. أزرار الاتصال (Slim)
|
||||
_buildCompactButtonsRow(context, c),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10), // مسافة مضغوطة
|
||||
|
||||
// شريط الوقت
|
||||
// 4. شريط الوقت
|
||||
c.currentRideState.value == RideState.driverArrived
|
||||
? const DriverArrivePassengerAndWaitMinute()
|
||||
: const TimeDriverToPassenger(),
|
||||
@@ -103,42 +103,90 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. قسم السعر (مع التنسيق الجديد)
|
||||
// [NEW] 1. صف الرأس المضغوط (يحتوي الحالة + الإحصائيات + السعر)
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildPriceHeader(
|
||||
Widget _buildCompactHeaderRow(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
// تنسيق الرقم (مثلاً: 60,000)
|
||||
// تنسيق السعر
|
||||
final formatter = NumberFormat("#,###");
|
||||
String formattedPrice = formatter.format(controller.totalPassenger);
|
||||
|
||||
return Column(
|
||||
// حساب الدقائق
|
||||
int minutes =
|
||||
(controller.timeToPassengerFromDriverAfterApplied / 60).ceil();
|
||||
if (minutes < 1) minutes = 1;
|
||||
|
||||
// تنسيق المسافة
|
||||
String distanceDisplay = "";
|
||||
try {
|
||||
double distMeters = double.parse(controller.distanceByPassenger);
|
||||
if (distMeters >= 1000) {
|
||||
distanceDisplay = "${(distMeters / 1000).toStringAsFixed(1)} km";
|
||||
} else {
|
||||
distanceDisplay = "${distMeters.toInt()} m";
|
||||
}
|
||||
} catch (e) {
|
||||
distanceDisplay = controller.distanceByPassenger;
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Driver Accepted Request'.tr,
|
||||
style: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
// القسم الأيسر: الحالة + Chips
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Driver is on the way'.tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13, // تصغير الخط
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
_buildMiniStatChip(
|
||||
icon: Icons.access_time_filled_rounded,
|
||||
text: "$minutes ${'min'.tr}",
|
||||
color: AppColor.primaryColor,
|
||||
bgColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildMiniStatChip(
|
||||
icon: Icons.near_me_rounded,
|
||||
text: distanceDisplay,
|
||||
color: Colors.orange[800]!,
|
||||
bgColor: Colors.orange.withOpacity(0.1),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
// القسم الأيمن: السعر (كبير وواضح في الزاوية)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
formattedPrice,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 28,
|
||||
fontSize: 24, // تصغير من 32 إلى 24
|
||||
fontWeight: FontWeight.w900,
|
||||
color: AppColor.primaryColor,
|
||||
height: 1.0,
|
||||
),
|
||||
),
|
||||
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],
|
||||
),
|
||||
Text(
|
||||
'SYP'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -147,207 +195,251 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMiniStatChip({
|
||||
required IconData icon,
|
||||
required String text,
|
||||
required Color color,
|
||||
required Color bgColor,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 12, color: color), // تصغير الأيقونة
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12, // تصغير الخط
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. كرت المعلومات الرئيسي (السائق + السيارة 3D)
|
||||
// [MODIFIED] 2. كرت المعلومات المضغوط جداً
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildMainInfoCard(BuildContext context,
|
||||
Widget _buildCompactInfoCard(BuildContext context,
|
||||
MapPassengerController controller, Color Function(String) parseColor) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
// تقليل الحواف الداخلية للكرت
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor, // لون خلفية فاتح
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.1)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// الصف العلوي: سائق + سيارة
|
||||
Row(
|
||||
children: [
|
||||
// صورة السائق (أصغر)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor.withOpacity(0.2), width: 2),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 22, // تصغير من 28 إلى 22
|
||||
backgroundColor: Colors.grey[200],
|
||||
backgroundImage: NetworkImage(
|
||||
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
|
||||
onBackgroundImageError: (_, __) =>
|
||||
const Icon(Icons.person, color: Colors.grey, size: 20),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 10),
|
||||
|
||||
// معلومات نصية
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
controller.driverName,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, // تصغير الخط
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.star_rounded,
|
||||
color: Colors.amber, size: 14),
|
||||
Text(
|
||||
" ${controller.driverRate} • ${controller.model}",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[700],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// أيقونة السيارة (أصغر)
|
||||
_buildMicroCarIcon(controller, parseColor),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// لوحة السيارة (شريط نحيف جداً)
|
||||
_buildSlimLicensePlate(controller.licensePlate),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMicroCarIcon(
|
||||
MapPassengerController controller, Color Function(String) parseColor) {
|
||||
Color carColor = parseColor(controller.colorHex);
|
||||
return Container(
|
||||
height: 40, // تصغير من 50
|
||||
width: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: carColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSlimLicensePlate(String plateNumber) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF5F5F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.3)),
|
||||
),
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
plateNumber,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'RobotoMono',
|
||||
fontSize: 18, // تصغير الرقم
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.black87,
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
|
||||
// الجزء الأيمن: أيقونة السيارة الـ 3D
|
||||
_build3DCarIcon(controller, parseColor),
|
||||
const Text("SYR",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black54)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. أيقونة السيارة الـ 3D (الدائرة والظلال والخلفية الذكية)
|
||||
// [MODIFIED] 3. أزرار الاتصال (Slim Buttons)
|
||||
// ---------------------------------------------------------------------------
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. أزرار الاتصال (بتصميم جديد)
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildContactButtonsRow(
|
||||
Widget _buildCompactButtonsRow(
|
||||
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),
|
||||
return SizedBox(
|
||||
height: 40, // تحديد ارتفاع ثابت وصغير للأزرار
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSlimButton(
|
||||
label: 'Message'.tr, // اختصار الكلمة
|
||||
icon: Icons.chat_bubble_outline_rounded,
|
||||
color: AppColor.blueColor,
|
||||
bgColor: AppColor.blueColor.withOpacity(0.08),
|
||||
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);
|
||||
},
|
||||
const SizedBox(width: 10), // تقليل المسافة
|
||||
Expanded(
|
||||
child: _buildSlimButton(
|
||||
label: 'Call'.tr, // اختصار الكلمة
|
||||
icon: Icons.phone_rounded,
|
||||
color: Colors.white,
|
||||
bgColor: AppColor.greenColor,
|
||||
onTap: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
makePhoneCall(controller.driverPhone);
|
||||
},
|
||||
isPrimary: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton({
|
||||
Widget _buildSlimButton({
|
||||
required String label,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required Color bgColor,
|
||||
required VoidCallback onTap,
|
||||
bool isPrimary = false,
|
||||
}) {
|
||||
return ElevatedButton(
|
||||
onPressed: onTap,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: color.withOpacity(0.1),
|
||||
backgroundColor: bgColor,
|
||||
foregroundColor: color,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
elevation: isPrimary ? 2 : 0,
|
||||
padding: EdgeInsets.zero, // إزالة الحواشي الداخلية
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Icon(icon, size: 18, color: color), // تصغير الأيقونة
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14, // تصغير الخط
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- النوافذ المنبثقة للرسائل (نفس المنطق القديم) ---
|
||||
// --- النوافذ المنبثقة للرسائل (نفس الكود السابق مع تحسين بسيط) ---
|
||||
void _showContactOptionsDialog(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
Get.bottomSheet(
|
||||
@@ -361,13 +453,13 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Quick Message'.tr, style: AppStyle.title),
|
||||
Text('Quick Message'.tr,
|
||||
style: AppStyle.title.copyWith(fontSize: 16)),
|
||||
const SizedBox(height: 15),
|
||||
..._buildPredefinedMessages(controller),
|
||||
const Divider(height: 30),
|
||||
const Divider(height: 20),
|
||||
_buildCustomMessageInput(controller, context),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).viewInsets.bottom), // للكيبورد
|
||||
SizedBox(height: MediaQuery.of(context).viewInsets.bottom),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -384,7 +476,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
|
||||
return messages
|
||||
.map((message) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_sendMessage(controller, message.tr);
|
||||
@@ -392,18 +484,20 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 12, horizontal: 15),
|
||||
const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.grey.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.1)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.quickreply_rounded,
|
||||
size: 18, color: Colors.grey),
|
||||
Icon(Icons.chat_bubble_outline,
|
||||
size: 16, color: AppColor.primaryColor),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(message.tr, style: AppStyle.subtitle)),
|
||||
child: Text(message.tr,
|
||||
style: AppStyle.subtitle.copyWith(fontSize: 13))),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -418,10 +512,11 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 40,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
color: Colors.grey.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Form(
|
||||
key: controller.messagesFormKey,
|
||||
@@ -429,24 +524,28 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
controller: controller.messageToDriver,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Type your message...'.tr,
|
||||
hintStyle: TextStyle(color: Colors.grey[500], fontSize: 13),
|
||||
border: InputBorder.none,
|
||||
contentPadding: const EdgeInsets.only(bottom: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
const SizedBox(width: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
if (controller.messagesFormKey.currentState!.validate()) {
|
||||
_sendMessage(controller, controller.messageToDriver.text);
|
||||
controller.messageToDriver.clear();
|
||||
Get.back();
|
||||
}
|
||||
},
|
||||
child: CircleAvatar(
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
radius: 20,
|
||||
child:
|
||||
const Icon(Icons.send_rounded, color: Colors.white, size: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -455,10 +554,10 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
|
||||
void _sendMessage(MapPassengerController controller, String text) {
|
||||
NotificationService.sendNotification(
|
||||
category: 'message From passenger',
|
||||
category: 'MSG_FROM_PASSENGER',
|
||||
target: controller.driverToken.toString(),
|
||||
title: 'Message From passenger'.tr,
|
||||
body: text,
|
||||
title: text.tr,
|
||||
body: text.tr,
|
||||
isTopic: false,
|
||||
tone: 'ding',
|
||||
driverList: [],
|
||||
@@ -467,7 +566,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// مؤشرات الانتظار والوقت (نفس المنطق مع تحسين بسيط في التصميم)
|
||||
// مؤشرات الانتظار والوقت (مضغوطة)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
|
||||
@@ -481,24 +580,27 @@ class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Driver is waiting'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text('Waiting...'.tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
color: Colors.orange)),
|
||||
Text(
|
||||
controller.stringRemainingTimeDriverWaitPassenger5Minute,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, color: AppColor.redColor),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.orange,
|
||||
fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
const SizedBox(height: 4),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Colors.grey[200],
|
||||
color: controller.remainingTimeDriverWaitPassenger5Minute < 60
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor,
|
||||
minHeight: 8,
|
||||
backgroundColor: Colors.orange.withOpacity(0.2),
|
||||
color: Colors.orange,
|
||||
minHeight: 4,
|
||||
value:
|
||||
controller.progressTimerDriverWaitPassenger5Minute.toDouble(),
|
||||
),
|
||||
@@ -520,25 +622,13 @@ class TimeDriverToPassenger extends StatelessWidget {
|
||||
}
|
||||
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),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Colors.grey[200],
|
||||
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
color: AppColor.primaryColor,
|
||||
minHeight: 8,
|
||||
minHeight: 4,
|
||||
value: controller.progressTimerToPassengerFromDriverAfterApplied
|
||||
.toDouble()
|
||||
.clamp(0.0, 1.0),
|
||||
|
||||
@@ -5,97 +5,156 @@ import 'package:Intaleq/constant/style.dart';
|
||||
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
|
||||
// دالة لإظهار الشيت
|
||||
void showCancelRideBottomSheet() {
|
||||
Get.bottomSheet(
|
||||
cancelRidePage(),
|
||||
const CancelRidePageWidget(),
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
);
|
||||
}
|
||||
|
||||
GetBuilder<MapPassengerController> cancelRidePage() {
|
||||
Get.put(MapPassengerController());
|
||||
// الويدجت مفصولة لترتيب الكود
|
||||
class CancelRidePageWidget extends StatelessWidget {
|
||||
const CancelRidePageWidget({Key? key}) : super(key: key);
|
||||
|
||||
final List<String> reasons = [
|
||||
"I don't need a ride anymore".tr,
|
||||
"I was just trying the application".tr,
|
||||
"No driver accepted my request".tr,
|
||||
"I added the wrong pick-up/drop-off location".tr,
|
||||
"I don't have a reason".tr,
|
||||
"Other".tr,
|
||||
];
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// تأكد من وجود الكنترولر
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => controller.isCancelRidePageShown
|
||||
? Container(
|
||||
height: Get.height * 0.6,
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
offset: const Offset(0, 8),
|
||||
blurRadius: 16,
|
||||
final List<String> reasons = [
|
||||
"Changed my mind".tr,
|
||||
"Found another transport".tr,
|
||||
"Driver is taking too long".tr,
|
||||
"Driver asked me to cancel".tr,
|
||||
"Wrong pickup location".tr,
|
||||
"Other".tr,
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: Get.height * 0.7, // ارتفاع مناسب
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
|
||||
),
|
||||
child: GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// مؤشر السحب
|
||||
Center(
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Can we know why you want to cancel Ride ?'.tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: reasons.length,
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Text(
|
||||
'Why do you want to cancel?'.tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: reasons.length,
|
||||
separatorBuilder: (context, index) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
bool isSelected = controller.selectedReasonIndex == index;
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
reasons[index],
|
||||
style: AppStyle.title.copyWith(fontSize: 16),
|
||||
),
|
||||
leading: Radio(
|
||||
value: index,
|
||||
groupValue: controller.selectedReason,
|
||||
onChanged: (int? value) {
|
||||
controller.selectReason(value!, reasons[index]);
|
||||
},
|
||||
activeColor: AppColor.primaryColor,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
color: isSelected
|
||||
? AppColor.primaryColor
|
||||
: Colors.black87,
|
||||
fontSize: 15),
|
||||
),
|
||||
trailing: isSelected
|
||||
? Icon(Icons.radio_button_checked,
|
||||
color: AppColor.primaryColor)
|
||||
: Icon(Icons.radio_button_off, color: Colors.grey),
|
||||
onTap: () {
|
||||
controller.selectReason(index, reasons[index]);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
MyElevatedButton(
|
||||
title: 'Cancel Ride'.tr,
|
||||
onPressed: () {
|
||||
if (controller.selectedReason == -1) {
|
||||
Get.snackbar(
|
||||
'You Should be select reason.'.tr,
|
||||
'',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: AppColor.redColor,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} else {
|
||||
controller.cancelRide();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// إظهار حقل النص فقط عند اختيار "أخرى"
|
||||
if (isSelected && reasons[index] == "Other".tr)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 10, left: 10, right: 10),
|
||||
child: TextField(
|
||||
controller: controller.otherReasonController,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Please write the reason...".tr,
|
||||
filled: true,
|
||||
fillColor: Colors.grey[100],
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 15, vertical: 12),
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// زر التأكيد
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.redColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
elevation: 0,
|
||||
),
|
||||
onPressed: () => controller.cancelRide(),
|
||||
child: Text(
|
||||
'Confirm Cancellation'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// زر التراجع
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text("Don't Cancel".tr,
|
||||
style: TextStyle(color: Colors.grey[600])),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../widgets/mydialoug.dart';
|
||||
|
||||
// --- CarType class (unchanged) ---
|
||||
// --- CarType class (Unchanged) ---
|
||||
class CarType {
|
||||
final String carType;
|
||||
final String carDetail;
|
||||
@@ -27,21 +27,20 @@ class CarType {
|
||||
{required this.carType, required this.carDetail, required this.image});
|
||||
}
|
||||
|
||||
// --- List of Car Types (unchanged) ---
|
||||
// --- List of Car Types (Unchanged) ---
|
||||
List<CarType> carTypes = [
|
||||
CarType(
|
||||
carType: 'Speed',
|
||||
carType: 'Fixed Price',
|
||||
carDetail: 'Closest & Cheapest'.tr,
|
||||
image: 'assets/images/carspeed.png'), // First choice
|
||||
image: 'assets/images/carspeed.png'),
|
||||
CarType(
|
||||
carType: 'Comfort',
|
||||
carDetail: 'Comfort choice'.tr,
|
||||
image: 'assets/images/blob.png'), // Second choice
|
||||
image: 'assets/images/blob.png'),
|
||||
CarType(
|
||||
carType: 'Electric',
|
||||
carDetail: 'Quiet & Eco-Friendly'.tr,
|
||||
image:
|
||||
'assets/images/electric.png'), // Third choice - NOTE: Use your actual image path
|
||||
image: 'assets/images/electric.png'),
|
||||
CarType(
|
||||
carType: 'Lady',
|
||||
carDetail: 'Lady Captain for girls'.tr,
|
||||
@@ -62,8 +61,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
final textToSpeechController = Get.put(TextToSpeechController());
|
||||
|
||||
void _prepareCarTypes(MapPassengerController controller) {
|
||||
// This logic remains the same
|
||||
if (controller.distance > 33) {
|
||||
if (controller.distance > 23) {
|
||||
if (!carTypes.any((car) => car.carType == 'Rayeh Gai')) {
|
||||
carTypes.add(CarType(
|
||||
carType: 'Rayeh Gai',
|
||||
@@ -83,110 +81,403 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
if (!(controller.isBottomSheetShown) && controller.rideConfirm == false) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
// Added a BackdropFilter for a modern glassmorphism effect
|
||||
|
||||
// Main Bottom Sheet Design
|
||||
return Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor.withOpacity(0.9),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
),
|
||||
border: Border.all(color: AppColor.writeColor.withOpacity(0.1)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Added a small handle for visual cue
|
||||
Container(
|
||||
width: 40,
|
||||
height: 5,
|
||||
margin: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.writeColor.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
_buildHeader(controller),
|
||||
_buildNegativeBalanceWarning(controller),
|
||||
SizedBox(
|
||||
height: 140, // Increased height for better spacing
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 12),
|
||||
itemCount: carTypes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final carType = carTypes[index];
|
||||
final isSelected = controller.selectedIndex == index;
|
||||
return _buildHorizontalCarCard(
|
||||
context, controller, carType, isSelected, index);
|
||||
},
|
||||
),
|
||||
),
|
||||
_buildPromoButton(context, controller),
|
||||
const SizedBox(height: 8), // Added padding at the bottom
|
||||
],
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor
|
||||
.secondaryColor, // Solid background for better performance
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 5,
|
||||
offset: const Offset(0, -5),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Drag Handle
|
||||
Center(
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 5,
|
||||
margin: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Header (Title + Trip Info)
|
||||
_buildModernHeader(controller),
|
||||
|
||||
// Warning Message (if any)
|
||||
_buildNegativeBalanceWarning(controller),
|
||||
|
||||
// Car List
|
||||
SizedBox(
|
||||
height: 165, // Fixed height for consistency
|
||||
child: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
itemCount: carTypes.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(width: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final carType = carTypes[index];
|
||||
final isSelected = controller.selectedIndex == index;
|
||||
return _buildVerticalCarCard(
|
||||
context, controller, carType, isSelected, index);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// Promo Code Button
|
||||
_buildPromoButton(context, controller),
|
||||
|
||||
// Safe Area spacing
|
||||
SizedBox(height: MediaQuery.of(context).padding.bottom + 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// --- All other methods are here, with updated designs ---
|
||||
// --- UI Components ---
|
||||
|
||||
Widget _buildModernHeader(MapPassengerController controller) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Choose your ride'.tr,
|
||||
style: AppStyle.headTitle.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w800,
|
||||
letterSpacing: 0.5),
|
||||
),
|
||||
),
|
||||
|
||||
// Trip Info Pill
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: AppColor.primaryColor.withOpacity(0.2)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.directions_car_filled_outlined,
|
||||
size: 16, color: AppColor.primaryColor),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primaryColor),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
height: 12,
|
||||
width: 1,
|
||||
color: Colors.grey.shade400),
|
||||
Icon(Icons.access_time_filled_rounded,
|
||||
size: 16, color: AppColor.primaryColor),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
controller.hours > 0
|
||||
? '${controller.hours}h ${controller.minutes}m'
|
||||
: '${controller.minutes} min',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primaryColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVerticalCarCard(
|
||||
BuildContext context,
|
||||
MapPassengerController controller,
|
||||
CarType carType,
|
||||
bool isSelected,
|
||||
int index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
controller.selectCarFromList(index);
|
||||
_showCarDetailsDialog(
|
||||
context, controller, carType, textToSpeechController);
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
width: 110,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColor.primaryColor.withOpacity(0.05)
|
||||
: Colors.white,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? AppColor.primaryColor
|
||||
: Colors.grey.withOpacity(0.2),
|
||||
width: isSelected ? 2.0 : 1.0,
|
||||
),
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: AppColor.primaryColor.withOpacity(0.2),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
)
|
||||
]
|
||||
: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Image with subtle scaling if selected
|
||||
AnimatedScale(
|
||||
scale: isSelected ? 1.1 : 1.0,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: Image.asset(
|
||||
carType.image,
|
||||
height: 50,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Car Type Text
|
||||
FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
carType.carType.tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontWeight:
|
||||
isSelected ? FontWeight.w800 : FontWeight.w600,
|
||||
fontSize: 14,
|
||||
color:
|
||||
isSelected ? AppColor.primaryColor : Colors.black87,
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 6),
|
||||
|
||||
// Price Tag
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColor.primaryColor
|
||||
: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
'${_getPassengerPriceText(carType, controller)} ${'SYP'.tr}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? Colors.white : Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Checkmark Badge for Selected Item
|
||||
if (isSelected)
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColor.primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.check, size: 12, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPromoButton(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
if (controller.promoTaken) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (controller.promoTaken) return const SizedBox.shrink();
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 16),
|
||||
child: GestureDetector(
|
||||
onTap: () => _showPromoCodeDialog(context, controller),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor.withOpacity(0.5),
|
||||
width: 1,
|
||||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 5),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => _showPromoCodeDialog(context, controller),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle),
|
||||
child: Icon(Icons.confirmation_number_outlined,
|
||||
color: AppColor.primaryColor, size: 20),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Promo Code'.tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
'Have a promo code?'.tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontSize: 12, color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(Icons.arrow_forward_ios_rounded,
|
||||
size: 16, color: Colors.grey.shade400)
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.local_offer_outlined,
|
||||
color: AppColor.primaryColor, size: 22),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'Have a promo code?'.tr,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 16,
|
||||
color: AppColor.primaryColor,
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNegativeBalanceWarning(MapPassengerController controller) {
|
||||
final passengerWallet =
|
||||
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ?? 0.0;
|
||||
if (passengerWallet < 0.0) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.redColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.redColor.withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color: AppColor.redColor, size: 24),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${'You have a negative balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${'SYP'.tr}.',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// --- Logic Helpers (Copied from your previous code to ensure functionality) ---
|
||||
|
||||
String _getPassengerPriceText(
|
||||
CarType carType, MapPassengerController mapPassengerController) {
|
||||
double rawPrice;
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
rawPrice = mapPassengerController.totalPassengerComfort;
|
||||
break;
|
||||
case 'Fixed Price':
|
||||
rawPrice = mapPassengerController.totalPassengerSpeed;
|
||||
break;
|
||||
case 'Electric':
|
||||
rawPrice = mapPassengerController.totalPassengerElectric;
|
||||
break;
|
||||
case 'Awfar Car':
|
||||
rawPrice = mapPassengerController.totalPassengerBalash;
|
||||
break;
|
||||
case 'Scooter':
|
||||
case 'Pink Bike':
|
||||
rawPrice = mapPassengerController.totalPassengerScooter;
|
||||
break;
|
||||
case 'Van':
|
||||
rawPrice = mapPassengerController.totalPassengerVan;
|
||||
break;
|
||||
case 'Lady':
|
||||
rawPrice = mapPassengerController.totalPassengerLady;
|
||||
break;
|
||||
case 'Rayeh Gai':
|
||||
rawPrice = mapPassengerController.totalPassengerRayehGai;
|
||||
break;
|
||||
default:
|
||||
return '...';
|
||||
}
|
||||
final int roundedPrice = rawPrice.round();
|
||||
final formatter = NumberFormat.decimalPattern();
|
||||
return formatter.format(roundedPrice);
|
||||
}
|
||||
|
||||
// --- Dialogs (Styled consistently) ---
|
||||
|
||||
void _showPromoCodeDialog(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
Get.dialog(
|
||||
@@ -194,6 +485,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
|
||||
backgroundColor: AppColor.secondaryColor,
|
||||
elevation: 10,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
@@ -202,17 +494,20 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Icon(Icons.local_activity_outlined,
|
||||
size: 40, color: AppColor.primaryColor),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Apply Promo Code'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.headTitle.copyWith(fontSize: 22),
|
||||
style: AppStyle.headTitle.copyWith(fontSize: 20),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Enter your code below to apply the discount.'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(color: AppColor.writeColor.withOpacity(0.7)),
|
||||
.copyWith(color: Colors.grey.shade600, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
MyTextForm(
|
||||
@@ -225,15 +520,10 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
child: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColor.writeColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
side: BorderSide(
|
||||
color: AppColor.writeColor.withOpacity(0.3)),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.grey,
|
||||
),
|
||||
child: Text('Cancel'.tr),
|
||||
),
|
||||
@@ -262,211 +552,6 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHorizontalCarCard(
|
||||
BuildContext context,
|
||||
MapPassengerController controller,
|
||||
CarType carType,
|
||||
bool isSelected,
|
||||
int index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
controller.selectCarFromList(index);
|
||||
_showCarDetailsDialog(
|
||||
context, controller, carType, textToSpeechController);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: 120, // Increased width
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
padding: const EdgeInsets.all(8), // Added padding
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: isSelected
|
||||
? [
|
||||
AppColor.primaryColor.withOpacity(0.3),
|
||||
AppColor.primaryColor.withOpacity(0.1)
|
||||
]
|
||||
: [
|
||||
AppColor.writeColor.withOpacity(0.05),
|
||||
AppColor.writeColor.withOpacity(0.1)
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20), // More rounded corners
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? AppColor.primaryColor
|
||||
: AppColor.writeColor.withOpacity(0.2),
|
||||
width: isSelected ? 2.5 : 1.0,
|
||||
),
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: AppColor.primaryColor.withOpacity(0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
)
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround, // Better alignment
|
||||
children: [
|
||||
Image.asset(carType.image, height: 55), // Slightly larger image
|
||||
Text(
|
||||
carType.carType.tr,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(fontWeight: FontWeight.bold, fontSize: 15),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
_buildPriceDisplay(controller, carType),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(MapPassengerController controller) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 8, 20, 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Choose your ride'.tr,
|
||||
style: AppStyle.headTitle.copyWith(fontSize: 24)),
|
||||
const SizedBox(height: 8),
|
||||
// Added icons for better visual representation
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.map_outlined,
|
||||
color: AppColor.primaryColor, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Icon(Icons.timer_outlined,
|
||||
color: AppColor.primaryColor, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
controller.hours > 0
|
||||
? '${controller.hours}h ${controller.minutes}m'
|
||||
: '${controller.minutes} min',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNegativeBalanceWarning(MapPassengerController controller) {
|
||||
final passengerWallet =
|
||||
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ?? 0.0;
|
||||
if (passengerWallet < 0.0) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.redColor.withOpacity(0.9),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.error_outline, color: Colors.white, size: 24),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${'You have a negative balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${'SYP'.tr}.',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: Colors.white, fontWeight: FontWeight.w600))),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildPriceDisplay(
|
||||
MapPassengerController mapPassengerController, CarType carType) {
|
||||
return Text(
|
||||
'${_getPassengerPriceText(carType, mapPassengerController)} ${'SYP'.tr}',
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontSize: 15, // Increased font size
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primaryColor));
|
||||
}
|
||||
|
||||
// --- LOGIC METHODS (UNCHANGED) ---
|
||||
|
||||
// 1. قم بإضافة هذا السطر في أعلى الملف
|
||||
|
||||
String _getPassengerPriceText(
|
||||
CarType carType, MapPassengerController mapPassengerController) {
|
||||
// الخطوة 1: احصل على السعر كـ double أولاً
|
||||
double rawPrice;
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
rawPrice = mapPassengerController.totalPassengerComfort;
|
||||
break;
|
||||
case 'Speed':
|
||||
rawPrice = mapPassengerController.totalPassengerSpeed;
|
||||
break;
|
||||
case 'Electric':
|
||||
rawPrice = mapPassengerController.totalPassengerElectric;
|
||||
break;
|
||||
case 'Awfar Car':
|
||||
rawPrice = mapPassengerController.totalPassengerBalash;
|
||||
break;
|
||||
case 'Scooter':
|
||||
case 'Pink Bike': // دمج الحالات المتشابهة
|
||||
rawPrice = mapPassengerController.totalPassengerScooter;
|
||||
break;
|
||||
case 'Van':
|
||||
rawPrice = mapPassengerController.totalPassengerVan;
|
||||
break;
|
||||
case 'Lady':
|
||||
rawPrice = mapPassengerController.totalPassengerLady;
|
||||
break;
|
||||
case 'Rayeh Gai':
|
||||
rawPrice = mapPassengerController.totalPassengerRayehGai;
|
||||
break;
|
||||
default:
|
||||
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(
|
||||
BuildContext context,
|
||||
MapPassengerController mapPassengerController,
|
||||
@@ -475,74 +560,75 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(24.0)),
|
||||
backgroundColor:
|
||||
Colors.transparent, // Make dialog background transparent
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(28.0)),
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
// Main content container
|
||||
Container(
|
||||
margin:
|
||||
const EdgeInsets.only(top: 50), // Make space for the image
|
||||
padding: const EdgeInsets.fromLTRB(20, 70, 20, 20),
|
||||
margin: const EdgeInsets.only(top: 60),
|
||||
padding: const EdgeInsets.fromLTRB(24, 70, 24, 24),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
borderRadius: BorderRadius.circular(28.0),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
carType.carType.tr,
|
||||
style: AppStyle.headTitle.copyWith(fontSize: 24),
|
||||
style: AppStyle.headTitle.copyWith(fontSize: 22),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
onPressed: () => textToSpeechController.speakText(
|
||||
InkWell(
|
||||
onTap: () => textToSpeechController.speakText(
|
||||
_getCarDescription(
|
||||
mapPassengerController, carType)),
|
||||
icon: Icon(Icons.volume_up_outlined,
|
||||
color: AppColor.primaryColor, size: 26),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Icon(Icons.volume_up_rounded,
|
||||
color: AppColor.primaryColor, size: 24),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
_getCarDescription(mapPassengerController, carType),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.writeColor.withOpacity(0.7),
|
||||
fontSize: 16,
|
||||
height: 1.5,
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Text(
|
||||
_getCarDescription(mapPassengerController, carType),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: Colors.black87,
|
||||
fontSize: 15,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor:
|
||||
AppColor.writeColor.withOpacity(0.8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
child: Text('Cancel'.tr),
|
||||
child: Text('Back'.tr,
|
||||
style: TextStyle(color: Colors.grey.shade600)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: MyElevatedButton(
|
||||
kolor: AppColor.greenColor,
|
||||
title: 'Next'.tr,
|
||||
title: 'Select This Ride'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
_handleCarSelection(
|
||||
@@ -555,10 +641,12 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
// Positioned car image
|
||||
Positioned(
|
||||
top: -10,
|
||||
child: Image.asset(carType.image, height: 120),
|
||||
top: 0,
|
||||
child: Hero(
|
||||
tag: 'car_${carType.carType}',
|
||||
child: Image.asset(carType.image, height: 130),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -567,6 +655,8 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// --- Logic Helpers (Keep unchanged) ---
|
||||
|
||||
String _getCarDescription(
|
||||
MapPassengerController mapPassengerController, CarType carType) {
|
||||
switch (carType.carType) {
|
||||
@@ -578,7 +668,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
.tr
|
||||
: 'Best choice for comfort car and flexible route and stops point'
|
||||
.tr;
|
||||
case 'Speed':
|
||||
case 'Fixed Price':
|
||||
return 'This trip goes directly from your starting point to your destination for a fixed price. The driver must follow the planned route'
|
||||
.tr;
|
||||
case 'Electric':
|
||||
@@ -632,7 +722,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
_buildRayehGaiOption(mapPassengerController, 'Awfar Car',
|
||||
mapPassengerController.totalPassengerRayehGaiBalash),
|
||||
_buildRayehGaiOption(mapPassengerController, 'Speed',
|
||||
_buildRayehGaiOption(mapPassengerController, 'Fixed Price',
|
||||
mapPassengerController.totalPassengerRayehGai),
|
||||
_buildRayehGaiOption(mapPassengerController, 'Comfort',
|
||||
mapPassengerController.totalPassengerRayehGaiComfort)
|
||||
@@ -661,7 +751,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
return mapPassengerController.totalPassengerComfort;
|
||||
case 'Speed':
|
||||
case 'Fixed Price':
|
||||
return mapPassengerController.totalPassengerSpeed;
|
||||
case 'Electric':
|
||||
return mapPassengerController.totalPassengerElectric;
|
||||
|
||||
@@ -7,53 +7,81 @@ import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
|
||||
// ---------------------------------------------------
|
||||
// -- Widget for Start Point Search --
|
||||
// -- Widget for Start Point Search (Updated) --
|
||||
// ---------------------------------------------------
|
||||
|
||||
GetBuilder<MapPassengerController> formSearchPlacesStart() {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
id: 'start_point_form', // إضافة معرف لتحديث هذا الجزء فقط عند الحاجة
|
||||
builder: (controller) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: TextFormField(
|
||||
controller: controller.placeStartController,
|
||||
onChanged: (value) {
|
||||
if (controller.placeStartController.text.length > 2) {
|
||||
controller.getPlacesStart();
|
||||
} else if (controller.placeStartController.text.isEmpty) {
|
||||
controller.clearPlacesStart();
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search for a starting point'.tr,
|
||||
hintStyle: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
prefixIcon:
|
||||
const Icon(Icons.search, color: AppColor.primaryColor),
|
||||
suffixIcon: controller.placeStartController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[400]),
|
||||
onPressed: () {
|
||||
controller.placeStartController.clear();
|
||||
controller.clearPlacesStart();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide.none,
|
||||
child: Row(
|
||||
children: [
|
||||
// --- حقل البحث النصي ---
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller.placeStartController,
|
||||
onChanged: (value) {
|
||||
if (controller.placeStartController.text.length > 2) {
|
||||
controller.getPlacesStart();
|
||||
} else if (controller.placeStartController.text.isEmpty) {
|
||||
controller.clearPlacesStart();
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search for a starting point'.tr,
|
||||
hintStyle:
|
||||
AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
prefixIcon:
|
||||
const Icon(Icons.search, color: AppColor.primaryColor),
|
||||
suffixIcon: controller.placeStartController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[400]),
|
||||
onPressed: () {
|
||||
controller.placeStartController.clear();
|
||||
controller.clearPlacesStart();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(color: AppColor.primaryColor),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey[50],
|
||||
),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(color: AppColor.primaryColor),
|
||||
|
||||
const SizedBox(width: 8.0),
|
||||
|
||||
// --- أيقونة اختيار الموقع من الخريطة (الجزء المضاف) ---
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
// هذا السطر مهم جداً: نخبر الكونترولر أننا نحدد نقطة البداية الآن
|
||||
controller.passengerStartLocationFromMap = true;
|
||||
|
||||
// إخفاء القائمة السفلية وفتح مؤشر الخريطة (Picker)
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.changePickerShown();
|
||||
},
|
||||
icon: Icon(Icons.location_on_outlined,
|
||||
color: AppColor.accentColor, size: 30),
|
||||
tooltip: 'Pick start point on map'.tr,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey[50],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// --- قائمة نتائج البحث ---
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: controller.placesStart.isNotEmpty ? 300 : 0,
|
||||
@@ -84,11 +112,19 @@ GetBuilder<MapPassengerController> formSearchPlacesStart() {
|
||||
var latitude = res['latitude'];
|
||||
var longitude = res['longitude'];
|
||||
if (latitude != null && longitude != null) {
|
||||
// تحديث موقع الراكب (نقطة الانطلاق) بناءً على الاختيار
|
||||
controller.passengerLocation =
|
||||
LatLng(double.parse(latitude), double.parse(longitude));
|
||||
|
||||
// تحديث النص في الحقل
|
||||
controller.placeStartController.text = title;
|
||||
|
||||
// مسح النتائج
|
||||
controller.clearPlacesStart();
|
||||
// You might want to update the camera position on the map here
|
||||
|
||||
// إغلاق القائمة والعودة للخريطة لرؤية النتيجة (اختياري حسب منطق تطبيقك)
|
||||
controller.changeMainBottomMenuMap();
|
||||
|
||||
controller.update();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -147,7 +147,7 @@ class GoogleMapPassengerWidget extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
|
||||
myLocationEnabled: true,
|
||||
myLocationEnabled: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/vip_waitting_page.dart';
|
||||
import '../../../env/env.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../auth/otp_page.dart';
|
||||
|
||||
// --- الدالة الرئيسية بالتصميم الجديد ---
|
||||
GetBuilder<MapPassengerController> leftMainMenuIcons() {
|
||||
@@ -35,9 +36,9 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
|
||||
duration: const Duration(milliseconds: 300),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor.withOpacity(0.8), // لون شبه شفاف
|
||||
color: AppColor.secondaryColor.withOpacity(0.4), // لون شبه شفاف
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
border: Border.all(color: AppColor.writeColor.withOpacity(0.2)),
|
||||
border: Border.all(color: AppColor.secondaryColor),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -49,12 +50,12 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
|
||||
tooltip: 'Toggle Map Type',
|
||||
onPressed: () => controller.changeMapType(),
|
||||
),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Icons.traffic_outlined,
|
||||
tooltip: 'Toggle Traffic',
|
||||
onPressed: () => controller.changeMapTraffic(),
|
||||
),
|
||||
// _buildVerticalDivider(),
|
||||
// _buildMapActionButton(
|
||||
// icon: Icons.traffic_outlined,
|
||||
// tooltip: 'Toggle Traffic',
|
||||
// onPressed: () => controller.changeMapTraffic(),
|
||||
// ),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Icons.my_location_rounded,
|
||||
@@ -129,22 +130,9 @@ 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:
|
||||
// 'eznj5vRWRnqwKNtKJBaYNg:APA91bHhJ2DJ1KQa3KRx6wQtX8BkFHq6I_-dXGxT16p6pnV5AwI0bWOeiTJOI35VfTBaK4YSCKmAB4SsRnpARK0MTJ96xtpPmwAKfkvsZFga8OoGMeb3PmA',
|
||||
// title: 'Order',
|
||||
// body: 'endNameAddress',
|
||||
// isTopic: false,
|
||||
// tone: 'tone1',
|
||||
// category: 'Order', // استخدام الفئة الثابتة
|
||||
// driverList: []);
|
||||
// RideState.driverApplied;
|
||||
// Get.find<MapPassengerController>().Ride
|
||||
// print(box.read(BoxName.lowEndMode));
|
||||
// box.read(BoxName.lowEndMode)
|
||||
Get.to(PhoneNumberScreen());
|
||||
},
|
||||
child: Text(
|
||||
"Text Button",
|
||||
|
||||
@@ -533,12 +533,12 @@ class MainBottomMenuMap extends StatelessWidget {
|
||||
|
||||
// else
|
||||
|
||||
Text(
|
||||
controller.nearestCar != null
|
||||
? 'Nearest Car: ${controller.nearestDistance.toStringAsFixed(0)} m'
|
||||
: 'No cars nearby'.tr,
|
||||
style: TextStyle(color: Colors.grey.shade600),
|
||||
),
|
||||
// Text(
|
||||
// controller.nearestCar != null
|
||||
// ? 'Nearest Car: ${controller.nearestDistance.toStringAsFixed(0)} m'
|
||||
// : 'No cars nearby'.tr,
|
||||
// style: TextStyle(color: Colors.grey.shade600),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,297 +1,10 @@
|
||||
// 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';
|
||||
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/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../constant/style.dart';
|
||||
@@ -299,6 +12,9 @@ import '../../../controller/functions/audio_record1.dart';
|
||||
import '../../../controller/functions/launch.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/profile/profile_controller.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../../views/home/profile/complaint_page.dart';
|
||||
|
||||
class RideBeginPassenger extends StatelessWidget {
|
||||
const RideBeginPassenger({super.key});
|
||||
@@ -312,70 +28,69 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
return Obx(() {
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
|
||||
// شرط الإظهار: تظهر فقط عندما تكون الرحلة جارية
|
||||
// شرط الإظهار
|
||||
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,
|
||||
curve: Curves.easeInOutCubic,
|
||||
// تم تقليل قيمة الإخفاء لأن الارتفاع الكلي للنافذة أصبح أصغر
|
||||
bottom: isVisible ? 0 : -300,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // خلفية بيضاء لنظافة التصميم
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30),
|
||||
topLeft: Radius.circular(25),
|
||||
topRight: Radius.circular(25),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.15),
|
||||
blurRadius: 25,
|
||||
spreadRadius: 5,
|
||||
offset: const Offset(0, -5),
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 2,
|
||||
offset: const Offset(0, -3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 12, 20, 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// مقبض السحب
|
||||
Container(
|
||||
width: 50,
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
// 1. مقبض السحب
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// الصف العلوي: معلومات السائق + السعر المثبت
|
||||
_buildDriverAndPriceSection(controller),
|
||||
// 2. هيدر المعلومات (سائق + سيارة + سعر)
|
||||
_buildCompactHeader(controller),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// الصف الأوسط: لوحة السيارة الواقعية + نوع السيارة
|
||||
_buildCarInfoSection(controller),
|
||||
// خط فاصل خفيف
|
||||
const Divider(
|
||||
height: 1, thickness: 0.5, color: Color(0xFFEEEEEE)),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// شريط التقدم والوقت
|
||||
_buildTripProgress(controller),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
const Divider(thickness: 1, color: Color(0xFFEEEEEE)),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// الأزرار
|
||||
_buildActionButtons(
|
||||
// 3. الأزرار (إجراءات)
|
||||
_buildCompactActionButtons(
|
||||
context, controller, profileController, audioController),
|
||||
|
||||
// إضافة هامش سفلي بسيط لرفع الأزرار عن حافة الشاشة
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -384,56 +99,81 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
});
|
||||
}
|
||||
|
||||
// ويدجت معلومات السائق والسعر
|
||||
Widget _buildDriverAndPriceSection(MapPassengerController controller) {
|
||||
// --- الهيدر (بدون تغيير، ممتاز) ---
|
||||
Widget _buildCompactHeader(MapPassengerController controller) {
|
||||
return Row(
|
||||
children: [
|
||||
// صورة السائق
|
||||
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),
|
||||
),
|
||||
],
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor.withOpacity(0.5), width: 1.5),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 30,
|
||||
radius: 24,
|
||||
backgroundImage: NetworkImage(
|
||||
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
|
||||
onBackgroundImageError: (_, __) => const Icon(Icons.person),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
const SizedBox(width: 10),
|
||||
|
||||
// الاسم والتقييم
|
||||
// الاسم ومعلومات السيارة
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
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),
|
||||
Flexible(
|
||||
child: Text(
|
||||
controller.driverName,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15,
|
||||
color: Colors.black87,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
const Icon(Icons.star, color: Colors.amber, size: 14),
|
||||
Text(
|
||||
controller.driverRate,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
fontWeight: FontWeight.bold, color: Colors.grey[600]),
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${controller.model} • ',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
border: Border.all(color: Colors.black12),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
controller.licensePlate,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -441,27 +181,26 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
|
||||
// السعر المثبت (تصميم كارد للسعر)
|
||||
// السعر
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.primaryColor.withOpacity(0.2)),
|
||||
color: AppColor.primaryColor.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text('Total'.tr,
|
||||
style: TextStyle(fontSize: 10, color: Colors.grey[600])),
|
||||
Text(
|
||||
'${NumberFormat('#,###').format(controller.totalPassenger)} 💰',
|
||||
NumberFormat('#,###').format(controller.totalPassenger),
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
Text('SYP',
|
||||
style: TextStyle(fontSize: 9, color: Colors.grey[600])),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -469,163 +208,22 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// ويدجت معلومات السيارة ولوحة الأرقام
|
||||
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: [
|
||||
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: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Colors.grey[200],
|
||||
color: controller.remainingTimeTimerRideBegin < 60
|
||||
? AppColor.redColor
|
||||
: AppColor.primaryColor,
|
||||
minHeight: 8,
|
||||
value: controller.progressTimerRideBegin.toDouble(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons(
|
||||
// --- الأزرار (بدون تغيير) ---
|
||||
Widget _buildCompactActionButtons(
|
||||
BuildContext context,
|
||||
MapPassengerController controller,
|
||||
ProfileController profileController,
|
||||
AudioRecorderController audioController) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// زر SOS بتصميم تحذيري
|
||||
_buildActionButton(
|
||||
return SizedBox(
|
||||
height: 60,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_compactBtn(
|
||||
icon: Icons.sos_rounded,
|
||||
label: 'SOS',
|
||||
iconColor: Colors.white,
|
||||
bgColor: AppColor.redColor,
|
||||
label: 'SOS'.tr,
|
||||
color: AppColor.redColor,
|
||||
bgColor: AppColor.redColor.withOpacity(0.1),
|
||||
onTap: () async {
|
||||
if (box.read(BoxName.sosPhonePassenger) == null) {
|
||||
await profileController.updatField(
|
||||
@@ -635,123 +233,100 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
} else {
|
||||
makePhoneCall('112');
|
||||
}
|
||||
}),
|
||||
|
||||
// زر واتساب
|
||||
_buildActionButton(
|
||||
},
|
||||
),
|
||||
_compactBtn(
|
||||
icon: FontAwesome.whatsapp,
|
||||
label: 'WhatsApp',
|
||||
iconColor: Colors.white,
|
||||
bgColor: const Color(0xFF25D366),
|
||||
label: 'WhatsApp'.tr,
|
||||
color: const Color(0xFF25D366),
|
||||
bgColor: const Color(0xFF25D366).withOpacity(0.1),
|
||||
onTap: () async {
|
||||
if (box.read(BoxName.sosPhonePassenger) == null ||
|
||||
box.read(BoxName.sosPhonePassenger) == 'sos') {
|
||||
if (box.read(BoxName.sosPhonePassenger) == null) {
|
||||
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);
|
||||
final phone = controller.formatSyrianPhoneNumber(
|
||||
box.read(BoxName.sosPhonePassenger).toString());
|
||||
controller.sendWhatsapp(phone);
|
||||
}
|
||||
}),
|
||||
|
||||
// زر المشاركة
|
||||
_buildActionButton(
|
||||
icon: Icons.share_location_rounded,
|
||||
label: 'Share',
|
||||
iconColor: AppColor.primaryColor,
|
||||
},
|
||||
),
|
||||
_compactBtn(
|
||||
icon: Icons.share,
|
||||
label: 'Share'.tr,
|
||||
color: AppColor.primaryColor,
|
||||
bgColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
onTap: () async {
|
||||
await controller.shareTripWithFamily();
|
||||
}),
|
||||
|
||||
// زر التسجيل
|
||||
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.report_gmailerrorred_rounded,
|
||||
onTap: () async => await controller.shareTripWithFamily(),
|
||||
),
|
||||
GetBuilder<AudioRecorderController>(
|
||||
init: audioController,
|
||||
builder: (audioCtx) {
|
||||
return _compactBtn(
|
||||
icon: audioCtx.isRecording
|
||||
? Icons.stop_circle_outlined
|
||||
: Icons.mic_none_outlined,
|
||||
label: audioCtx.isRecording ? 'Stop'.tr : 'Record'.tr,
|
||||
color: audioCtx.isRecording
|
||||
? AppColor.redColor
|
||||
: AppColor.primaryColor,
|
||||
bgColor: audioCtx.isRecording
|
||||
? AppColor.redColor.withOpacity(0.1)
|
||||
: AppColor.primaryColor.withOpacity(0.1),
|
||||
onTap: () async {
|
||||
if (!audioCtx.isRecording) {
|
||||
await audioCtx.startRecording();
|
||||
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
|
||||
} else {
|
||||
await audioCtx.stopRecording();
|
||||
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
_compactBtn(
|
||||
icon: Icons.info_outline_rounded,
|
||||
label: 'Report'.tr,
|
||||
iconColor: Colors.grey[700]!,
|
||||
color: Colors.grey[700]!,
|
||||
bgColor: Colors.grey[200]!,
|
||||
onTap: () {
|
||||
Get.to(() => ComplaintPage(), transition: Transition.downToUp);
|
||||
}),
|
||||
],
|
||||
onTap: () => Get.to(() => ComplaintPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton({
|
||||
Widget _compactBtn({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required Color iconColor,
|
||||
required Color color,
|
||||
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,
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
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),
|
||||
),
|
||||
],
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(icon, color: iconColor, size: 24),
|
||||
child: Icon(icon, size: 20, color: color),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label.tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black54,
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey[700],
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ void showPaymentBottomSheet(BuildContext context) {
|
||||
_buildPaymentOption(
|
||||
context: context,
|
||||
controller: controller,
|
||||
amount: 10000,
|
||||
bonusAmount: 0,
|
||||
amount: 500,
|
||||
bonusAmount: 30,
|
||||
currency: 'SYP'.tr,
|
||||
),
|
||||
|
||||
@@ -160,8 +160,8 @@ void showPaymentBottomSheet(BuildContext context) {
|
||||
_buildPaymentOption(
|
||||
context: context,
|
||||
controller: controller,
|
||||
amount: 20000,
|
||||
bonusAmount: 500,
|
||||
amount: 1000,
|
||||
bonusAmount: 70,
|
||||
currency: 'SYP'.tr,
|
||||
),
|
||||
|
||||
@@ -169,8 +169,8 @@ void showPaymentBottomSheet(BuildContext context) {
|
||||
_buildPaymentOption(
|
||||
context: context,
|
||||
controller: controller,
|
||||
amount: 40000,
|
||||
bonusAmount: 2500,
|
||||
amount: 2000,
|
||||
bonusAmount: 180,
|
||||
currency: 'SYP'.tr,
|
||||
),
|
||||
|
||||
@@ -178,8 +178,8 @@ void showPaymentBottomSheet(BuildContext context) {
|
||||
_buildPaymentOption(
|
||||
context: context,
|
||||
controller: controller,
|
||||
amount: 100000,
|
||||
bonusAmount: 4000,
|
||||
amount: 5000,
|
||||
bonusAmount: 700,
|
||||
currency: 'SYP'.tr,
|
||||
),
|
||||
|
||||
@@ -466,18 +466,24 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
|
||||
Get.to(() => PaymentScreenSmsProvider(
|
||||
amount: double.parse(controller.selectedAmount.toString())));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Pay by Sham Cash'.tr),
|
||||
const SizedBox(width: 10),
|
||||
Image.asset(
|
||||
'assets/images/shamCash.png',
|
||||
width: 70,
|
||||
height: 70,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Pay by Sham Cash'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Image.asset(
|
||||
'assets/images/shamCash.png',
|
||||
width: 70,
|
||||
height: 70,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
|
||||
@@ -8,9 +8,8 @@ import '../../../constant/links.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
// خدمة الدفع للراكب (تم تحديث المسارات)
|
||||
// --- خدمة الدفع (نفس المنطق السابق) ---
|
||||
class PaymentService {
|
||||
// المسار الجديد لمجلد الركاب
|
||||
final String _baseUrl = "${AppLink.paymentServer}/ride/shamcash/passenger";
|
||||
|
||||
Future<String?> createInvoice({required double amount}) async {
|
||||
@@ -19,7 +18,7 @@ class PaymentService {
|
||||
final response = await CRUD().postWallet(
|
||||
link: url,
|
||||
payload: {
|
||||
'passengerID': box.read(BoxName.passengerID), // استخدام passengerID
|
||||
'passengerID': box.read(BoxName.passengerID),
|
||||
'amount': amount.toString(),
|
||||
},
|
||||
).timeout(const Duration(seconds: 15));
|
||||
@@ -83,21 +82,46 @@ class PaymentScreenSmsProvider extends StatefulWidget {
|
||||
_PaymentScreenSmsProviderState();
|
||||
}
|
||||
|
||||
class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final PaymentService _paymentService = PaymentService();
|
||||
Timer? _pollingTimer;
|
||||
PaymentStatus _status = PaymentStatus.creatingInvoice;
|
||||
String? _invoiceNumber;
|
||||
|
||||
// العنوان الثابت للدفع (المستخرج من الصورة)
|
||||
final String _paymentAddress = "80f23afe40499b02f49966c3340ae0fc";
|
||||
|
||||
// متحكم الأنيميشن للوميض
|
||||
late AnimationController _blinkController;
|
||||
late Animation<Color?> _colorAnimation;
|
||||
late Animation<double> _shadowAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// إعداد الأنيميشن (وميض أحمر)
|
||||
_blinkController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
)..repeat(reverse: true); // يكرر الحركة ذهاباً وإياباً
|
||||
|
||||
_colorAnimation = ColorTween(
|
||||
begin: Colors.red.shade700,
|
||||
end: Colors.red.shade100,
|
||||
).animate(_blinkController);
|
||||
|
||||
_shadowAnimation = Tween<double>(begin: 2.0, end: 15.0).animate(
|
||||
CurvedAnimation(parent: _blinkController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_createAndPollInvoice();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pollingTimer?.cancel();
|
||||
_blinkController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -216,7 +240,7 @@ class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
// 1. المبلغ
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 15),
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.blue.shade800, Colors.blue.shade600]),
|
||||
@@ -224,97 +248,166 @@ class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.blue.withOpacity(0.25),
|
||||
blurRadius: 15,
|
||||
offset: const Offset(0, 8))
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 5))
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Text("المبلغ المطلوب",
|
||||
style: TextStyle(color: Colors.white70, fontSize: 14)),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 5),
|
||||
Text("${currencyFormat.format(widget.amount)} ل.س",
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
const SizedBox(height: 25),
|
||||
|
||||
// 2. التعليمات والنسخ (للراكب)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.shade100,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4))
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
// 2. رقم البيان (هام جداً - وميض أحمر)
|
||||
AnimatedBuilder(
|
||||
animation: _blinkController,
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: _colorAnimation.value ?? Colors.red,
|
||||
width: 3.0, // إطار سميك
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: (_colorAnimation.value ?? Colors.red)
|
||||
.withOpacity(0.4),
|
||||
blurRadius: _shadowAnimation.value,
|
||||
spreadRadius: 2,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade50, shape: BoxShape.circle),
|
||||
child: Icon(Icons.priority_high_rounded,
|
||||
color: Colors.orange.shade800, size: 20),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
"انسخ الرقم أدناه وضعه في خانة (الملاحظات) عند الدفع.",
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.warning_rounded,
|
||||
color: Colors.red.shade800, size: 28),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"هام جداً: لا تنسَ!",
|
||||
style: TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.w600)),
|
||||
color: Colors.red.shade900,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
"يجب نسخ (رقم البيان) هذا ووضعه في تطبيق شام كاش لضمان نجاح العملية.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: invoiceText));
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: const Text("تم نسخ رقم البيان ✅",
|
||||
textAlign: TextAlign.center),
|
||||
backgroundColor: Colors.red.shade700));
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade50,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.red.shade200, width: 1)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("رقم البيان (Invoice No)",
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: Colors.grey)),
|
||||
Text(invoiceText,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 2.0,
|
||||
color: Colors.red.shade900)),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade100,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.copy_rounded,
|
||||
color: Colors.red.shade900, size: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
|
||||
// 3. عنوان الدفع (اختياري / عادي)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("عنوان الدفع (Payment Address)",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: invoiceText));
|
||||
Clipboard.setData(ClipboardData(text: _paymentAddress));
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: const Text("تم نسخ رقم البيان ✅",
|
||||
content: const Text("تم نسخ عنوان الدفع ✅",
|
||||
textAlign: TextAlign.center),
|
||||
backgroundColor: Colors.green.shade600));
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.blue.shade200, width: 1.5)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("رقم البيان (Invoice ID)",
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: Colors.grey)),
|
||||
Text(invoiceText,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 1.5)),
|
||||
],
|
||||
),
|
||||
const Icon(Icons.copy_rounded,
|
||||
color: Colors.blue, size: 24),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(_paymentAddress,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'Courier',
|
||||
color: Colors.black87,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Icon(Icons.copy, size: 18, color: Colors.grey),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -322,10 +415,10 @@ class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// 3. QR Code
|
||||
const Text("امسح الرمز للدفع",
|
||||
// 4. QR Code
|
||||
const Text("أو امسح الرمز للدفع",
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 10),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
@@ -336,30 +429,23 @@ class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
child: Image.asset(widget.qrImagePath))));
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.grey.shade300)),
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(widget.qrImagePath,
|
||||
width: 180,
|
||||
height: 180,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (c, o, s) => const Icon(Icons.qr_code_2,
|
||||
size: 100, color: Colors.grey)),
|
||||
const SizedBox(height: 8),
|
||||
const Text("اضغط للتكبير",
|
||||
style: TextStyle(fontSize: 10, color: Colors.grey)),
|
||||
],
|
||||
),
|
||||
child: Image.asset(widget.qrImagePath,
|
||||
width: 150,
|
||||
height: 150,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (c, o, s) => const Icon(Icons.qr_code_2,
|
||||
size: 100, color: Colors.grey)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
const SizedBox(height: 30),
|
||||
const LinearProgressIndicator(backgroundColor: Colors.white),
|
||||
const SizedBox(height: 10),
|
||||
const Text("ننتظر إشعار الدفع تلقائياً...",
|
||||
const Text("جاري التحقق من الدفع تلقائياً...",
|
||||
style: TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
@@ -405,7 +491,7 @@ class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider> {
|
||||
const SizedBox(height: 15),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30),
|
||||
child: Text("لم يصلنا إشعار الدفع.",
|
||||
child: Text("لم يصلنا إشعار الدفع خلال الوقت المحدد.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.grey))),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
Reference in New Issue
Block a user