first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,407 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/upload_image.dart';
import 'package:siro_driver/controller/home/captin/home_captain_controller.dart';
import 'package:siro_driver/device_compatibility_page.dart';
import 'package:siro_driver/main.dart';
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
import 'package:siro_driver/views/Rate/rate_app_page.dart';
import 'package:siro_driver/views/auth/captin/contact_us_page.dart';
import 'package:siro_driver/views/auth/captin/invite_driver_screen.dart';
import 'package:siro_driver/views/home/statistics/statistics_dashboard.dart';
import 'package:siro_driver/views/gamification/challenges_page.dart';
import 'package:siro_driver/views/gamification/leaderboard_page.dart';
import 'package:siro_driver/views/gamification/referral_center_page.dart';
import 'package:siro_driver/views/home/journal/schedule_page.dart';
import 'package:siro_driver/views/notification/available_rides_page.dart';
import 'package:siro_driver/views/auth/captin/logout_captain.dart';
import 'package:siro_driver/views/home/Captin/history/history_captain.dart';
import 'package:siro_driver/views/home/Captin/home_captain/help_captain.dart';
import 'package:siro_driver/views/home/Captin/About Us/settings_captain.dart';
import 'package:siro_driver/views/home/my_wallet/walet_captain.dart';
import 'package:siro_driver/views/home/profile/profile_captain.dart';
import 'package:siro_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: 'Statistics'.tr,
icon: Icons.bar_chart_rounded,
color: Colors.deepPurple,
onTap: () => Get.to(() => StatisticsDashboard())),
DrawerItem(
title: 'Challenges'.tr,
icon: Icons.bolt_rounded,
color: Colors.amber,
onTap: () => Get.to(() => ChallengesPage())),
DrawerItem(
title: 'My Schedule'.tr,
icon: Icons.calendar_today_rounded,
color: Colors.teal,
onTap: () => Get.to(() => SchedulePage())),
DrawerItem(
title: 'Leaderboard'.tr,
icon: Icons.leaderboard_rounded,
color: Colors.red,
onTap: () => Get.to(() => LeaderboardPage())),
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: 'Invite Driver'.tr,
icon: Icons.person_add_rounded,
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.withValues(alpha: 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: () {
Navigator.pop(context); // لإغلاق القائمة عند الضغط بشكل آمن
Future.delayed(const Duration(milliseconds: 250), () {
item.onTap(); // الانتقال للصفحة بعد تأخير بسيط لإظهار الأنيميشن
});
},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
splashColor: item.color.withValues(alpha: 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.withValues(alpha: 0.3), blurRadius: 5)
],
),
child: controller.isloading
? const CircularProgressIndicator(color: Colors.white)
: CircleAvatar(
backgroundImage: NetworkImage((box
.read(BoxName.driverPhotoUrl)
?.toString()
.isNotEmpty ==
true)
? box.read(BoxName.driverPhotoUrl)
: '${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: [
SizedBox(
width: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
homeCaptainController.rating.toString(),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14),
),
const SizedBox(width: 2),
const Icon(Icons.star, color: Colors.amber, size: 16),
],
),
),
],
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Theme.of(context).primaryColor, Colors.blue.shade700],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
);
}
}

View File

@@ -0,0 +1,189 @@
// 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,
// );
// }
// }

View File

