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

764 lines
29 KiB
Dart
Executable File

import 'dart:io';
import 'dart:ui';
import 'package:bubble_head/bubble.dart';
// import 'package:google_maps_flutter/google_maps_flutter.dart';
// import 'package:flutter_map/flutter_map.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
// import 'package:latlong2/latlong.dart' as latlng; // لإحداثيات العرض
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:sefer_driver/views/home/Captin/home_captain/drawer_captain.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/info.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/location_background_controller.dart';
import '../../../../controller/functions/location_controller.dart';
import '../../../../controller/functions/overlay_permisssion.dart';
import '../../../../controller/functions/package_info.dart';
import '../../../../controller/home/captin/home_captain_controller.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../../main.dart';
import '../../../notification/available_rides_page.dart';
import '../../../widgets/circle_container.dart';
import '../driver_map_page.dart';
import 'widget/connect.dart';
import 'widget/left_menu_map_captain.dart';
// ==================================================================
// Redesigned Main Widget (V3)
// ==================================================================
class HomeCaptain extends StatelessWidget {
HomeCaptain({super.key});
final LocationController locationController =
Get.put(LocationController(), permanent: true);
final HomeCaptainController homeCaptainController =
Get.put(HomeCaptainController());
@override
Widget build(BuildContext context) {
// Initial calls remain the same.
// Get.put(HomeCaptainController());
WidgetsBinding.instance.addPostFrameCallback((_) async {
print("🔥 HomeCaptain postFrameCallback started"); // Debug
await closeOverlayIfFound();
await checkForUpdate(context);
await getPermissionOverlay();
await showDriverGiftClaim(context);
await checkForAppliedRide(context);
print("✅ postFrameCallback completed");
});
// The stack is now even simpler.
return Scaffold(
appBar: const _HomeAppBar(),
drawer: AppDrawer(),
body: SafeArea(
child: Stack(
children: [
// 1. The Map View is the base layer.
const _MapView(),
// 2. The new floating "Status Pod" at the bottom.
const _StatusPodOverlay(),
FloatingActionButtons(),
// This widget from the original code remains.
leftMainMenuCaptainIcons(),
],
),
),
);
}
}
// ==================================================================
// Redesigned Helper Widgets (V3)
// ==================================================================
/// 1. The AppBar now contains the map actions in a PopupMenuButton.
class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
const _HomeAppBar();
@override
Widget build(BuildContext context) {
final homeCaptainController = Get.find<HomeCaptainController>();
return AppBar(
backgroundColor: Colors.white,
elevation: 1,
shadowColor: Colors.black.withOpacity(0.1),
title: Row(
children: [
Image.asset(
'assets/images/logo.gif',
height: 35,
),
const SizedBox(width: 10),
Text(
AppInformation.appName.split(' ')[0].toString().tr,
style: AppStyle.title.copyWith(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColor.blueColor,
),
),
],
),
actions: [
// Refuse count indicator
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Center(
child: MyCircleContainer(
child: Text(
homeCaptainController.countRefuse.toString(),
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
),
),
),
),
// The new PopupMenuButton for all map and ride actions.
Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
),
],
),
child: Row(
children: [
_MapControlButton(
iconColor: Colors.blue,
icon: Icons.satellite_alt,
tooltip: 'Change Map Type'.tr,
onPressed: homeCaptainController.changeMapType,
),
// _MapControlButton(
// iconColor: Colors.blue,
// icon: Icons.streetview_sharp,
// tooltip: 'Toggle Traffic'.tr,
// onPressed: homeCaptainController.changeMapTraffic,
// ),
GetBuilder<HomeCaptainController>(
builder: (controller) {
return _MapControlButton(
// تغيير الأيقونة واللون بناءً على الحالة
icon: Icons.local_fire_department,
iconColor: controller.isHeatmapVisible
? Colors.orange
: Colors.grey,
tooltip: 'Show Heatmap'.tr,
onPressed: controller.toggleHeatmap,
);
},
),
_MapControlButton(
iconColor: Colors.blue,
icon: Icons.my_location, // Changed for clarity
tooltip: 'Center on Me'.tr,
onPressed: () {
if (homeCaptainController.mapHomeCaptainController != null) {
homeCaptainController.mapHomeCaptainController!
.animateCamera(CameraUpdate.newLatLngZoom(
Get.find<LocationController>().myLocation,
17.5,
));
}
},
),
],
),
),
],
);
}
PopupMenuItem<String> _buildPopupMenuItem({
required String value,
IconData? icon,
Widget? iconWidget,
required String text,
Color? iconColor,
}) {
return PopupMenuItem<String>(
value: value,
child: Row(
children: [
iconWidget ?? Icon(icon, color: iconColor ?? Colors.grey.shade600),
const SizedBox(width: 16),
Text(text),
],
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
/// 2. The Map View is unchanged functionally.
class _MapView extends StatelessWidget {
const _MapView();
@override
Widget build(BuildContext context) {
// جلب الكونترولر الرئيسي
final homeController = Get.find<HomeCaptainController>();
return GetBuilder<HomeCaptainController>(
builder: (controller) {
if (controller.isLoading) {
return const MyCircularProgressIndicator();
}
// --- هذا هو التعديل ---
// هذا الـ Builder يستمع إلى تحديثات الموقع
return GetBuilder<LocationController>(
builder: (locationController) {
// if (homeController.mapHomeCaptainController != null &&
// homeController.isActive) {
// homeController.mapHomeCaptainController!.animateCamera(
// CameraUpdate.newCameraPosition(
// CameraPosition(
// target: locationController.myLocation, // الموقع الجديد
// zoom: 17.5, // تقريب لمتابعة السائق
// tilt: 50.0, // زاوية رؤية 3D
// bearing: locationController.heading, // اتجاه السيارة
// ),
// ),
// );
// }
// --- نهاية الكود الجديد ---
// إرجاع الخريطة
return GoogleMap(
padding: const EdgeInsets.only(bottom: 110, top: 300),
fortyFiveDegreeImageryEnabled: true,
onMapCreated: controller.onMapCreated,
// onCameraMove: controller.onCameraMove,
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
initialCameraPosition: CameraPosition(
target: locationController.myLocation,
zoom: 15,
),
// --- تم حذف onCameraMove الخاطئ ---
// === إضافة الطبقة الحرارية هنا ===
polygons: controller.heatmapPolygons,
// =
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: locationController.myLocation, // يتم تحديثه من هنا
rotation: locationController.heading, // يتم تحديثه من هنا
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
)
},
mapType: controller.mapType ? MapType.satellite : MapType.terrain,
myLocationButtonEnabled: false,
myLocationEnabled: false,
trafficEnabled: controller.mapTrafficON,
buildingsEnabled: false,
mapToolbarEnabled: false,
compassEnabled: false,
zoomControlsEnabled: false,
);
// --- الكود الجديد ---
// // تحويل الإحداثيات من جوجل (إذا كنت لا تزال تستخدمها) إلى latlong2
// final latlng.LatLng currentCarPosition = latlng.LatLng(
// locationController.myLocation.latitude,
// locationController.myLocation.longitude);
// return FlutterMap(
// // 1. تمرير الـ Controller الذي أنشأناه في الخطوة 2
// mapController: homeController.mapController,
// options: MapOptions(
// // 2. هذا بديل initialCameraPosition
// initialCenter: currentCarPosition,
// initialZoom: 15,
// // هذا بديل padding
// // (ملاحظة: flutter_map لا يدعم padding مباشرة، قد تحتاج لتعديل الواجهة
// // أو استخدام خاصية nonRotatedChildren لبدائل أخرى)
// // هذا بديل minMaxZoomPreference
// minZoom: 12,
// maxZoom: 16, onMapReady: homeController.onMapReady,
// ),
// // 3. الخرائط في flutter_map عبارة عن "طبقات" (Layers)
// children: [
// // --- الطبقة الأولى: الخريطة الأساسية (OSM) ---
// TileLayer(
// urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
// userAgentPackageName:
// 'com.your.app.name', // هام: ضع اسم تطبيقك هنا
// ),
// // --- (اختياري) طبقة القمر الصناعي بناءً على MapType ---
// if (controller.mapType) // إذا كنت لا تزال تستخدم mapType
// // TileLayer(
// // // ملاحظة: هذا الرابط يحتاج مفتاح API من MapTiler أو مزود آخر
// // urlTemplate:
// // 'https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=YOUR_API_KEY',
// // userAgentPackageName: 'com.your.app.name',
// // ),
// // --- الطبقة الثانية: أيقونة السيارة (Marker) ---
// MarkerLayer(
// markers: [
// Marker(
// point: currentCarPosition, // الإحداثيات
// width: 80,
// height: 80,
// child: Transform.rotate(
// // 4. هذا بديل rotation
// angle: locationController.heading *
// (3.1415926535 / 180), // تحويل من درجات إلى راديان
// // 5. هذا بديل carIcon (أصبح أسهل!)
// child: Image.asset(
// 'assets/images/car.png', // نفس المسار الذي استخدمته من قبل
// width: 30, // الحجم الذي حددته في addCustomCarIcon
// height: 35,
// ),
// ),
// ),
// ],
// ),
// ],
// );
},
);
},
);
}
}
/// 3. The floating "Status Pod" at the bottom of the screen.
class _StatusPodOverlay extends StatelessWidget {
const _StatusPodOverlay();
void _showDetailsDialog(
BuildContext context, HomeCaptainController controller) {
Get.dialog(
_DriverDetailsDialog(controller), // تمرير الكنترولر هنا
barrierColor: Colors.black.withOpacity(0.3),
);
}
@override
Widget build(BuildContext context) {
final homeCaptainController = Get.find<HomeCaptainController>();
return Positioned(
bottom: 16,
left: 16,
right: 16,
child: GestureDetector(
onTap: () => _showDetailsDialog(context, homeCaptainController),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.85),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.white.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
spreadRadius: -5,
)
],
),
child: Row(
children: [
const ConnectWidget(),
const Spacer(),
_buildQuickStat(
icon: Icons.directions_car_rounded,
value: homeCaptainController.countRideToday,
label: 'Rides'.tr,
color: AppColor.blueColor,
),
const SizedBox(width: 16),
_buildQuickStat(
icon: Entypo.wallet,
value: homeCaptainController.totalMoneyToday.toString(),
label: 'Today'.tr,
color: AppColor.greenColor,
),
const SizedBox(width: 8),
],
),
),
),
),
),
);
}
Widget _buildQuickStat(
{required IconData icon,
required String value,
required String label,
required Color color}) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 4),
Text(value,
style: AppStyle.title
.copyWith(fontSize: 16, fontWeight: FontWeight.bold)),
],
),
Text(label,
style: AppStyle.title
.copyWith(fontSize: 12, color: Colors.grey.shade700)),
],
);
}
}
/// 4. The Dialog that shows detailed driver stats.
class _DriverDetailsDialog extends StatelessWidget {
// 1. إضافة متغير للكنترولر
final HomeCaptainController controller;
// 2. تحديث البناء لاستقباله
const _DriverDetailsDialog(this.controller);
@override
Widget build(BuildContext context) {
// 3. حذف السطر الذي يسبب الخطأ: final homeCaptainController = Get.find...
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: AlertDialog(
backgroundColor: Colors.white.withOpacity(0.95),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
titlePadding: const EdgeInsets.only(top: 20),
title: Center(
child: Text(
'Your Activity'.tr,
style: AppStyle.title
.copyWith(fontSize: 20, fontWeight: FontWeight.bold),
),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(height: 20),
_buildStatRow(
icon: Entypo.wallet,
color: AppColor.greenColor,
label: 'Today'.tr,
// استخدام المتغير controller الذي تم تمريره
value: controller.totalMoneyToday.toString(),
),
const SizedBox(height: 12),
_buildStatRow(
icon: Entypo.wallet,
color: AppColor.yellowColor,
label: AppInformation.appName,
value: controller.totalMoneyInSEFER.toString(),
),
const Divider(height: 24),
_buildDurationRow(
icon: Icons.timer_outlined,
label: 'Active Duration:'.tr,
value: controller.stringActiveDuration,
color: AppColor.greenColor,
),
const SizedBox(height: 12),
_buildDurationRow(
icon: Icons.access_time,
label: 'Total Connection Duration:'.tr,
value: controller.totalDurationToday,
color: AppColor.accentColor,
),
const Divider(height: 24),
_buildStatRow(
icon: Icons.star_border_rounded,
color: AppColor.blueColor,
label: 'Total Points'.tr,
value: controller.totalPoints.toString(),
),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('Close'.tr,
style: AppStyle.title.copyWith(
color: AppColor.blueColor, fontWeight: FontWeight.bold)),
)
],
),
);
}
// ... بقية الدوال المساعدة (_buildStatRow, _buildDurationRow) تبقى كما هي ...
Widget _buildStatRow(
{required IconData icon,
required Color color,
required String label,
required String value}) {
return Row(
children: [
Icon(icon, color: color, size: 22),
const SizedBox(width: 12),
Text('$label:', style: AppStyle.title),
const Spacer(),
Text(
value,
style: AppStyle.title.copyWith(
color: color, fontWeight: FontWeight.bold, fontSize: 18),
),
],
);
}
Widget _buildDurationRow(
{required IconData icon,
required String label,
required String value,
required Color color}) {
return Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 12),
Text(label, style: AppStyle.title),
const Spacer(),
Text(
value,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold, color: color, fontSize: 16),
),
],
);
}
}
class _MapControlButton extends StatelessWidget {
final IconData icon;
final VoidCallback onPressed;
final String tooltip;
const _MapControlButton({
required this.icon,
required this.onPressed,
required this.tooltip,
required MaterialColor iconColor,
});
@override
Widget build(BuildContext context) {
return Tooltip(
message: tooltip,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onPressed,
child: Container(
padding: const EdgeInsets.all(8),
child: Icon(
icon,
size: 24,
color: AppColor.blueColor,
),
),
),
),
);
}
}
class FloatingActionButtons extends StatelessWidget {
const FloatingActionButtons();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي للأزرار
return Positioned(
bottom: Get.height * .2,
right: 6,
child:
GetBuilder<HomeCaptainController>(builder: (homeCaptainController) {
return Column(
children: [
Platform.isAndroid
? AnimatedContainer(
duration: const Duration(microseconds: 200),
width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(color: AppColor.blueColor),
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: IconButton(
onPressed: () async {
Bubble().startBubbleHead(sendAppToBackground: true);
},
icon: Image.asset(
'assets/images/logo1.png',
fit: BoxFit.cover,
width: 35,
height: 35,
),
),
)
: const SizedBox(),
const SizedBox(
height: 5,
),
AnimatedContainer(
duration: const Duration(microseconds: 200),
width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(color: AppColor.blueColor),
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: IconButton(
onPressed: () {
Get.to(() => const AvailableRidesPage());
},
icon: const Icon(
Icons.train_sharp,
size: 29,
color: AppColor.blueColor,
),
),
),
const SizedBox(
height: 5,
),
// هذا الكود يوضع داخل الـ Stack في ملف الواجهة (HomeCaptain View)
box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Begin'
? Positioned(
bottom: Get.height * .2,
// جعلنا الزر يظهر في المنتصف أو يمتد ليكون واضحاً جداً
right: 20,
left: 20,
child: Center(
child: AnimatedContainer(
duration: const Duration(
milliseconds:
200), // تم تصحيح microseconds إلى milliseconds لحركة أنعم
// أزلنا العرض الثابت homeCaptainController.widthMapTypeAndTraffic لكي يتسع للنص
// width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(
color: AppColor.blueColor,
width: 2), // تعريض الإطار قليلاً
color: AppColor.secondaryColor, // لون الخلفية
borderRadius: BorderRadius.circular(
30), // تدوير الحواف ليشبه الأزرار الحديثة
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
)
]),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onLongPress: () {
// وظيفة الحذف عند الضغط الطويل (للطوارئ)
box.write(BoxName.rideStatus, 'delete');
homeCaptainController.update();
},
onTap: () {
// نفس منطقك الأصلي للانتقال
if (box.read(BoxName.rideStatus) == 'Applied') {
Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments));
Get.put(MapDriverController())
.changeRideToBeginToPassenger();
} else {
Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments));
Get.put(MapDriverController())
.startRideFromStartApp();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 12),
child: Row(
mainAxisSize:
MainAxisSize.min, // حجم الزر على قد المحتوى
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons
.directions_car_filled_rounded, // تغيير الأيقونة لسيارة أو اتجاهات لتكون معبرة أكثر
size: 24,
color: AppColor.blueColor,
),
const SizedBox(
width: 10), // مسافة بين الأيقونة والنص
Text(
"متابعة الرحلة", // النص الواضح للسائق
style: const TextStyle(
color: AppColor.blueColor,
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily:
'Cairo', // تأكد من نوع الخط المستخدم عندك
),
),
if (box.read(BoxName.rideStatus) ==
'Begin') ...[
const SizedBox(width: 5),
// إضافة مؤشر صغير (نقطة حمراء) إذا كانت الرحلة قد بدأت بالفعل (اختياري)
const Icon(Icons.circle,
size: 8, color: Colors.green)
]
],
),
),
),
),
),
),
)
: const SizedBox()
],
);
}),
);
}
}
Future<void> checkForAppliedRide(BuildContext context) async {
checkForPendingOrderFromServer();
}