feat: N1d/N2/N3 + قانون فلاتر وإعادة ضبط تطبيق السائق
الباك إند: - تعيين أول أدمن صار يُنشئ المستخدم إن لم يوجد — كان يشترط دخولاً سابقاً، وهي بيضة ودجاجة تمنع الدخول إلى لوحة أي مستأجر جديد أصلاً. - OTP_DEV_MODE كان يفشل مفتوحاً (`!== 'false'`): غياب المتغيّر أو خطأ مطبعي يترك الإنتاج برمز ثابت يفتح كل حساب، ويحرس السحب المالي كذلك. صار `=== 'true'` في الإعداد وفي قارئَيه، مع تحذير عند الإقلاع. - N2: التحقّق من اللوغو عند الرفع (PNG/JPEG · 512+ · مربّع · سقف 5MB) عبر قراءة الترويسة بلا اعتمادية — بدل اكتشاف أيقونة ممطوطة بعد النشر. N3: السكربت يولّد build_config.dart بأعلام const (طبقات docs/22 §1.5)، ويضبط bundle IDs واسم التطبيق والأيقونات/splash، و--build يشغّل Shorebird. فلاتر: - docs/23: قانون مُلزِم للتطبيقين (البنية · Cubit · طبقة الشبكة · الأعلام). - السائق: أُزيل Dart القديم (GetX) مع الإبقاء على الأصيل والإضافات وشهادات التوقيع وShorebird وFirebase الخاص به، وأُعيد هيكلته مطابقاً للراكب. - طبقة الشبكة ترسل x-device-id وتجدّد التوكن عند 401 مرة واحدة فقط. - الحزم موحّدة بين التطبيقين، والـAPI https حصراً في الاثنين. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3ab74a13a8
commit
de4bbd00ac
@@ -1,375 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/functions/upload_image.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:sefer_driver/device_compatibility_page.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
|
||||
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
|
||||
import 'package:sefer_driver/views/Rate/rate_app_page.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/invite_driver_screen.dart';
|
||||
import 'package:sefer_driver/views/notification/available_rides_page.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/logout_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/history/history_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/help_captain.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/About Us/settings_captain.dart';
|
||||
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
|
||||
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
|
||||
import 'package:sefer_driver/views/notification/notification_captain.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../../constant/colors.dart';
|
||||
import '../About Us/video_page.dart';
|
||||
import '../assurance_health_page.dart';
|
||||
import '../maintain_center_page.dart';
|
||||
|
||||
// 1. إنشاء Class لتعريف بيانات كل عنصر في القائمة
|
||||
class DrawerItem {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
final VoidCallback onTap;
|
||||
|
||||
DrawerItem(
|
||||
{required this.title,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
required this.onTap});
|
||||
}
|
||||
|
||||
// --- الويدجت الرئيسية للقائمة الجانبية ---
|
||||
class AppDrawer extends StatelessWidget {
|
||||
AppDrawer({super.key});
|
||||
|
||||
final ImageController imageController = Get.put(ImageController());
|
||||
|
||||
// 2. تعريف بيانات القائمة بشكل مركزي ومنظم
|
||||
final List<DrawerItem> drawerItems = [
|
||||
DrawerItem(
|
||||
title: 'Balance'.tr,
|
||||
icon: Icons.account_balance_wallet,
|
||||
color: Colors.green,
|
||||
onTap: () => Get.to(() => WalletCaptainRefactored())),
|
||||
DrawerItem(
|
||||
title: 'Profile'.tr,
|
||||
icon: Icons.person,
|
||||
color: Colors.blue,
|
||||
onTap: () => Get.to(() => ProfileCaptain())),
|
||||
DrawerItem(
|
||||
title: 'History of Trip'.tr,
|
||||
icon: Icons.history,
|
||||
color: Colors.orange,
|
||||
onTap: () => Get.to(() => const HistoryCaptain())),
|
||||
DrawerItem(
|
||||
title: 'Available for rides'.tr,
|
||||
icon: Icons.drive_eta,
|
||||
color: Colors.teal,
|
||||
onTap: () => Get.to(() => const AvailableRidesPage())),
|
||||
DrawerItem(
|
||||
title: 'Notifications'.tr,
|
||||
icon: Icons.notifications,
|
||||
color: Colors.purple,
|
||||
onTap: () => Get.to(() => const NotificationCaptain())),
|
||||
DrawerItem(
|
||||
title: 'Helping Center'.tr,
|
||||
icon: Icons.help_center,
|
||||
color: Colors.cyan,
|
||||
onTap: () => Get.to(() => HelpCaptain())),
|
||||
DrawerItem(
|
||||
title: 'Share App'.tr,
|
||||
icon: Icons.share,
|
||||
color: Colors.indigo,
|
||||
onTap: () => Get.to(() => InviteScreen())),
|
||||
// DrawerItem(
|
||||
// title: 'Maintenance Center'.tr,
|
||||
// icon: Icons.build,
|
||||
// color: Colors.brown,
|
||||
// onTap: () => Get.to(() => MaintainCenterPage())),
|
||||
// DrawerItem(
|
||||
// title: 'Health Insurance'.tr,
|
||||
// icon: Icons.favorite,
|
||||
// color: Colors.pink,
|
||||
// onTap: () => Get.to(() => AssuranceHealthPage())),
|
||||
DrawerItem(
|
||||
title: 'Contact Us'.tr,
|
||||
icon: Icons.email,
|
||||
color: Colors.blueGrey,
|
||||
onTap: () => Get.to(() => ContactUsPage())),
|
||||
DrawerItem(
|
||||
title: 'Videos Tutorials'.tr,
|
||||
icon: Icons.video_library,
|
||||
color: Colors.redAccent,
|
||||
onTap: () => Get.to(() => VideoListPage())),
|
||||
DrawerItem(
|
||||
title: 'Rate Our App'.tr,
|
||||
icon: Icons.star,
|
||||
color: Colors.amber,
|
||||
onTap: () => Get.to(() => RatingScreen())),
|
||||
DrawerItem(
|
||||
title: 'Is device compatible'.tr,
|
||||
icon: Icons.memory,
|
||||
color: Colors.greenAccent,
|
||||
onTap: () => Get.to(() => DeviceCompatibilityPage())),
|
||||
DrawerItem(
|
||||
title: 'Privacy Policy'.tr,
|
||||
icon: Icons.memory,
|
||||
color: Colors.greenAccent,
|
||||
onTap: () =>
|
||||
launchUrl(Uri.parse('${AppLink.server}/privacy_policy.php'))),
|
||||
DrawerItem(
|
||||
title: 'Settings'.tr,
|
||||
icon: Icons.settings,
|
||||
color: Colors.grey.shade600,
|
||||
onTap: () => Get.to(() => const SettingsCaptain())),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
child: Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Column(
|
||||
children: [
|
||||
// --- الجزء العلوي من القائمة (بيانات المستخدم) ---
|
||||
UserHeader(), // استخدمنا الويدجت المحسنة بالأسفل
|
||||
|
||||
// --- قائمة العناصر المتحركة ---
|
||||
Expanded(
|
||||
child: AnimationLimiter(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
itemCount: drawerItems.length + 1, // +1 لزر تسجيل الخروج
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
// --- زر تسجيل الخروج في النهاية ---
|
||||
if (index == drawerItems.length) {
|
||||
return AnimationConfiguration.staggeredList(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 375),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 50.0,
|
||||
child: FadeInAnimation(
|
||||
child: _DrawerItemTile(
|
||||
item: DrawerItem(
|
||||
title: 'Sign Out'.tr,
|
||||
icon: Icons.logout,
|
||||
color: Colors.red,
|
||||
onTap: () =>
|
||||
Get.to(() => const LogoutCaptain())),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- بقية العناصر ---
|
||||
final item = drawerItems[index];
|
||||
return AnimationConfiguration.staggeredList(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 375),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 50.0,
|
||||
child: FadeInAnimation(
|
||||
child: _DrawerItemTile(item: item),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- ويدجت خاصة بكل عنصر في القائمة ---
|
||||
class _DrawerItemTile extends StatelessWidget {
|
||||
final DrawerItem item;
|
||||
const _DrawerItemTile({required this.item});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: item.color.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(item.icon, color: item.color, size: 24),
|
||||
),
|
||||
title: Text(
|
||||
item.title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back(); // لإغلاق القائمة عند الضغط
|
||||
Future.delayed(const Duration(milliseconds: 250), () {
|
||||
item.onTap(); // الانتقال للصفحة بعد تأخير بسيط لإظهار الأنيميشن
|
||||
});
|
||||
},
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
splashColor: item.color.withOpacity(0.2),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- ويدجت محسنة للجزء العلوي من القائمة ---
|
||||
// ... (الاستيرادات السابقة تبقى كما هي)
|
||||
|
||||
// --- تم تعديل UserHeader لإضافة التحقق من الصورة ---
|
||||
class UserHeader extends StatelessWidget {
|
||||
UserHeader({super.key});
|
||||
final ImageController imageController = Get.find<ImageController>();
|
||||
final HomeCaptainController homeCaptainController =
|
||||
Get.find<HomeCaptainController>();
|
||||
|
||||
// دالة لإظهار التنبيه
|
||||
void _showUploadPhotoDialog(
|
||||
BuildContext context, ImageController controller) {
|
||||
// نستخدم addPostFrameCallback لضمان عدم ظهور الخطأ أثناء بناء الواجهة
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// نتأكد ألا يكون هناك dialog مفتوح بالفعل لتجنب التكرار
|
||||
if (Get.isDialogOpen == true) return;
|
||||
|
||||
Get.defaultDialog(
|
||||
title: "Profile Photo Required".tr,
|
||||
titleStyle:
|
||||
const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
|
||||
middleText:
|
||||
"Please upload a clear photo of your face to be identified by passengers."
|
||||
.tr,
|
||||
barrierDismissible: false, // منع الإغلاق بالضغط خارج النافذة
|
||||
radius: 15,
|
||||
contentPadding: const EdgeInsets.all(20),
|
||||
confirm: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Get.back(); // إغلاق النافذة الحالية
|
||||
// فتح الكاميرا فوراً
|
||||
controller.choosImagePicture(
|
||||
AppLink.uploadImagePortrate, 'portrait');
|
||||
},
|
||||
icon: const Icon(Icons.camera_alt, color: Colors.white),
|
||||
label: Text("Take Photo Now".tr,
|
||||
style: const TextStyle(color: Colors.white)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor
|
||||
.primaryColor, // تأكد من وجود هذا اللون أو استبدله بـ Colors.blue
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
),
|
||||
),
|
||||
cancel: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text("Later".tr, style: const TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UserAccountsDrawerHeader(
|
||||
accountName: Text(
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
shadows: [Shadow(blurRadius: 2, color: Colors.black26)]),
|
||||
),
|
||||
accountEmail:
|
||||
box.read(BoxName.emailDriver).toString().contains('intaleqapp')
|
||||
? Text('Your email not updated yet'.tr)
|
||||
: Text(box.read(BoxName.emailDriver)),
|
||||
currentAccountPicture: GetBuilder<ImageController>(
|
||||
builder: (controller) => Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 2),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 5)
|
||||
],
|
||||
),
|
||||
child: controller.isloading
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: CircleAvatar(
|
||||
// محاولة تحميل الصورة
|
||||
backgroundImage: NetworkImage(
|
||||
'${AppLink.server}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg'),
|
||||
|
||||
// [تعديل هام]: في حال فشل تحميل الصورة (غير موجودة)
|
||||
onBackgroundImageError: (exception, stackTrace) {
|
||||
// طباعة الخطأ في الكونسول للتوضيح
|
||||
debugPrint(
|
||||
"Profile image not found or error loading: $exception");
|
||||
// استدعاء نافذة التنبيه
|
||||
_showUploadPhotoDialog(context, controller);
|
||||
},
|
||||
|
||||
// أيقونة بديلة تظهر في الخلفية إذا لم تكن الصورة موجودة
|
||||
backgroundColor: Colors.grey.shade300,
|
||||
child: const Icon(Icons.person,
|
||||
size: 40, color: Colors.white),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: -5,
|
||||
right: -5,
|
||||
child: InkWell(
|
||||
onTap: () => controller.choosImagePicture(
|
||||
AppLink.uploadImagePortrate, 'portrait'),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(Icons.camera_alt,
|
||||
color: Theme.of(context).primaryColor, size: 18),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
otherAccountsPictures: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
homeCaptainController.rating.toString(),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
const Icon(Icons.star, color: Colors.amber, size: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Theme.of(context).primaryColor, Colors.blue.shade700],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
// import 'dart:async';
|
||||
// import 'package:SEFER/constant/box_name.dart';
|
||||
// import 'package:SEFER/main.dart';
|
||||
// import 'package:SEFER/views/widgets/my_scafold.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
// import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
||||
|
||||
// import '../../../../constant/api_key.dart';
|
||||
// import '../../../../controller/functions/crud.dart';
|
||||
|
||||
// String appId = AK.agoraAppId;
|
||||
|
||||
// class DriverCallPage extends StatefulWidget {
|
||||
// const DriverCallPage({super.key});
|
||||
|
||||
// @override
|
||||
// State<DriverCallPage> createState() => _DriverCallPageState();
|
||||
// }
|
||||
|
||||
// class _DriverCallPageState extends State<DriverCallPage> {
|
||||
// String channelName = '';
|
||||
// String token = '';
|
||||
// // "00612994c6e707543e68d5638894d04f989IAAlydoFEC3ZeZkeUwl0dSswZTX8n+xyZR8PBWdwXFV6t6MLiA8AAAAAEACCHD/gn3TUZQEAAQAAAAAA";
|
||||
|
||||
// // int uid = int.parse(box.read(BoxName.phoneDriver)); // uid of the local user
|
||||
// int uid = 0;
|
||||
// int? _remoteUid; // uid of the remote user
|
||||
// bool _isJoined = false; // Indicates if the local user has joined the channel
|
||||
// late RtcEngine agoraEngine; // Agora engine instance
|
||||
|
||||
// final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
|
||||
// GlobalKey<ScaffoldMessengerState>(); // Global key to access the scaffold
|
||||
|
||||
// showMessage(String message) {
|
||||
// scaffoldMessengerKey.currentState?.showSnackBar(SnackBar(
|
||||
// content: Text(message),
|
||||
// ));
|
||||
// }
|
||||
|
||||
// initAgora() async {
|
||||
// await fetchToken();
|
||||
// await setupVoiceSDKEngine();
|
||||
// }
|
||||
|
||||
// fetchToken() async {
|
||||
// var res = await CRUD()
|
||||
// .getAgoraToken(channelName: channelName, uid: uid.toString());
|
||||
// setState(() {
|
||||
// token = res;
|
||||
// });
|
||||
// }
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _remoteUid = box.read(BoxName.phone) != null
|
||||
// ? int.parse(box.read(BoxName.phone))
|
||||
// : int.parse(box.read(BoxName.phoneDriver));
|
||||
// uid = box.read(BoxName.phoneDriver) != null
|
||||
// ? int.parse(box.read(BoxName.phoneDriver))
|
||||
// : int.parse(box.read(BoxName.phone));
|
||||
// // Set up an instance of Agora engine
|
||||
// initAgora();
|
||||
// }
|
||||
|
||||
// Future<void> setupVoiceSDKEngine() async {
|
||||
// // retrieve or request microphone permission
|
||||
// await [Permission.microphone].request();
|
||||
|
||||
// //create an instance of the Agora engine
|
||||
// agoraEngine = createAgoraRtcEngine();
|
||||
// await agoraEngine.initialize(RtcEngineContext(appId: AK.agoraAppId));
|
||||
// // Register the event handler
|
||||
// agoraEngine.registerEventHandler(
|
||||
// RtcEngineEventHandler(
|
||||
// onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
|
||||
// showMessage(
|
||||
// "Local user uid:${connection.localUid} joined the channel");
|
||||
// setState(() {
|
||||
// _isJoined = true;
|
||||
// });
|
||||
// },
|
||||
// onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
|
||||
// showMessage("Remote user uid:$remoteUid joined the channel");
|
||||
// setState(() {
|
||||
// _remoteUid = remoteUid;
|
||||
// });
|
||||
// },
|
||||
// onUserOffline: (RtcConnection connection, int remoteUid,
|
||||
// UserOfflineReasonType reason) {
|
||||
// showMessage("Remote user uid:$remoteUid left the channel");
|
||||
// setState(() {
|
||||
// _remoteUid = null;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void join() async {
|
||||
// // Set channel options including the client role and channel profile
|
||||
// ChannelMediaOptions options = const ChannelMediaOptions(
|
||||
// clientRoleType: ClientRoleType.clientRoleBroadcaster,
|
||||
// channelProfile: ChannelProfileType.channelProfileCommunication,
|
||||
// );
|
||||
|
||||
// await agoraEngine.joinChannel(
|
||||
// token: token,
|
||||
// channelId: channelName,
|
||||
// options: options,
|
||||
// uid: uid,
|
||||
// );
|
||||
// }
|
||||
// //https://console.agora.io/invite?sign=5e9e22d06f22caeeada9954c9e908572%253A5ba8aed978a35eab5a5113742502ded2a41478b2a81cb19c71a30776e125b58a
|
||||
|
||||
// void leave() {
|
||||
// setState(() {
|
||||
// _isJoined = false;
|
||||
// _remoteUid = null;
|
||||
// });
|
||||
// agoraEngine.leaveChannel();
|
||||
// }
|
||||
|
||||
// // Clean up the resources when you leave
|
||||
// @override
|
||||
// void dispose() async {
|
||||
// await agoraEngine.leaveChannel();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
// // Build UI
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MaterialApp(
|
||||
// scaffoldMessengerKey: scaffoldMessengerKey,
|
||||
// home: MyScafolld(
|
||||
// // appBar: AppBar(
|
||||
// // title: const Text('Get started with Voice Calling'),
|
||||
// // ),
|
||||
// title: 'Voice Calling'.tr,
|
||||
// isleading: true,
|
||||
// body: [
|
||||
// ListView(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
// children: [
|
||||
// // Status text
|
||||
// Container(height: 40, child: Center(child: _status())),
|
||||
// // Button Row
|
||||
// Row(
|
||||
// children: <Widget>[
|
||||
// Expanded(
|
||||
// child: ElevatedButton(
|
||||
// child: const Text("Join"),
|
||||
// onPressed: () => {join()},
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 10),
|
||||
// Expanded(
|
||||
// child: ElevatedButton(
|
||||
// child: const Text("Leave"),
|
||||
// onPressed: () => {leave()},
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ]),
|
||||
// );
|
||||
// }
|
||||
|
||||
// Widget _status() {
|
||||
// String statusText;
|
||||
|
||||
// if (!_isJoined)
|
||||
// statusText = 'Join a channel';
|
||||
// else if (_remoteUid == null)
|
||||
// statusText = 'Waiting for a remote user to join...';
|
||||
// else
|
||||
// statusText = 'Connected to remote user, uid:$_remoteUid';
|
||||
|
||||
// return Text(
|
||||
// statusText,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
@@ -1,333 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/help/help_controller.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/home_captain/help_details_replay_page.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
|
||||
class HelpCaptain extends StatelessWidget {
|
||||
HelpCaptain({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(HelpController());
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(
|
||||
'Helping Page'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
leading: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Icon(CupertinoIcons.back),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
20.0), // Increased padding around the content
|
||||
child: ListView(
|
||||
// crossAxisAlignment:
|
||||
// CrossAxisAlignment.stretch, // Stretch children to full width
|
||||
children: [
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.all(18.0), // Slightly increased padding
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? CupertinoColors.systemGrey6
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
borderRadius:
|
||||
BorderRadius.circular(15.0), // More rounded corners
|
||||
),
|
||||
child: Text(
|
||||
'If you need any help or have questions, this is the right place to do that. You are welcome!'
|
||||
.tr,
|
||||
style:
|
||||
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 16, // Slightly larger font size
|
||||
color: CupertinoColors.label.resolveFrom(
|
||||
context), // Ensure text color adapts to theme
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
CupertinoFormSection.insetGrouped(
|
||||
// Using CupertinoFormSection for better styling
|
||||
header: Text('Submit Your Question'.tr),
|
||||
margin: EdgeInsets.zero,
|
||||
children: [
|
||||
GetBuilder<HelpController>(
|
||||
builder: (helpController) => Form(
|
||||
key: helpController.formKey,
|
||||
child: CupertinoTextFormFieldRow(
|
||||
controller: helpController.helpQuestionController,
|
||||
placeholder: 'Enter your Question here'.tr,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter your question'.tr;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
prefix: const Icon(CupertinoIcons
|
||||
.question_circle), // Added a prefix icon
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
child: GetBuilder<HelpController>(
|
||||
builder: (helpController) => helpController.isLoading
|
||||
? const CupertinoActivityIndicator()
|
||||
: CupertinoButton.filled(
|
||||
onPressed: () {
|
||||
if (helpController.formKey.currentState!
|
||||
.validate()) {
|
||||
helpController.addHelpQuestion();
|
||||
helpController.helpQuestionController
|
||||
.clear(); // Clear the text field
|
||||
}
|
||||
},
|
||||
child: Text('Submit Question'.tr),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Your Questions'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.navTitleTextStyle
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: GetBuilder<HelpController>(
|
||||
builder: (helpController) =>
|
||||
CupertinoListSection.insetGrouped(
|
||||
margin: EdgeInsets.zero,
|
||||
children: helpController.helpQuestionDate['message'] != null
|
||||
? List.generate(
|
||||
helpController.helpQuestionDate['message'].length,
|
||||
(index) {
|
||||
var list = helpController
|
||||
.helpQuestionDate['message'][index];
|
||||
return CupertinoListTile(
|
||||
title: Text(
|
||||
EncryptionHelper.instance
|
||||
.decryptData(list['helpQuestion']),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
list['datecreated'],
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.tabLabelTextStyle,
|
||||
),
|
||||
const Icon(CupertinoIcons.chevron_forward),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
helpController.getIndex(
|
||||
int.parse(EncryptionHelper.instance
|
||||
.decryptData(list['id'])),
|
||||
EncryptionHelper.instance
|
||||
.decryptData(list['helpQuestion']));
|
||||
helpController
|
||||
.getHelpRepley(list['id'].toString());
|
||||
Get.to(() => const HelpDetailsReplayPage());
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: [
|
||||
Center(
|
||||
child: Text('No questions asked yet.'.tr),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// class HelpCaptain extends StatelessWidget {
|
||||
// HelpCaptain({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// Get.put(HelpController());
|
||||
// return CupertinoPageScaffold(
|
||||
// navigationBar: CupertinoNavigationBar(
|
||||
// middle: Text('Helping Page'.tr),
|
||||
// leading: CupertinoButton(
|
||||
// padding: EdgeInsets.zero,
|
||||
// child: Icon(CupertinoIcons.back),
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// ),
|
||||
// ),
|
||||
// child: SafeArea(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey2,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(
|
||||
// 'If you need any help or have questions, this is the right place to do that. You are welcome!'
|
||||
// .tr,
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Card(
|
||||
// elevation: 3,
|
||||
// color: CupertinoColors.systemGrey6,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: GetBuilder<HelpController>(
|
||||
// builder: (helpController) => Form(
|
||||
// key: helpController.formKey,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// CupertinoTextField(
|
||||
// controller: helpController.helpQuestionController,
|
||||
// placeholder: 'Enter your Question here'.tr,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey,
|
||||
// ),
|
||||
// ),
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// clearButtonMode: OverlayVisibilityMode.editing,
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// helpController.isLoading
|
||||
// ? const CupertinoActivityIndicator()
|
||||
// : CupertinoButton.filled(
|
||||
// onPressed: () {
|
||||
// if (helpController.formKey.currentState!
|
||||
// .validate()) {
|
||||
// helpController.addHelpQuestion();
|
||||
|
||||
// // Clear the feedback form
|
||||
// helpController.formKey.currentState!
|
||||
// .reset();
|
||||
// }
|
||||
// },
|
||||
// child: Text('Submit Question'.tr),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// Expanded(
|
||||
// child: GetBuilder<HelpController>(
|
||||
// builder: (helpController) => Padding(
|
||||
// padding: const EdgeInsets.all(10),
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.systemGrey2,
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
// child: ListView.builder(
|
||||
// itemCount: helpController.helpQuestionDate['message'] !=
|
||||
// null
|
||||
// ? helpController.helpQuestionDate['message'].length
|
||||
// : 0,
|
||||
// itemBuilder: (BuildContext context, int index) {
|
||||
// var list =
|
||||
// helpController.helpQuestionDate['message'][index];
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.all(3),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// vertical: 12, horizontal: 16),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: CupertinoColors.activeGreen,
|
||||
// width: 2,
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// helpController.getIndex(
|
||||
// list['id'], list['helpQuestion']);
|
||||
// helpController
|
||||
// .getHelpRepley(list['id'].toString());
|
||||
// Get.to(() => const HelpDetailsReplayPage());
|
||||
// },
|
||||
// child: Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// list['helpQuestion'],
|
||||
// style: CupertinoTheme.of(context)
|
||||
// .textTheme
|
||||
// .textStyle,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// Text(
|
||||
// list['datecreated'],
|
||||
// style: CupertinoTheme.of(context)
|
||||
// .textTheme
|
||||
// .tabLabelTextStyle,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
@@ -1,85 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../../controller/functions/encrypt_decrypt.dart';
|
||||
import '../../../../controller/home/captin/help/help_controller.dart';
|
||||
import '../../../widgets/my_scafold.dart';
|
||||
|
||||
class HelpDetailsReplayPage extends StatelessWidget {
|
||||
const HelpDetailsReplayPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.find<HelpController>();
|
||||
return GetBuilder<HelpController>(
|
||||
builder: (helpController) => MyScafolld(
|
||||
title: 'Help Details'.tr,
|
||||
body: [
|
||||
helpController.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
child: Container(
|
||||
width: Get.width * .66,
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
helpController.qustion,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 3,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
width: Get.width * .66,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: helpController.status ==
|
||||
'not yet' ||
|
||||
EncryptionHelper.instance
|
||||
.decryptData(helpController
|
||||
.helpQuestionRepleyDate[
|
||||
'message']['replay'])
|
||||
.toString() ==
|
||||
'not yet'
|
||||
? Text(
|
||||
'No Response yet.'.tr,
|
||||
style: AppStyle.title,
|
||||
)
|
||||
: Text(
|
||||
EncryptionHelper.instance
|
||||
.decryptData(helpController
|
||||
.helpQuestionRepleyDate[
|
||||
'message']['replay'])
|
||||
.toString(),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
],
|
||||
)
|
||||
],
|
||||
isleading: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,763 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_map/flutter_map.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
// import 'package:latlong2/latlong.dart';
|
||||
|
||||
// import '../../../../controller/functions/location_controller.dart';
|
||||
// import '../../../../controller/home/captin/home_captain_controller.dart';
|
||||
// // هذه ويدجت بديلة للـ _MapView في الكود الخاص بك
|
||||
// // V3 - MapView Replacement using flutter_map
|
||||
|
||||
// class OsmMapView extends StatelessWidget {
|
||||
// const OsmMapView({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// // افترض أنك تحصل على الموقع والاتجاه بنفس الطريقة من الكونترولر
|
||||
// final LocationController locationController =
|
||||
// Get.find<LocationController>();
|
||||
// final HomeCaptainController homeCaptainController =
|
||||
// Get.find<HomeCaptainController>();
|
||||
|
||||
// // يمكنك استخدام GetBuilder لمراقبة التغييرات في الموقع
|
||||
// return Obx(() {
|
||||
// final LatLng currentLocation = LatLng(
|
||||
// locationController.myLocation.latitude,
|
||||
// locationController.myLocation.longitude);
|
||||
// final double currentHeading = locationController.heading;
|
||||
|
||||
// return FlutterMap(
|
||||
// // يمكنك ربط هذا بـ MapController الخاص بـ flutter_map
|
||||
// // mapController: homeCaptainController.flutterMapController,
|
||||
// options: MapOptions(
|
||||
// initialCenter: currentLocation,
|
||||
// initialZoom: 15,
|
||||
// maxZoom: 18,
|
||||
// minZoom: 6,
|
||||
// // تدوير الخريطة (اختياري)
|
||||
// initialRotation: currentHeading,
|
||||
// ),
|
||||
// children: [
|
||||
// // 1. طبقة الخريطة الأساسية (Tiles)
|
||||
// // هذا هو الرابط لخرائط OSM الأساسية
|
||||
// TileLayer(
|
||||
// urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
// subdomains: ['a', 'b', 'c'],
|
||||
// userAgentPackageName:
|
||||
// 'com.example.app', // استبدل باسم الباكج الخاص بك
|
||||
|
||||
// // لاستخدام الخرائط الأوفلاين (بعد إعداد flutter_map_tile_caching)
|
||||
// // tileProvider: CachedTileProvider(),
|
||||
// ),
|
||||
|
||||
// // 2. طبقة العلامات (Markers)
|
||||
// MarkerLayer(
|
||||
// markers: [
|
||||
// Marker(
|
||||
// width: 80.0,
|
||||
// height: 80.0,
|
||||
// point: currentLocation,
|
||||
// child: Transform.rotate(
|
||||
// angle: currentHeading *
|
||||
// (3.1415926535 / 180), // تحويل من درجات إلى راديان
|
||||
// child: Image.asset(
|
||||
// 'assets/images/car_icon.png', // تأكد أن لديك أيقونة السيارة
|
||||
// // يمكنك استخدام نفس الـ carIcon من الكونترولر
|
||||
// // icon: homeCaptainController.carIcon, (ملاحظة: flutter_map تستخدم ويدجت)
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
|
||||
// // يمكنك إضافة طبقات أخرى هنا (مثل الخطوط Polylines أو المضلعات Polygons)
|
||||
// ],
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// // ملاحظة: ستحتاج إلى تعديل بسيط في HomeCaptainController
|
||||
// // لإنشاء MapController الخاص بـ flutter_map بدلاً من GoogleMapController
|
||||
// // import 'package:flutter_map/flutter_map.dart';
|
||||
// // MapController flutterMapController = MapController();
|
||||
@@ -1,84 +0,0 @@
|
||||
// import 'package:SEFER/constant/colors.dart';
|
||||
// import 'package:SEFER/constant/style.dart';
|
||||
// import 'package:SEFER/controller/firebase/firbase_messge.dart';
|
||||
// import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
||||
// import 'package:SEFER/views/widgets/my_scafold.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||
|
||||
// import '../../../../../controller/functions/call_controller.dart';
|
||||
|
||||
// class CallPage extends StatelessWidget {
|
||||
// const CallPage({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return MyScafolld(
|
||||
// title: 'Call Page'.tr, isleading: true, body: [callPage()]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// GetBuilder<HomeCaptainController> callPage() {
|
||||
// CallController callController = Get.put(CallController());
|
||||
// Get.put(MapDriverController());
|
||||
// // callController.join();
|
||||
// return GetBuilder<HomeCaptainController>(
|
||||
// builder: (controller) => Positioned(
|
||||
// top: Get.height * .2,
|
||||
// child: Container(
|
||||
// height: 100, width: Get.width,
|
||||
// decoration: AppStyle.boxDecoration,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () async {
|
||||
// callController.join();
|
||||
// },
|
||||
// child: Container(
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
// decoration: const BoxDecoration(
|
||||
// shape: BoxShape.circle, color: AppColor.greenColor),
|
||||
// child: const Icon(
|
||||
// Icons.phone,
|
||||
// size: 35,
|
||||
// color: AppColor.secondaryColor,
|
||||
// )),
|
||||
// ),
|
||||
// Column(
|
||||
// children: [
|
||||
// Text(callController.status),
|
||||
// Text(Get.find<MapDriverController>().passengerName.toString()),
|
||||
// ],
|
||||
// ),
|
||||
// GestureDetector(
|
||||
// onTap: () async {
|
||||
// FirebaseMessagesController().sendNotificationToPassengerToken(
|
||||
// 'Call End'.tr,
|
||||
// 'Call End',
|
||||
// Get.find<MapDriverController>().tokenPassenger,
|
||||
// [],
|
||||
// 'iphone_ringtone.wav');
|
||||
// callController.leave();
|
||||
// Get.back();
|
||||
// },
|
||||
// child: Container(
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
// decoration: const BoxDecoration(
|
||||
// shape: BoxShape.circle, color: AppColor.redColor),
|
||||
// child: const Icon(
|
||||
// Icons.phone_disabled_sharp,
|
||||
// size: 35,
|
||||
// color: AppColor.secondaryColor,
|
||||
// )),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// // ignore: prefer_const_constructors
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
@@ -1,141 +0,0 @@
|
||||
import 'package:sefer_driver/controller/functions/tts.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
||||
|
||||
import '../../../../../constant/style.dart';
|
||||
import '../../../../../controller/functions/encrypt_decrypt.dart';
|
||||
import '../../../../widgets/elevated_btn.dart';
|
||||
import '../../../../../controller/home/captin/home_captain_controller.dart';
|
||||
|
||||
class ConnectWidget extends StatelessWidget {
|
||||
const ConnectWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// final OrderRequestController orderRequestController =
|
||||
// Get.put(OrderRequestController());
|
||||
CaptainWalletController captainWalletController =
|
||||
Get.put(CaptainWalletController());
|
||||
int refusedRidesToday = 0;
|
||||
// captainWalletController.getCaptainWalletFromBuyPoints();
|
||||
return Center(
|
||||
child: GetBuilder<HomeCaptainController>(
|
||||
builder: (homeCaptainController) => double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-200
|
||||
? CupertinoButton(
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
// backgroundColor: CupertinoColors.destructiveRed,
|
||||
barrierDismissible: false,
|
||||
title: double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-200
|
||||
? 'You dont have Points'.tr
|
||||
: 'You Are Stopped For this Day !'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
double.parse((captainWalletController
|
||||
.totalPoints)) <
|
||||
-200
|
||||
? await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'You must be recharge your Account'
|
||||
.tr)
|
||||
: await Get.find<TextToSpeechController>()
|
||||
.speakText(
|
||||
'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
||||
.tr);
|
||||
},
|
||||
icon: const Icon(Icons.headphones),
|
||||
),
|
||||
Text(
|
||||
double.parse((captainWalletController
|
||||
.totalPoints)) <
|
||||
-200
|
||||
? 'You must be recharge your Account'.tr
|
||||
: 'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
||||
.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
confirm: double.parse(
|
||||
(captainWalletController.totalPoints)) <
|
||||
-200
|
||||
? MyElevatedButton(
|
||||
title: 'Recharge my Account'.tr,
|
||||
onPressed: () {
|
||||
homeCaptainController.goToWalletFromConnect();
|
||||
})
|
||||
: MyElevatedButton(
|
||||
title: 'Ok , See you Tomorrow'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
Get.back();
|
||||
}));
|
||||
},
|
||||
color: CupertinoColors.destructiveRed,
|
||||
child: Text(
|
||||
'You are Stopped'.tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: homeCaptainController.isActive
|
||||
? [Colors.green.shade400, Colors.green.shade700]
|
||||
: [Colors.grey.shade400, Colors.grey.shade700],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: homeCaptainController.isActive
|
||||
? Colors.green.withOpacity(0.3)
|
||||
: Colors.grey.withOpacity(0.3),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CupertinoButton(
|
||||
onPressed: homeCaptainController.onButtonSelected,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
homeCaptainController.isActive
|
||||
? CupertinoIcons.check_mark_circled_solid
|
||||
: CupertinoIcons.circle,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
homeCaptainController.isActive
|
||||
? 'Connected'.tr
|
||||
: 'Not Connected'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/firebase/local_notification.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/auth/captin/otp_page.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
||||
import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
|
||||
import '../../../../../constant/colors.dart';
|
||||
import '../../../../../constant/links.dart';
|
||||
import '../../../../../controller/firebase/firbase_messge.dart';
|
||||
import '../../../../../controller/firebase/notification_service.dart';
|
||||
import '../../../../../controller/functions/crud.dart';
|
||||
import '../../../../../controller/home/captin/order_request_controller.dart';
|
||||
import '../../../../../controller/home/navigation/navigation_view.dart';
|
||||
import '../../../../../print.dart';
|
||||
import '../../../../Rate/ride_calculate_driver.dart';
|
||||
|
||||
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||
final firebaseMessagesController =
|
||||
Get.isRegistered<FirebaseMessagesController>()
|
||||
? Get.find<FirebaseMessagesController>()
|
||||
: Get.put(FirebaseMessagesController());
|
||||
return GetBuilder<HomeCaptainController>(
|
||||
builder: (controller) => Positioned(
|
||||
bottom: Get.height * .2,
|
||||
left: 6,
|
||||
child: Column(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: Builder(builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
await checkForPendingOrderFromServer();
|
||||
Log.print(
|
||||
'box.read(BoxName.rideArgumentsFromBackground): ${box.read(BoxName.rideArgumentsFromBackground)}');
|
||||
|
||||
box.read(BoxName.rideArgumentsFromBackground).toString() !=
|
||||
'failure'
|
||||
? Get.to(() => PassengerLocationMapPage(),
|
||||
arguments:
|
||||
box.read(BoxName.rideArgumentsFromBackground))
|
||||
: MyDialog().getDialog(
|
||||
'Ride info'.tr,
|
||||
'you dont have accepted ride'.tr,
|
||||
() {
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
// 'box.read(BoxName.rideArgumentsFromBackground): ${box.read(BoxName.rideArgumentsFromBackground)}');
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.directions_car_rounded,
|
||||
size: 29,
|
||||
color:
|
||||
box.read(BoxName.rideArgumentsFromBackground) == 'failure'
|
||||
? AppColor.redColor
|
||||
: AppColor.greenColor,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: IconButton(
|
||||
onLongPress: () {
|
||||
box.write(BoxName.statusDriverLocation, 'off');
|
||||
},
|
||||
onPressed: () {
|
||||
// NotificationController1()
|
||||
// .showNotification('Sefer Driver'.tr, ''.tr, '', '');
|
||||
final now = DateTime.now();
|
||||
DateTime? lastRequestTime =
|
||||
box.read(BoxName.lastTimeStaticThrottle);
|
||||
|
||||
if (lastRequestTime == null ||
|
||||
now.difference(lastRequestTime).inMinutes >= 2) {
|
||||
// Update the last request time to now
|
||||
lastRequestTime = now;
|
||||
box.write(BoxName.lastTimeStaticThrottle, lastRequestTime);
|
||||
// Navigate to the RideCalculateDriver page
|
||||
Get.to(() => RideCalculateDriver());
|
||||
} else {
|
||||
// Optionally show a message or handle the throttling case
|
||||
final minutesLeft =
|
||||
2 - now.difference(lastRequestTime).inMinutes;
|
||||
// Get.snackbar(
|
||||
// '${'Please wait'.tr} $minutesLeft ${"minutes before trying again.".tr}',
|
||||
// '');
|
||||
NotificationController().showNotification(
|
||||
'Intaleq Driver'.tr,
|
||||
'${'Please wait'.tr} $minutesLeft ${"minutes before trying again.".tr}',
|
||||
'ding',
|
||||
'');
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
FontAwesome5.chart_bar,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
// Platform.isAndroid
|
||||
// ?
|
||||
int.parse(box.read(BoxName.carYear).toString()) > 2023
|
||||
? AnimatedContainer(
|
||||
duration: const Duration(microseconds: 200),
|
||||
width: controller.widthMapTypeAndTraffic,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
border: Border.all(color: AppColor.blueColor),
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
child: Builder(builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
// mySnakeBarError('ad');
|
||||
Get.to(() => const VipOrderPage());
|
||||
},
|
||||
icon: const Icon(
|
||||
Octicons.watch,
|
||||
size: 29,
|
||||
color: AppColor.blueColor,
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
: const SizedBox(),
|
||||
// const SizedBox(
|
||||
// height: 5,
|
||||
// ),
|
||||
|
||||
// AnimatedContainer(
|
||||
// duration: const Duration(microseconds: 200),
|
||||
// width: controller.widthMapTypeAndTraffic,
|
||||
// decoration: BoxDecoration(
|
||||
// color: AppColor.secondaryColor,
|
||||
// border: Border.all(color: AppColor.blueColor),
|
||||
// borderRadius: BorderRadius.circular(15)),
|
||||
// child: Builder(builder: (context) {
|
||||
// return IconButton(
|
||||
// onPressed: () async {
|
||||
// box.remove(BoxName.agreeTerms);
|
||||
// Get.to(() => const NavigationView());
|
||||
|
||||
// // box.write(BoxName.statusDriverLocation, 'off');
|
||||
// },
|
||||
// icon: const Icon(
|
||||
// FontAwesome5.map,
|
||||
// size: 29,
|
||||
// color: AppColor.blueColor,
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// ),
|
||||
// AnimatedContainer(
|
||||
// duration: const Duration(microseconds: 200),
|
||||
// width: controller.widthMapTypeAndTraffic,
|
||||
// decoration: BoxDecoration(
|
||||
// color: AppColor.secondaryColor,
|
||||
// border: Border.all(color: AppColor.blueColor),
|
||||
// borderRadius: BorderRadius.circular(15)),
|
||||
// child: Builder(builder: (context) {
|
||||
// return IconButton(
|
||||
// onPressed: () async {
|
||||
// NotificationService.sendNotification(
|
||||
// target: 'service', // الإرسال لجميع المشتركين في "service"
|
||||
// title: 'طلب خدمة جديد',
|
||||
// body: 'تم استلام طلب خدمة جديد. الرجاء مراجعة التفاصيل.',
|
||||
// isTopic: true,
|
||||
// category: 'new_service_request', // فئة توضح نوع الإشعار
|
||||
// );
|
||||
// },
|
||||
// icon: const Icon(
|
||||
// FontAwesome5.grin_tears,
|
||||
// size: 29,
|
||||
// color: AppColor.blueColor,
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// ),
|
||||
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> checkForPendingOrderFromServer() async {
|
||||
bool _isProcessingOrder = false;
|
||||
if (_isProcessingOrder) return;
|
||||
|
||||
final driverId = box.read(BoxName.driverID)?.toString();
|
||||
if (driverId == null) return; // Can't check without a driver ID
|
||||
|
||||
_isProcessingOrder = true; // Lock
|
||||
|
||||
try {
|
||||
// You need to create this CRUD method
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.getArgumentAfterAppliedFromBackground,
|
||||
payload: {'driver_id': driverId},
|
||||
);
|
||||
Log.print('response getArgumentAfterAppliedFromBackground: ${response}');
|
||||
|
||||
// Assuming the server returns order data if found, or 'failure'/'none' if not
|
||||
if (response != 'failure') {
|
||||
final Map<String, dynamic> orderInfoFromServer = response['message'];
|
||||
final Map<String, dynamic> rideArguments =
|
||||
_transformServerDataToAppArguments(orderInfoFromServer);
|
||||
// 2. Build the new arguments map, matching your Flutter structure
|
||||
|
||||
final customerToken = (response)['message']['token_passenger'];
|
||||
final orderId = (response)['message']['ride_id'].toString();
|
||||
box.write(BoxName.rideArgumentsFromBackground, rideArguments);
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
box.write(BoxName.rideStatus, 'Apply');
|
||||
Get.put(OrderRequestController()).changeApplied();
|
||||
// MyDialog().getDialog(orderId.toString(), customerToken, () {});
|
||||
|
||||
// Now proceed with the UI flow
|
||||
// _sendAcceptanceNotification(customerToken, orderId.toString());
|
||||
// await _bringAppToForegroundAndNavigate(orderId);
|
||||
Get.to(() => PassengerLocationMapPage(),
|
||||
arguments: box.read(BoxName.rideArgumentsFromBackground));
|
||||
} else {
|
||||
box.write(BoxName.rideArgumentsFromBackground, 'failure');
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
_isProcessingOrder = false; // Release lock
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _transformServerDataToAppArguments(
|
||||
Map<String, dynamic> serverData) {
|
||||
// Helper function to safely get and convert values to String
|
||||
String _getString(String key, [String defaultValue = 'unknown']) {
|
||||
// serverData[key] might be an int, double, or string. .toString() handles all.
|
||||
// If it's null, use the default value.
|
||||
return serverData[key]?.toString() ?? defaultValue;
|
||||
}
|
||||
|
||||
return {
|
||||
'passengerLocation': _getString('passenger_location'),
|
||||
'passengerDestination': _getString('passenger_destination'),
|
||||
'Duration': _getString('duration'),
|
||||
'totalCost': _getString('total_cost'),
|
||||
'Distance': _getString('distance'),
|
||||
'name': _getString('name'),
|
||||
'phone': _getString('phone'),
|
||||
'email': _getString('email'),
|
||||
'tokenPassenger': _getString('token_passenger'),
|
||||
'direction': _getString('direction_url'),
|
||||
'DurationToPassenger': _getString('duration_to_passenger'),
|
||||
'rideId': _getString('ride_id'),
|
||||
'passengerId': _getString('passenger_id'),
|
||||
'driverId': _getString('driver_id'),
|
||||
'durationOfRideValue': _getString('duration_of_ride'),
|
||||
'paymentAmount': _getString('payment_amount'),
|
||||
'paymentMethod': _getString('payment_method'),
|
||||
'passengerWalletBurc': _getString('passenger_wallet_burc'),
|
||||
'timeOfOrder': _getString('time_of_order'),
|
||||
'totalPassenger': _getString('total_passenger'),
|
||||
'carType': _getString('car_type'),
|
||||
'kazan': _getString('kazan'),
|
||||
'startNameLocation': _getString('start_name_location'),
|
||||
'endNameLocation': _getString('end_name_location'),
|
||||
|
||||
// --- Special Handling ---
|
||||
|
||||
// Steps (handle null values by providing an empty string)
|
||||
'step0': _getString('step0'),
|
||||
'step1': _getString('step1'),
|
||||
'step2': _getString('step2'),
|
||||
'step3': _getString('step3'),
|
||||
'step4': _getString('step4'),
|
||||
|
||||
// Boolean conversion (1/0 from server to 'true'/'false' string for the app)
|
||||
'WalletChecked': (serverData['wallet_checked'] == 1).toString(),
|
||||
|
||||
// Logic-based conversion for isHaveSteps
|
||||
// Your app's `rideArguments` expects 'startEnd', so we provide that if has_steps is 1.
|
||||
// You might need to adjust this logic if 'haveSteps' is also a possibility.
|
||||
'isHaveSteps': (serverData['has_steps'] == 1)
|
||||
? 'startEnd'
|
||||
: 'noSteps', // Providing a default
|
||||
};
|
||||
}
|
||||
|
||||
void _sendAcceptanceNotification(String? customerToken, rideId) {
|
||||
try {
|
||||
if (customerToken == null) return;
|
||||
|
||||
List<String> bodyToPassenger = [
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
box.read(BoxName.tokenDriver).toString(),
|
||||
rideId.toString()
|
||||
];
|
||||
|
||||
// Safely check for customer token
|
||||
final String? token = customerToken;
|
||||
if (token != null && token.isNotEmpty) {
|
||||
NotificationService.sendNotification(
|
||||
target: token.toString(),
|
||||
title: 'Accepted Ride'.tr,
|
||||
body: 'your ride is Accepted'.tr,
|
||||
isTopic: false, // Important: this is a token
|
||||
tone: 'start',
|
||||
driverList: bodyToPassenger, category: 'Accepted Ride',
|
||||
);
|
||||
} else {}
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
class ZonesController extends GetxController {
|
||||
Map<String, List<LatLng>> generateZoneMap(
|
||||
LatLng southwest, LatLng southEast, LatLng northeast) {
|
||||
const double desiredZoneArea = 4; // in square kilometers
|
||||
|
||||
final double width = (southEast.longitude - southwest.longitude) * 100;
|
||||
final double height = (northeast.latitude - southEast.latitude) * 100;
|
||||
final double totalArea = width * height;
|
||||
|
||||
// final int numZones = (totalArea / desiredZoneArea).ceil();
|
||||
|
||||
final double zoneWidth = width / sqrt(desiredZoneArea);
|
||||
final double zoneHeight = height / sqrt(desiredZoneArea);
|
||||
final numRows =
|
||||
((northeast.latitude - southwest.latitude) / zoneHeight).ceil();
|
||||
final numCols =
|
||||
((southEast.longitude - southwest.longitude) / zoneWidth).ceil();
|
||||
List<String> zoneNames = [];
|
||||
List<LatLng> zoneCoordinates = [];
|
||||
|
||||
for (int row = 0; row < numRows; row++) {
|
||||
for (int col = 0; col < numCols; col++) {
|
||||
final double zoneSouthwestLat =
|
||||
southwest.latitude + (row * zoneHeight / 100);
|
||||
final double zoneSouthwestLng =
|
||||
southwest.longitude + (col * zoneWidth / 100);
|
||||
final double zoneNortheastLat = zoneSouthwestLat + zoneHeight / 100;
|
||||
final double zoneNortheastLng = zoneSouthwestLng + zoneWidth / 100;
|
||||
|
||||
LatLng zoneSouthwest = LatLng(zoneSouthwestLat, zoneSouthwestLng);
|
||||
LatLng zoneNortheast = LatLng(zoneNortheastLat, zoneNortheastLng);
|
||||
|
||||
String zoneName =
|
||||
'Zone${row + col}'; // Assign a unique name to each zone
|
||||
|
||||
zoneNames.add(zoneName);
|
||||
zoneCoordinates.add(zoneSouthwest);
|
||||
zoneCoordinates.add(zoneNortheast);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<LatLng>> zoneMap = {};
|
||||
for (int i = 0; i < zoneNames.length; i++) {
|
||||
zoneMap[zoneNames[i]] = [
|
||||
zoneCoordinates[i], // Southwest LatLng
|
||||
zoneCoordinates[i + 1], // Northeast LatLng
|
||||
];
|
||||
}
|
||||
|
||||
return zoneMap;
|
||||
}
|
||||
|
||||
void getJsonOfZones() {
|
||||
LatLng southwest = const LatLng(32.111107, 36.062222);
|
||||
LatLng southEast = const LatLng(32.108333, 36.101667);
|
||||
LatLng northeast = const LatLng(32.143889, 36.058889);
|
||||
Map<String, List<LatLng>> zoneMap =
|
||||
generateZoneMap(southwest, southEast, northeast);
|
||||
String jsonMap = json.encode(zoneMap);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user