@@ -0,0 +1,331 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/controller/home/captin/help/help_controller.dart';
import 'package:siro_driver/views/home/Captin/home_captain/help_details_replay_page.dart';
import 'package:flutter/cupertino.dart';
import '../../../../constant/colors.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
import '../../../widgets/my_scafold.dart';
class HelpCaptain extends StatelessWidget {
HelpCaptain({super.key});
@override
Widget build(BuildContext context) {
Get.put(HelpController());
final theme = Theme.of(context);
return MyScafolld(
title: 'Helping Page'.tr,
isleading: true,
body: [
SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(18.0),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(15.0),
border: Border.all(color: theme.dividerColor),
),
child: Text(
'If you need any help or have questions, this is the right place to do that. You are welcome!'
.tr,
style: theme.textTheme.bodyLarge,
textAlign: TextAlign.center,
),
),
const SizedBox(height: 24),
Text(
'Submit Your Question'.tr,
style: theme.textTheme.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
GetBuilder<HelpController>(
builder: (helpController) => Form(
key: helpController.formKey,
child: Column(
children: [
TextFormField(
controller: helpController.helpQuestionController,
decoration: InputDecoration(
hintText: 'Enter your Question here'.tr,
prefixIcon:
const Icon(Icons.question_answer_outlined),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12)),
),
maxLines: 3,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your question'.tr;
}
return null;
},
),
const SizedBox(height: 16),
helpController.isLoading
? const Center(child: CircularProgressIndicator())
: SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
if (helpController.formKey.currentState!
.validate()) {
helpController.addHelpQuestion();
helpController.helpQuestionController
.clear();
}
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
foregroundColor: Colors.white,
padding:
const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
child: Text('Submit Question'.tr),
),
),
],
),
),
),
const SizedBox(height: 32),
Text(
'Your Questions'.tr,
style: theme.textTheme.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
GetBuilder<HelpController>(
builder: (helpController) {
final questions = helpController.helpQuestionDate['message'];
if (questions == null || questions.isEmpty) {
return Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Text('No questions asked yet.'.tr),
),
);
}
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: questions.length,
separatorBuilder: (context, index) =>
const SizedBox(height: 12),
itemBuilder: (context, index) {
var list = questions[index];
return Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(color: theme.dividerColor),
),
child: ListTile(
title: Text(
EncryptionHelper.instance
.decryptData(list['helpQuestion']),
style: const TextStyle(fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
list['datecreated'],
style: theme.textTheme.bodySmall,
),
trailing:
const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () {
helpController.getIndex(
int.parse(EncryptionHelper.instance
.decryptData(list['id'])),
EncryptionHelper.instance
.decryptData(list['helpQuestion']));
helpController.getHelpRepley(list['id'].toString());
Get.to(() => const HelpDetailsReplayPage());
},
),
);
},
);
},
),
],
),
),
],
);
}
}
// 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,
// ),
// ],
// ),
// ),
// ),
// );
// },
// ),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// );
// }
// }

View File

@@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/style.dart';
import 'package:siro_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()
: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Question Bubble (Aligned to Start/End based on locale, usually start for sender)
Align(
alignment: AlignmentDirectional.centerStart,
child: Container(
constraints: BoxConstraints(maxWidth: Get.width * 0.75),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: const BorderRadius.only(
topRight: Radius.circular(16),
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Your Question'.tr,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
helpController.qustion,
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
),
const SizedBox(height: 24),
// Reply Bubble (Aligned to opposite side)
Align(
alignment: AlignmentDirectional.centerEnd,
child: Container(
constraints: BoxConstraints(maxWidth: Get.width * 0.75),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Support Reply'.tr,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Builder(builder: (context) {
final replayData = helpController.helpQuestionRepleyDate['message'];
final isNoReply = helpController.status == 'not yet' ||
replayData == null ||
EncryptionHelper.instance.decryptData(replayData['replay']).toString() == 'not yet';
return Text(
isNoReply
? 'No Response yet.'.tr
: EncryptionHelper.instance.decryptData(replayData['replay']).toString(),
style: Theme.of(context).textTheme.bodyLarge,
);
}),
],
),
),
),
],
),
)
],
isleading: true,
));
}
}

View File

