Files
intaleq_driver/lib/views/home/Captin/driver_map_page.dart
Hamza-Ayed 3c0ae4cf2f 26-1-20/1
2026-01-20 10:11:10 +03:00

449 lines
18 KiB
Dart
Executable File

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import '../../../constant/colors.dart';
import '../../../controller/functions/location_controller.dart';
import '../../../main.dart';
import '../../Rate/rate_passenger.dart';
import '../../widgets/my_textField.dart';
import 'mapDriverWidgets/driver_end_ride_bar.dart';
import 'mapDriverWidgets/google_driver_map_page.dart';
import 'mapDriverWidgets/google_map_app.dart';
import 'mapDriverWidgets/passenger_info_window.dart';
import 'mapDriverWidgets/sos_connect.dart';
import 'mapDriverWidgets/sped_circle.dart';
class PassengerLocationMapPage extends StatelessWidget {
PassengerLocationMapPage({super.key});
final LocationController locationController = Get.put(LocationController());
final MapDriverController mapDriverController =
Get.put(MapDriverController());
// دالة ديالوج الخروج
Future<bool> showExitDialog() async {
bool? result = await Get.defaultDialog(
title: "Warning".tr,
titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
middleText:
"Active ride in progress. Leaving might stop tracking. Exit?".tr,
barrierDismissible: false,
radius: 15,
confirm: MyElevatedButton(
title: 'Stay'.tr,
kolor: AppColor.greenColor,
onPressed: () => Get.back(result: false)),
cancel: MyElevatedButton(
title: 'Exit'.tr,
kolor: AppColor.redColor,
onPressed: () => Get.back(result: true)),
);
return result ?? false;
}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
mapDriverController.argumentLoading();
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
// 2. فرض التحديث لكل المعرفات (IDs) لضمان ظهورها
// لأن argumentLoading قد تستدعي update() العادية التي لا تؤثر على هؤلاء
mapDriverController
.update(['PassengerInfo', 'DriverEndBar', 'SosConnect']);
}
});
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
if (didPop) return;
final shouldExit = await showExitDialog();
if (shouldExit) Get.back();
},
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
// 1. الخريطة (الخلفية)
Positioned.fill(
child: GoogleDriverMap(locationController: locationController)),
// 2. واجهة المستخدم (فوق الخريطة)
SafeArea(
child: Stack(
children: [
// أ) زر الإلغاء (أعلى اليسار)
CancelWidget(mapDriverController: mapDriverController),
// ب) شريط إنهاء الرحلة (أعلى الوسط)
Positioned(
top: 0,
left: 0,
right: 0,
child: SafeArea(child: driverEndRideBar())),
// ج) شريط التعليمات الملاحية (الأسفل)
const InstructionsOfRoads(),
// د) نافذة معلومات الراكب (تعلو التعليمات ديناميكياً)
const PassengerInfoWindow(),
// SpeedCircle(),
Positioned(
right: 16,
bottom: 20, // أو أي مسافة تناسبك
child: GetBuilder<MapDriverController>(
// id: 'SosConnect', // لتحديث الزر عند بدء الرحلة
builder: (controller) {
// حساب الهوامش ديناميكياً لرفع الأزرار فوق النوافذ السفلية
double bottomPadding = 0;
if (controller.currentInstruction.isNotEmpty)
bottomPadding += 120;
if (controller.isPassengerInfoWindow)
bottomPadding += 220;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: EdgeInsets.only(bottom: bottomPadding),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SosConnect(), // ويدجت نظيفة
const SizedBox(height: 12),
const GoogleMapApp(), // ويدجت نظيفة
],
),
);
},
),
),
],
),
),
// 3. النوافذ المنبثقة (Overlay)
const PricesWindow(),
],
),
),
);
}
}
// ---------------------------------------------------------------------------
// 1. ويدجت شريط التعليمات (InstructionsOfRoads)
// ---------------------------------------------------------------------------
class InstructionsOfRoads extends StatelessWidget {
const InstructionsOfRoads({super.key});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 20,
left: 15,
right: 15,
child: GetBuilder<MapDriverController>(
builder: (controller) {
// إخفاء الشريط إذا لم يكن هناك تعليمات
if (controller.currentInstruction.isEmpty) return const SizedBox();
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 500),
builder: (context, value, child) {
return Transform.translate(
offset: Offset(0, 50 * (1 - value)), // حركة انزلاق
child: Opacity(
opacity: value,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: const Color(0xFF1F1F1F)
.withOpacity(0.95), // خلفية داكنة
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.4),
blurRadius: 15,
offset: const Offset(0, 5)),
],
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
// أيقونة الاتجاه
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.primaryColor,
shape: BoxShape.circle,
),
child: const Icon(Icons.turn_right_rounded,
color: Colors.white, size: 24),
),
const SizedBox(width: 14),
// نص التعليمات
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"NEXT STEP".tr,
style: TextStyle(
color: Colors.grey.shade500,
fontSize: 10,
fontWeight: FontWeight.bold,
letterSpacing: 1.2),
),
const SizedBox(height: 2),
Text(
controller.currentInstruction,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 1.2),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
// فاصل عمودي
Container(
width: 1,
height: 30,
color: Colors.white12,
margin: const EdgeInsets.symmetric(horizontal: 10)),
// زر التحكم بالصوت
Material(
color: Colors.transparent,
child: InkWell(
onTap: () => controller.toggleTts(),
borderRadius: BorderRadius.circular(20),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
controller.isTtsEnabled
? Icons.volume_up_rounded
: Icons.volume_off_rounded,
color: controller.isTtsEnabled
? AppColor.greenColor
: Colors.grey,
size: 24,
),
),
),
),
],
),
),
),
);
},
);
},
),
);
}
}
// ---------------------------------------------------------------------------
// 2. ويدجت زر الإلغاء (CancelWidget) - كامل
// ---------------------------------------------------------------------------
class CancelWidget extends StatelessWidget {
const CancelWidget({super.key, required this.mapDriverController});
final MapDriverController mapDriverController;
@override
Widget build(BuildContext context) {
return Positioned(
top: 10,
left: 15,
child: GetBuilder<MapDriverController>(builder: (controller) {
// نخفي الزر إذا انتهت الرحلة
if (controller.isRideFinished) return const SizedBox();
return ClipRRect(
borderRadius: BorderRadius.circular(30),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 8)
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: () => _showCancelDialog(context, controller),
child: const Padding(
padding: EdgeInsets.all(10.0),
child: Icon(Icons.close_rounded,
color: AppColor.redColor, size: 26),
),
),
),
),
),
);
}),
);
}
void _showCancelDialog(BuildContext context, MapDriverController controller) {
Get.defaultDialog(
title: "Cancel Trip?".tr,
titleStyle: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
radius: 16,
content: Column(
children: [
const Icon(Icons.warning_amber_rounded,
size: 50, color: Colors.orangeAccent),
const SizedBox(height: 10),
Text(
"Please tell us why you want to cancel.".tr,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey[600]),
),
const SizedBox(height: 15),
Form(
key: controller.formKeyCancel,
child: MyTextForm(
controller: controller.cancelTripCotroller,
label: "Reason".tr,
hint: "Write your reason...".tr,
type: TextInputType.text,
),
),
],
),
confirm: SizedBox(
width: 100,
child: MyElevatedButton(
title: 'Confirm'.tr,
kolor: AppColor.redColor,
onPressed: () async {
// استدعاء دالة الإلغاء من الكنترولر
await controller.cancelTripFromDriverAfterApplied();
// Get.back(); // عادة موجودة داخل الدالة في الكنترولر
},
),
),
cancel: SizedBox(
width: 100,
child: TextButton(
onPressed: () => Get.back(),
child: Text('Back'.tr, style: const TextStyle(color: Colors.grey)),
),
),
);
}
}
// ---------------------------------------------------------------------------
// 3. ويدجت نافذة الأسعار (PricesWindow) - كامل
// ---------------------------------------------------------------------------
class PricesWindow extends StatelessWidget {
const PricesWindow({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(builder: (controller) {
// إخفاء إذا لم تكن مفعلة
if (!controller.isPriceWindow) return const SizedBox();
return Container(
color: Colors.black.withOpacity(0.6), // خلفية معتمة
child: Center(
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0.8, end: 1.0),
duration: const Duration(milliseconds: 300),
curve: Curves.elasticOut,
builder: (context, scale, child) {
return Transform.scale(
scale: scale,
child: Container(
width: Get.width * 0.85,
padding: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
spreadRadius: 5,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(Icons.check_circle_rounded,
color: AppColor.primaryColor, size: 50),
),
const SizedBox(height: 20),
Text(
'Total Price'.tr,
style: AppStyle.headTitle2
.copyWith(fontSize: 18, color: Colors.grey[600]),
),
const SizedBox(height: 10),
Text(
'${controller.totalCost} ${'\$'.tr}',
style: AppStyle.headTitle2.copyWith(
color: Colors.black87,
fontSize: 42,
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: 30),
SizedBox(
width: double.infinity,
height: 55,
child: MyElevatedButton(
title: 'Collect Payment'.tr,
kolor: AppColor.primaryColor,
onPressed: () {
// الذهاب لصفحة التقييم
Get.to(() => RatePassenger(), arguments: {
'rideId': controller.rideId,
'passengerId': controller.passengerId,
'driverId': controller.driverId,
'price': controller.paymentAmount,
'walletChecked': controller.walletChecked
});
},
),
),
],
),
),
);
},
),
),
);
});
}
}