@@ -0,0 +1,672 @@
import 'dart:io';
import 'package:bubble_head/bubble.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
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:siro_driver/views/home/Captin/home_captain/drawer_captain.dart';
import 'package:siro_driver/views/widgets/mycircular.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/info.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 '../../../../controller/home/navigation/navigation_view.dart';
import '../../../../controller/profile/setting_controller.dart';
import '../../../../env/env.dart';
import '../../../../main.dart';
import '../../../notification/available_rides_page.dart';
import '../driver_map_page.dart';
import 'widget/connect.dart';
import 'widget/left_menu_map_captain.dart';
// ─────────────────────────────────────────────
// Design Tokens (Responsive to Theme)
// ─────────────────────────────────────────────
class _Token {
static Color surface(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? const Color(0xFF1A1A2E)
: Colors.white;
static Color surfaceCard(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? const Color(0xFF16213E)
: const Color(0xFFF8F9FA);
static const Color accent = Color(0xFFF0A500);
static Color accentSoft(BuildContext context) =>
const Color(0xFFF0A500).withOpacity(0.12);
static const Color danger = Color(0xFFE53935);
static const Color success = Color(0xFF2ECC71);
static const Color info = Color(0xFF3498DB);
static Color border(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? const Color(0xFF2A2A4A)
: Colors.grey.withOpacity(0.2);
static Color text(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? Colors.white
: const Color(0xFF2D3436);
static Color textDim(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? Colors.white38
: Colors.black45;
static const double radius = 16.0;
static const double radiusSm = 10.0;
}
// ─────────────────────────────────────────────
// Root Widget
// ─────────────────────────────────────────────
class HomeCaptain extends StatelessWidget {
HomeCaptain({super.key});
final LocationController locationController =
Get.put(LocationController(), permanent: true);
final HomeCaptainController homeCaptainController =
Get.put(HomeCaptainController(), permanent: true);
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await closeOverlayIfFound();
if (!context.mounted) return;
await checkForUpdate(context);
if (!context.mounted) return;
await getPermissionOverlay();
if (!context.mounted) return;
await showDriverGiftClaim(context);
if (!context.mounted) return;
await checkForAppliedRide(context);
});
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: _Token.surface(context),
appBar: const _HomeAppBar(),
drawer: AppDrawer(),
body: SafeArea(
top: false,
child: Stack(
children: [
const _MapView(),
leftMainMenuCaptainIcons(),
const _BottomStatusBar(),
const _FloatingControls(),
],
),
),
);
}
}
// ─────────────────────────────────────────────
// AppBar — no blur, solid gradient
// ─────────────────────────────────────────────
class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
const _HomeAppBar();
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
final ctrl = Get.find<HomeCaptainController>();
return AppBar(
backgroundColor: _Token.surface(context),
elevation: 0,
systemOverlayStyle: SystemUiOverlayStyle.light,
titleSpacing: 0,
// ── Logo + App Name ──────────────────────
title: Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
children: [
_LogoBadge(),
const SizedBox(width: 10),
Text(
AppInformation.appName.split(' ')[0].tr,
style: const TextStyle(
color: _Token.accent,
fontSize: 20,
fontWeight: FontWeight.w800,
letterSpacing: 0.8,
),
),
],
),
),
actions: [
// ── Refuse Counter ───────────────────
GetBuilder<HomeCaptainController>(
builder: (c) => _PillBadge(
icon: Icons.block_rounded,
label: c.countRefuse.toString(),
color: _Token.danger,
),
),
const SizedBox(width: 6),
// ── Map Controls Row ─────────────────
_AppBarControls(ctrl: ctrl),
const SizedBox(width: 8),
],
);
}
}
class _LogoBadge extends StatelessWidget {
@override
Widget build(BuildContext context) => Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: _Token.surfaceCard(context),
shape: BoxShape.circle,
border: Border.all(color: _Token.accent, width: 1.5),
),
child: ClipOval(
child: Image.asset('assets/images/logo.gif', fit: BoxFit.cover),
),
);
}
class _PillBadge extends StatelessWidget {
final IconData icon;
final String label;
final Color color;
const _PillBadge(
{required this.icon, required this.label, required this.color});
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.12),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color.withOpacity(0.35)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: color, size: 13),
const SizedBox(width: 4),
Text(
label,
style: TextStyle(
color: color, fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
);
}
class _AppBarControls extends StatelessWidget {
final HomeCaptainController ctrl;
const _AppBarControls({required this.ctrl});
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
decoration: BoxDecoration(
color: _Token.surfaceCard(context),
borderRadius: BorderRadius.circular(_Token.radiusSm),
border: Border.all(color: _Token.border(context)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Navigation
_IconBtn(
icon: Icons.map_rounded,
color: _Token.success,
tooltip: 'Navigation'.tr,
onTap: () => Get.to(() => const NavigationView()),
),
// Heatmap
GetBuilder<HomeCaptainController>(
builder: (c) => _IconBtn(
icon: Icons.local_fire_department_rounded,
color:
c.isHeatmapVisible ? Colors.orange : Colors.grey.shade600,
tooltip: 'Heatmap'.tr,
onTap: c.toggleHeatmap,
),
),
// Center on me
_IconBtn(
icon: Icons.my_location_rounded,
color: _Token.accent,
tooltip: 'My Location'.tr,
onTap: () {
ctrl.mapHomeCaptainController?.animateCamera(
CameraUpdate.newLatLngZoom(
Get.find<LocationController>().myLocation,
17.5,
),
);
},
),
],
),
);
}
class _IconBtn extends StatelessWidget {
final IconData icon;
final Color color;
final String tooltip;
final VoidCallback onTap;
const _IconBtn(
{required this.icon,
required this.color,
required this.tooltip,
required this.onTap});
@override
Widget build(BuildContext context) => Tooltip(
message: tooltip,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 6),
child: Icon(icon, color: color, size: 20),
),
),
);
}
// ─────────────────────────────────────────────
// Map View
// ─────────────────────────────────────────────
class _MapView extends StatelessWidget {
const _MapView();
@override
Widget build(BuildContext context) {
return GetBuilder<HomeCaptainController>(
builder: (ctrl) {
if (ctrl.isLoading) return const MyCircularProgressIndicator();
return GetBuilder<SettingController>(
builder: (s) => GetBuilder<LocationController>(
builder: (loc) => IntaleqMap(
apiKey: Env.mapSaasKey,
onMapCreated: ctrl.onMapCreated,
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
initialCameraPosition: CameraPosition(
target: (loc.myLocation.latitude == 0 ||
loc.myLocation.latitude.isNaN)
? ctrl.myLocation
: loc.myLocation,
zoom: 15,
),
mapType:
s.isMapDarkMode ? IntaleqMapType.normal : IntaleqMapType.light,
polygons: ctrl.heatmapPolygons,
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: loc.myLocation,
rotation: loc.heading,
flat: true,
anchor: const Offset(0.5, 0.5),
icon: ctrl.carIcon,
)
},
myLocationButtonEnabled: false,
myLocationEnabled: false,
compassEnabled: false,
zoomControlsEnabled: false,
),
),
);
},
);
}
}
// ─────────────────────────────────────────────
// Bottom Status Bar — no blur, solid card
// ─────────────────────────────────────────────
class _BottomStatusBar extends StatelessWidget {
const _BottomStatusBar();
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 12,
left: 12,
right: 12,
child: GestureDetector(
onTap: () => _showDetailsSheet(context),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: _Token.surfaceCard(context),
borderRadius: BorderRadius.circular(_Token.radius),
border: Border.all(color: _Token.border(context)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.35),
blurRadius: 16,
offset: const Offset(0, 4),
)
],
),
child: Row(
children: [
// Online toggle
const ConnectWidget(),
const Spacer(),
// Ride count
GetBuilder<HomeCaptainController>(
builder: (c) => _StatChip(
icon: Icons.directions_car_rounded,
value: c.countRideToday,
label: 'Rides'.tr,
color: _Token.info,
),
),
const SizedBox(width: 14),
// Today earnings
GetBuilder<HomeCaptainController>(
builder: (c) => _StatChip(
icon: Entypo.wallet,
value: c.totalMoneyToday.toString(),
label: 'Today'.tr,
color: _Token.success,
),
),
// Chevron hint
const SizedBox(width: 8),
const Icon(Icons.keyboard_arrow_up_rounded,
color: Colors.grey, size: 18),
],
),
),
),
);
}
void _showDetailsSheet(BuildContext context) {
final ctrl = Get.find<HomeCaptainController>();
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (_) => _DetailsSheet(ctrl),
);
}
}
class _StatChip extends StatelessWidget {
final IconData icon;
final String value;
final String label;
final Color color;
const _StatChip(
{required this.icon,
required this.value,
required this.label,
required this.color});
@override
Widget build(BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Icon(icon, color: color, size: 16),
const SizedBox(width: 4),
Text(
value,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15),
),
],
),
Text(label,
style: const TextStyle(color: Colors.white38, fontSize: 11)),
],
);
}
// ─────────────────────────────────────────────
// Details Bottom Sheet — replaces AlertDialog
// ─────────────────────────────────────────────
class _DetailsSheet extends StatelessWidget {
final HomeCaptainController ctrl;
const _DetailsSheet(this.ctrl);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: _Token.surfaceCard(context),
borderRadius: BorderRadius.circular(24),
),
padding: const EdgeInsets.fromLTRB(20, 12, 20, 28),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Drag handle
Container(
width: 36,
height: 4,
decoration: BoxDecoration(
color: _Token.border(context),
borderRadius: BorderRadius.circular(2)),
),
const SizedBox(height: 16),
Text(
'Your Activity'.tr,
style: TextStyle(
color: _Token.text(context),
fontSize: 18,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Divider(color: _Token.border(context), height: 1),
const SizedBox(height: 16),
_SheetRow(
icon: Entypo.wallet,
color: _Token.success,
label: 'Today'.tr,
value: ctrl.totalMoneyToday.toString(),
),
const SizedBox(height: 12),
_SheetRow(
icon: Entypo.wallet,
color: _Token.accent,
label: AppInformation.appName,
value: ctrl.totalMoneyInSEFER.toString(),
),
const SizedBox(height: 12),
Divider(color: _Token.border(context), height: 1),
const SizedBox(height: 12),
_SheetRow(
icon: Icons.timer_outlined,
color: _Token.success,
label: 'Active Duration'.tr,
value: ctrl.stringActiveDuration,
),
const SizedBox(height: 12),
_SheetRow(
icon: Icons.access_time_rounded,
color: _Token.info,
label: 'Total Connection'.tr,
value: ctrl.totalDurationToday,
),
const SizedBox(height: 12),
Divider(color: _Token.border(context), height: 1),
const SizedBox(height: 12),
_SheetRow(
icon: Icons.star_rounded,
color: _Token.accent,
label: 'Total Points'.tr,
value: ctrl.totalPoints.toString(),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () => Get.back(),
style: TextButton.styleFrom(
backgroundColor: _Token.accentSoft(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(_Token.radiusSm)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text('Close'.tr,
style: const TextStyle(
color: _Token.accent, fontWeight: FontWeight.bold)),
),
),
],
),
);
}
}
class _SheetRow extends StatelessWidget {
final IconData icon;
final Color color;
final String label;
final String value;
const _SheetRow(
{required this.icon,
required this.color,
required this.label,
required this.value});
@override
Widget build(BuildContext context) => Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 12),
Text(label,
style: TextStyle(color: _Token.textDim(context), fontSize: 14)),
const Spacer(),
Text(value,
style: TextStyle(
color: color, fontSize: 16, fontWeight: FontWeight.bold)),
],
);
}
// ─────────────────────────────────────────────
// Floating Action Buttons (right side)
// ─────────────────────────────────────────────
class _FloatingControls extends StatelessWidget {
const _FloatingControls({super.key});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 88,
right: 12,
child: GetBuilder<HomeCaptainController>(
builder: (ctrl) => Column(
children: [
// Bubble/overlay mode (Android only)
if (Platform.isAndroid) ...[
_Fab(
onTap: () =>
Bubble().startBubbleHead(sendAppToBackground: true),
tooltip: 'Overlay'.tr,
child: Image.asset('assets/images/logo1.png',
width: 26, height: 26),
),
const SizedBox(height: 10),
],
// Available rides
_Fab(
onTap: () => Get.to(() => const AvailableRidesPage()),
icon: Icons.list_alt_rounded,
color: AppColor.primaryColor,
tooltip: 'Available Rides'.tr,
),
// Continue active ride
if (box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Begin') ...[
const SizedBox(height: 10),
_Fab(
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();
}
},
icon: Icons.navigation_rounded,
color: _Token.info,
tooltip: 'Continue Ride'.tr,
),
],
],
),
),
);
}
}
class _Fab extends StatelessWidget {
final VoidCallback onTap;
final IconData? icon;
final Widget? child;
final Color? color;
final String? tooltip;
const _Fab(
{required this.onTap, this.icon, this.child, this.color, this.tooltip});
@override
Widget build(BuildContext context) => Tooltip(
message: tooltip ?? '',
child: Material(
color: color ?? _Token.surfaceCard(context),
shape: const CircleBorder(),
elevation: 6,
shadowColor: (color ?? Colors.black).withOpacity(0.4),
child: InkWell(
onTap: onTap,
customBorder: const CircleBorder(),
child: SizedBox(
width: 52,
height: 52,
child: Center(
child: child ??
Icon(icon,
color: color != null ? Colors.white : _Token.accent,
size: 24),
),
),
),
),
);
}
// ─────────────────────────────────────────────
// Helper
// ─────────────────────────────────────────────
Future<void> checkForAppliedRide(BuildContext context) async {
checkForPendingOrderFromServer();
}

View File

@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import '../../../../constant/api_key.dart';
import '../../../../controller/functions/location_controller.dart';
import '../../../../controller/home/captin/home_captain_controller.dart';
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>();
return Obx(() {
final LatLng currentLocation = LatLng(
locationController.myLocation.latitude,
locationController.myLocation.longitude);
final double currentHeading = locationController.heading;
return IntaleqMap(
apiKey: AK.mapSaasKey,
initialCameraPosition: CameraPosition(
target: currentLocation,
zoom: 15,
bearing: currentHeading,
),
markers: {
Marker(
markerId: const MarkerId('myLocation'),
position: currentLocation,
rotation: currentHeading,
icon: homeCaptainController.carIcon,
anchor: const Offset(0.5, 0.5),
flat: true,
zIndex: 2,
)
},
onMapCreated: (IntaleqMapController controller) {
// You can assign this controller if needed
},
);
});
}
}

View File

@@ -0,0 +1,84 @@
// 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
// ),
// ),
// );
// }

View File

@@ -0,0 +1,151 @@
import 'package:siro_driver/controller/functions/tts.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/controller/home/payment/captain_wallet_controller.dart';
import '../../../../../constant/style.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(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: homeCaptainController.isActive
? [const Color(0xFF00C853), const Color(0xFF00E676)]
: [Colors.grey.shade600, Colors.grey.shade400],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: (homeCaptainController.isActive
? const Color(0xFF00C853)
: Colors.grey)
.withOpacity(0.4),
spreadRadius: 0,
blurRadius: 15,
offset: const Offset(0, 5),
),
],
),
child: CupertinoButton(
onPressed: homeCaptainController.onButtonSelected,
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
homeCaptainController.isActive
? Icons.power_settings_new_rounded
: Icons.power_off_rounded,
color: Colors.white,
size: 20,
),
),
const SizedBox(width: 10),
Text(
homeCaptainController.isActive
? 'Online'.tr
: 'Offline'.tr,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
],
),
),
)),
);
}
}

View File

@@ -0,0 +1,323 @@
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/controller/firebase/local_notification.dart';
import 'package:siro_driver/main.dart';
import 'package:siro_driver/views/home/Captin/driver_map_page.dart';
import 'package:siro_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
import 'package:siro_driver/views/auth/syria/registration_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:siro_driver/controller/home/captin/home_captain_controller.dart';
import 'package:siro_driver/views/widgets/mydialoug.dart';
import '../../../../../constant/links.dart';
import '../../../../../controller/firebase/notification_service.dart';
import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../../print.dart';
import '../../../../Rate/ride_calculate_driver.dart';
// ─────────────────────────────────────────────
// Design Tokens (Responsive)
// ─────────────────────────────────────────────
class _T {
static Color surface(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? const Color(0xFF16213E)
: Colors.white;
static const Color accent = Color(0xFFF0A500);
static const Color blue = Color(0xFF3498DB);
static Color border(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark
? const Color(0xFF2A2A4A)
: Colors.grey.withOpacity(0.2);
static const double radius = 14.0;
}
// ─────────────────────────────────────────────
// Left Side Menu
// ─────────────────────────────────────────────
/// Returns the vertical icon column anchored to the left-center of the map.
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
return GetBuilder<HomeCaptainController>(
builder: (ctrl) => Positioned(
// Place just above the bottom status bar
bottom: 100,
left: 10,
child: Builder(builder: (context) {
return Container(
decoration: BoxDecoration(
color: _T.surface(context),
borderRadius: BorderRadius.circular(_T.radius),
border: Border.all(color: _T.border(context)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 10,
offset: const Offset(2, 4),
),
],
),
padding: const EdgeInsets.symmetric(vertical: 6),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ── 1. Active Ride shortcut ──────────
_MenuIcon(
icon: Icons.directions_car_rounded,
color:
box.read(BoxName.rideArgumentsFromBackground) == 'failure'
? Colors.red.shade400
: Colors.green.shade400,
tooltip: 'Active Ride'.tr,
onTap: () async {
await checkForPendingOrderFromServer();
if (box.read(BoxName.rideArgumentsFromBackground) !=
'failure') {
Get.to(
() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArgumentsFromBackground),
);
} else {
MyDialog().getDialog(
'Ride info'.tr,
'you dont have accepted ride'.tr,
() => Get.back(),
);
}
},
onLongPress: () {
box.write(BoxName.rideArgumentsFromBackground, 'failure');
box.write(BoxName.statusDriverLocation, 'off');
box.write(BoxName.rideStatus, 'no_ride');
Log.print(box.read(BoxName.statusDriverLocation));
ctrl.update();
},
),
_Divider(context),
// ── 2. Earnings Chart ────────────────
_MenuIcon(
icon: FontAwesome5.chart_bar,
color: _T.blue,
tooltip: 'Earnings'.tr,
onTap: () {
final now = DateTime.now();
final lastTimeRaw = box.read(BoxName.lastTimeStaticThrottle);
DateTime? lastTime;
if (lastTimeRaw != null) {
try {
lastTime = DateTime.parse(lastTimeRaw.toString());
} catch (_) {
lastTime = null;
}
}
if (lastTime == null ||
now.difference(lastTime).inMinutes >= 2) {
box.write(
BoxName.lastTimeStaticThrottle, now.toIso8601String());
Get.to(() => RideCalculateDriver());
} else {
final left = 2 - now.difference(lastTime).inMinutes;
NotificationController().showNotification(
'Intaleq Driver'.tr,
'${'Please wait'.tr} $left ${"minutes before trying again.".tr}',
'ding',
'',
);
}
},
onLongPress: () =>
box.write(BoxName.statusDriverLocation, 'off'),
),
// ── 3. VIP Orders (2023+ cars only) ──
if (int.tryParse(box.read(BoxName.carYear).toString()) != null &&
int.parse(box.read(BoxName.carYear).toString()) > 2023) ...[
_Divider(context),
_MenuIcon(
icon: Octicons.watch,
color: _T.accent,
tooltip: 'VIP Orders'.tr,
onTap: () => Get.to(() => const VipOrderPage()),
),
],
// _Divider(context),
// // ── 4. Driver Registration ──────────
// _MenuIcon(
// icon: Icons.person_add_alt_1_rounded,
// color: Colors.purple.shade400,
// tooltip: 'Registration'.tr,
// onTap: () => Get.to(() => const RegistrationView()),
// ),
],
),
);
}),
),
);
}
// ─────────────────────────────────────────────
// Reusable sub-widgets
// ─────────────────────────────────────────────
class _MenuIcon extends StatelessWidget {
final IconData icon;
final Color color;
final String tooltip;
final VoidCallback onTap;
final VoidCallback? onLongPress;
const _MenuIcon({
required this.icon,
required this.color,
required this.tooltip,
required this.onTap,
this.onLongPress,
});
@override
Widget build(BuildContext context) => Tooltip(
message: tooltip,
child: InkWell(
onTap: onTap,
onLongPress: onLongPress,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Icon(icon, color: color, size: 26),
),
),
);
}
/// Thin separator between icons
class _Divider extends StatelessWidget {
final BuildContext context;
const _Divider(this.context);
@override
Widget build(BuildContext context) => Container(
height: 1,
width: 32,
margin: const EdgeInsets.symmetric(horizontal: 6),
color: _T.border(this.context),
);
}
// ─────────────────────────────────────────────
// Server Helpers (unchanged logic)
// ─────────────────────────────────────────────
Future<void> checkForPendingOrderFromServer() async {
bool isProcessing = false;
if (isProcessing) return;
final driverId = box.read(BoxName.driverID)?.toString();
if (driverId == null) return;
isProcessing = true;
try {
var response = await CRUD().post(
link: AppLink.getArgumentAfterAppliedFromBackground,
payload: {'driver_id': driverId},
);
Log.print('response: $response');
if (response['status'] == 'success') {
final Map<String, dynamic> orderInfo = response['message'];
final Map<String, dynamic> rideArgs =
_transformServerDataToAppArguments(orderInfo);
final customerToken = response['message']['token_passenger'];
final orderId = response['message']['ride_id'].toString();
box.write(BoxName.rideArgumentsFromBackground, rideArgs);
box.write(BoxName.statusDriverLocation, 'on');
box.write(BoxName.rideStatus, 'Apply');
Get.put(OrderRequestController()).changeApplied();
Get.to(
() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArgumentsFromBackground),
);
} else {
box.write(BoxName.rideArgumentsFromBackground, 'failure');
}
} catch (_) {
// silent
} finally {
isProcessing = false;
}
}
Map<String, dynamic> _transformServerDataToAppArguments(
Map<String, dynamic> d) {
String s(String key, [String def = 'unknown']) => d[key]?.toString() ?? def;
return {
'passengerLocation': s('passenger_location'),
'passengerDestination': s('passenger_destination'),
'Duration': s('duration'),
'totalCost': s('total_cost'),
'Distance': s('distance'),
'name': s('name'),
'phone': s('phone'),
'email': s('email'),
'tokenPassenger': s('token_passenger'),
'direction': s('direction_url'),
'DurationToPassenger': s('duration_to_passenger'),
'rideId': s('ride_id'),
'passengerId': s('passenger_id'),
'driverId': s('driver_id'),
'durationOfRideValue': s('duration_of_ride'),
'paymentAmount': s('payment_amount'),
'paymentMethod': s('payment_method'),
'passengerWalletBurc': s('passenger_wallet_burc'),
'timeOfOrder': s('time_of_order'),
'totalPassenger': s('total_passenger'),
'carType': s('car_type'),
'kazan': s('kazan'),
'startNameLocation': s('start_name_location'),
'endNameLocation': s('end_name_location'),
'step0': s('step0'),
'step1': s('step1'),
'step2': s('step2'),
'step3': s('step3'),
'step4': s('step4'),
'WalletChecked': (d['wallet_checked'] == 1).toString(),
'isHaveSteps': (d['has_steps'] == 1) ? 'startEnd' : 'noSteps',
};
}
void _sendAcceptanceNotification(String? customerToken, dynamic rideId) {
if (customerToken == null || customerToken.isEmpty) return;
List<String> body = [
box.read(BoxName.driverID).toString(),
box.read(BoxName.nameDriver).toString(),
box.read(BoxName.tokenDriver).toString(),
rideId.toString(),
];
NotificationService.sendNotification(
target: customerToken,
title: 'Accepted Ride'.tr,
body: 'your ride is Accepted'.tr,
isTopic: false,
tone: 'start',
driverList: body,
category: 'Accepted Ride',
);
}

View File

@@ -0,0 +1,67 @@
import 'dart:convert';
import 'dart:math';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.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);
}
}