25-7-28-2

This commit is contained in:
Hamza-Ayed
2025-07-28 12:21:28 +03:00
parent 660d60e1f5
commit 83a97baed1
549 changed files with 109870 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class AboutPage extends StatelessWidget {
const AboutPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'About Us'.tr,
body: [
// Company Logo (consider adding an image asset)
ListView(
children: [
Center(
child: Image.asset(
'assets/images/logo.png', // Replace with your logo image asset path
height: 100.0,
width: 100.0,
),
), // Company Name and Location
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}',
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
), // Security Features List
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
children: [
Row(
children: [
const Icon(Icons.lock, color: Colors.blue),
const SizedBox(width: 8.0),
Text(
'Most Secure Methods'.tr,
style: AppStyle.title,
),
],
),
const SizedBox(height: 8.0),
Row(
children: [
const Icon(Icons.phone, color: Colors.blue),
const SizedBox(width: 8.0),
Text(
'In-App VOIP Calls'.tr,
style: AppStyle.title,
),
],
),
const SizedBox(height: 8.0),
Row(
children: [
const Icon(Icons.videocam, color: Colors.blue),
const SizedBox(width: 8.0),
Text(
'Recorded Trips for Safety'.tr,
style: AppStyle.title,
),
],
),
],
),
), // Affordability Highlight
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
],
),
// About Us Text
],
isleading: true);
}
}

View File

@@ -0,0 +1,145 @@
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../constant/style.dart';
class FrequentlyQuestionsPage extends StatelessWidget {
const FrequentlyQuestionsPage({super.key});
@override
Widget build(BuildContext context) {
String selectedPayment = 'cash'; // Replace with your initial selection
bool canCancelRide = false;
return MyScafolld(
title: 'Frequently Questions'.tr,
body: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
// Question 1: How do I request a ride?
ExpansionTile(
title: Text(
'How do I request a ride?'.tr,
style: AppStyle.title,
),
children: [
Text(
'Step-by-step instructions on how to request a ride through the Intaleq app.'
.tr,
style: AppStyle.title,
),
],
),
// Question 2: What types of vehicles are available?
ExpansionTile(
title: Text(
'What types of vehicles are available?'.tr,
style: AppStyle.title,
),
children: [
Text(
'Intaleq offers a variety of vehicle options to suit your needs, including economy, comfort, and luxury. Choose the option that best fits your budget and passenger count.'
.tr,
style: AppStyle.title,
),
],
),
// Question 3: How can I pay for my ride?
ExpansionTile(
title: Text(
'How can I pay for my ride?'.tr,
style: AppStyle.title,
),
children: [
Text(
'Intaleq offers multiple payment methods for your convenience. Choose between cash payment or credit/debit card payment during ride confirmation.'
.tr,
style: AppStyle.title,
),
],
),
// Question 4: Can I cancel my ride? (if applicable)
ExpansionTile(
title: Text(
'Can I cancel my ride?'.tr,
style: AppStyle.title,
),
children: [
Text(
'Yes, you can cancel your ride under certain conditions (e.g., before driver is assigned). See the Intaleq cancellation policy for details.'
.tr,
style: AppStyle.title,
),
],
),
// Question 5 & 6: Driver-specific questions
ExpansionTile(
title: Text(
'Driver Registration & Requirements'.tr,
style: AppStyle.title,
),
children: [
Text(
'${'How can I register as a driver?'.tr}\n${'What are the requirements to become a driver?'.tr}',
style: AppStyle.title,
),
InkWell(
onTap: () {
MyDialog().getDialog('title', 'midTitle', () {
; //todo add in this dialog papers for driver
});
},
child: Text(
'Visit our website or contact Intaleq support for information on driver registration and requirements.'
.tr,
style: AppStyle.title,
),
),
],
),
// Question 7: How do I communicate with the other party?
ExpansionTile(
title: Text(
'How do I communicate with the other party (passenger/driver)?'
.tr,
style: AppStyle.title,
),
children: [
Text(
'Intaleq provides in-app chat functionality to allow you to communicate with your driver or passenger during your ride.'
.tr,
style: AppStyle.title,
),
],
),
// Question 8: What safety measures does Intaleq offer?
ExpansionTile(
title: Text(
'What safety measures does Intaleq offer?'.tr,
style: AppStyle.title,
),
children: [
Text(
'Intaleq prioritizes your safety. We offer features like driver verification, in-app trip tracking, and emergency contact options.'
.tr,
style: AppStyle.title,
),
],
),
],
),
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,157 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/profile/setting_controller.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/lang/languages.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../../../controller/functions/vibrate.dart';
import '../../../auth/country_widget.dart';
import 'about_us.dart';
import 'frequantly_question.dart';
import 'using_app_page.dart';
class SettingsCaptain extends StatelessWidget {
const SettingsCaptain({super.key});
@override
Widget build(BuildContext context) {
Get.put(SettingController());
Get.put(HomePageController());
return MyScafolld(
title: 'Settings'.tr,
body: [
ListView(
physics: const BouncingScrollPhysics(),
children: <Widget>[
// General Section
_buildSectionHeader('General'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
children: [
CupertinoListTile(
leading: const Icon(CupertinoIcons.globe),
title: Text('Language'.tr, style: AppStyle.headTitle2),
subtitle: Text('You can change the language of the app'.tr,
style: AppStyle.subtitle),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(const Language()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.flag_fill),
title: Text('Change Country'.tr, style: AppStyle.headTitle2),
subtitle: Text(
'You can change the Country to get all features'.tr,
style: AppStyle.subtitle),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(
MyScafolld(
title: 'Change Country'.tr,
body: [CountryPickerFromSetting()],
isleading: true,
// isCupertino: true, // Indicate it's a Cupertino style page
),
),
),
],
),
// App Preferences Section
_buildSectionHeader('App Preferences'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
children: [
CupertinoListTile(
leading: Icon(
CupertinoIcons.map_pin_ellipse,
color: AppColor.redColor,
),
title: Text('Google Map App'.tr, style: AppStyle.headTitle2),
subtitle: Text(
'If you want to make Google Map App run directly when you apply order'
.tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<SettingController>(
builder: (settingController) {
return CupertinoSwitch(
value: settingController.isGoogleMapsEnabled,
activeTrackColor: AppColor.primaryColor,
onChanged: (bool value) {
settingController.onChangMapApp();
},
);
},
),
),
CupertinoListTile(
leading: Icon(Icons.vibration),
title: Text('Vibration'.tr, style: AppStyle.headTitle2),
subtitle: Text(
"You can change the vibration feedback for all buttons".tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<HomePageController>(
builder: (controller) => CupertinoSwitch(
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
activeTrackColor: AppColor.primaryColor,
),
),
onTap: () => print('3'),
),
],
),
// Help & Support Section
_buildSectionHeader('Help & Support'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
children: [
CupertinoListTile(
leading: const Icon(CupertinoIcons.question_circle_fill),
title: Text('Frequently Questions'.tr,
style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.hand_raised_fill),
title:
Text("How to use Intaleq".tr, style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const UsingAppPage()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.info_circle_fill),
title: Text('About Us'.tr, style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const AboutPage()),
),
],
),
const SizedBox(height: 16),
],
),
],
isleading: true,
// isCupertino: true, // Indicate this screen is generally Cupertino style
);
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.only(left: 16.0, top: 20.0, bottom: 10.0),
child: Text(
title,
style: const TextStyle(
fontSize: 17.0,
fontWeight: FontWeight.w600,
color: CupertinoColors.secondaryLabel,
),
),
);
}
}

View File

@@ -0,0 +1,112 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class UsingAppPage extends StatelessWidget {
const UsingAppPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: "How to use Intaleq".tr,
body: [
SizedBox(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
InkWell(
onTap: () {
MyDialogContent().getDialog(
"What are the order details we provide to you?".tr,
Image.network(
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg',
height: 300,
width: 300,
fit: BoxFit.cover,
), () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What are the order details we provide to you?".tr,
style: AppStyle.title,
),
),
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What are the order details we provide to you?".tr,
'''Intaleq Wallet Features:
Transfer money multiple times.
Transfer to anyone.
Make purchases.
Charge your account.
Charge a friend's Intaleq account.
Store your money with us and receive it in your bank as a monthly salary.'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is the feature of our wallet?".tr,
style: AppStyle.title,
),
),
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What is Types of Trips in Intaleq?".tr,
'''Types of Trips in Intaleq:
Comfort: For cars newer than 2017 with air conditioning.
Lady: For girl drivers.
Speed: For fixed salary and endpoints.
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
Raih Gai: For same-day return trips longer than 50km.
'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is Types of Trips in Intaleq?".tr,
style: AppStyle.title,
),
),
),
),
],
),
),
)
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,193 @@
import 'package:sefer_driver/constant/info.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:video_player/video_player.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import '../../../../controller/home/captin/help/video_controller.dart';
import 'package:flutter/cupertino.dart';
class VideoListPage extends StatelessWidget {
final VideoController videoController = Get.put(VideoController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Videos Tutorials'.tr),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 10.0), // Outer padding around the list
child: GetBuilder<VideoController>(
builder: (videoController) {
return ListView.builder(
itemCount: videoController.videos.length,
itemBuilder: (context, index) {
final video = videoController.videos[index];
return Container(
margin: const EdgeInsets.symmetric(
vertical: 8.0), // Spacing between each card
decoration: BoxDecoration(
color: CupertinoColors.white,
borderRadius:
BorderRadius.circular(12.0), // Rounded corners
boxShadow: [
BoxShadow(
color: CupertinoColors.systemGrey.withOpacity(0.3),
offset: const Offset(
0, 4), // Offset for shadow to appear below
blurRadius: 10.0, // Blur for softer shadow effect
),
],
border: Border.all(
color: CupertinoColors.systemGrey4,
width: 0.5, // Subtle border for a refined iOS-like look
),
),
child: CupertinoListTile(
title: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
video['title'],
style: const TextStyle(
color: CupertinoColors.black,
fontWeight: FontWeight.bold,
),
),
),
subtitle: Text(
video['description'],
style: const TextStyle(
color: CupertinoColors.systemGrey,
),
),
onTap: () {
// Navigate to video player page (iOS-style)
Get.to(() => VideoPlayerPage1(videoUrl: video['url']));
},
),
);
},
);
},
),
),
),
);
}
}
class VideoPlayerPage extends StatelessWidget {
final String videoUrl;
VideoPlayerPage({required this.videoUrl});
final VideoController videoController = Get.put(VideoController());
@override
Widget build(BuildContext context) {
// Initialize the video when the page is loaded
videoController.initializeVideo(videoUrl);
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text(AppInformation.appName),
),
child: SafeArea(
child: Center(
child: GetBuilder<VideoController>(
builder: (controller) {
if (!controller.videoPlayerController.value.isInitialized) {
return const CircularProgressIndicator(); // Show loading indicator while initializing
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Video player widget
AspectRatio(
aspectRatio:
controller.videoPlayerController.value.aspectRatio,
// child: VideoPlayer(controller.videoPlayerController),
child: VideoPlayer(controller.videoPlayerController),
),
const SizedBox(height: 20),
// Play/pause button
CupertinoButton(
onPressed: () {
if (controller.videoPlayerController.value.isPlaying) {
controller.pause();
} else {
controller.play();
}
},
child: Icon(
controller.videoPlayerController.value.isPlaying
? CupertinoIcons.pause
: CupertinoIcons.play_arrow,
color: CupertinoColors.activeBlue,
size: 30,
),
),
],
);
},
),
),
),
);
}
}
class VideoPlayerPage1 extends StatelessWidget {
final String videoUrl;
VideoPlayerPage1({required this.videoUrl});
@override
Widget build(BuildContext context) {
// Extract the video ID from the URL
String videoId = YoutubePlayer.convertUrlToId(videoUrl)!;
// Create a YoutubePlayerController
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: videoId,
flags: const YoutubePlayerFlags(
autoPlay: true,
loop: true,
mute: false,
captionLanguage: 'ar',
),
);
return Scaffold(
body: Stack(
children: [
// Full-screen YouTube player
Positioned.fill(
child: YoutubePlayer(
controller: _controller,
showVideoProgressIndicator: true,
),
),
// Overlay back button in the top left corner for exit
Positioned(
top: 40.0,
left: 16.0,
child: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,231 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/captin/help/assurance_controller.dart';
import 'package:flutter/cupertino.dart';
class AssuranceHealthPage extends StatelessWidget {
AssuranceHealthPage({super.key});
AssuranceHealthController assuranceHealthController =
Get.put(AssuranceHealthController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Health Insurance".tr),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GetBuilder<AssuranceHealthController>(
builder: (assuranceHealthController) {
return Column(
children: [
Text(
"When you complete 500 trips, you will be eligible for exclusive health insurance offers."
.tr,
textAlign: TextAlign.center,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
CupertinoButton.filled(
child: Text("Show My Trip Count".tr),
onPressed: () async {
assuranceHealthController.getTripCountByCaptain();
},
),
_buildTripCountAvatar(
assuranceHealthController.tripCount['count'] == null
? '0'
: assuranceHealthController.tripCount['count']
.toString(),
),
],
),
const SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Text(
"We have partnered with health insurance providers to offer you special health coverage. Complete 500 trips and receive a 20% discount on health insurance premiums."
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(fontWeight: FontWeight.w600),
textAlign: TextAlign.center,
),
),
),
const SizedBox(height: 10),
CupertinoButton.filled(
disabledColor: AppColor.blueColor,
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
borderRadius: BorderRadius.circular(12),
child: Text(
"Would you like to proceed with health insurance?".tr,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
),
onPressed: () async {
// Show confirmation dialog before proceeding
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
// Variable to store the health insurance provider chosen by the driver
TextEditingController providerController =
TextEditingController();
return CupertinoAlertDialog(
title: Text(
"Confirmation".tr,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 18),
),
content: Column(
children: [
Text(
"Would you like to proceed with health insurance?"
.tr,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 20),
CupertinoTextField(
controller: providerController,
placeholder:
"Do you have a disease for a long time?".tr,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(
color: CupertinoColors.systemGrey,
width: 1),
borderRadius: BorderRadius.circular(8),
),
),
],
),
actions: <Widget>[
CupertinoDialogAction(
isDefaultAction: true,
child: Text(
"Yes".tr,
style: const TextStyle(
color: CupertinoColors.activeBlue),
),
onPressed: () async {
// Ensure the provider name is not empty
if (providerController.text.isNotEmpty) {
// Call the function to insert data into the database
await assuranceHealthController
.addDriverHealthAssurance(
healthInsuranceProvider:
providerController.text,
);
// Close the dialog and navigate to a success screen or show a snackbar
Navigator.of(context).pop();
} else {
// Show an alert if the provider name is empty
showCupertinoDialog(
context: context,
builder: (_) => CupertinoAlertDialog(
title: Text("Error".tr),
content: Text(
"Do you have a disease for a long time?"
.tr),
actions: [
CupertinoDialogAction(
child: Text("OK".tr),
onPressed: () =>
Navigator.of(context).pop(),
),
],
),
);
}
},
),
CupertinoDialogAction(
child: Text(
"No".tr,
style: const TextStyle(
color: CupertinoColors.destructiveRed),
),
onPressed: () {
Navigator.of(context)
.pop(); // Just close the dialog
// Optionally show feedback if the driver opts out
// Get.snackbar(
// "Opted out".tr,
// "You have chosen not to proceed with health insurance."
// .tr,
// backgroundColor:
// CupertinoColors.systemGrey);
mySnackeBarError(
"You have chosen not to proceed with health insurance."
.tr);
},
),
],
);
},
);
},
),
],
);
}),
),
),
);
}
Widget _buildTripCountAvatar(String count) {
return Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const RadialGradient(
colors: [
Color(0xFF42A5F5),
Color(0xFF1976D2),
], // Health theme colors
center: Alignment.center,
radius: 0.8,
),
boxShadow: [
BoxShadow(
color: CupertinoColors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Center(
child: Text(
count,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
);
}
}

View File

@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
class BottomBarController extends GetxController {
var currentIndex = 0.obs;
void changePage(int index) {
currentIndex.value = index;
}
}
class HomeScreen extends StatelessWidget {
final BottomBarController controller = Get.put(BottomBarController());
HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom Bar Example'.tr),
),
body: Obx(() => IndexedStack(
index: controller.currentIndex.value,
children: const [
HomeView(),
ProfileView(),
StatisticsView(),
WalletView(),
],
)),
bottomNavigationBar: Obx(() => BottomNavigationBar(
backgroundColor: Colors.greenAccent,
currentIndex: controller.currentIndex.value,
onTap: controller.changePage,
items: [
BottomNavigationBarItem(
icon: const Icon(
Icons.home,
color: AppColor.primaryColor,
),
label: 'Home'.tr,
),
BottomNavigationBarItem(
icon: const Icon(
Icons.person,
color: AppColor.primaryColor,
),
label: 'Profile'.tr,
),
BottomNavigationBarItem(
icon: const Icon(
Icons.bar_chart,
color: AppColor.primaryColor,
),
label: 'Statistics'.tr,
),
BottomNavigationBarItem(
icon: const Icon(
Icons.account_balance_wallet,
color: AppColor.primaryColor,
),
label: 'Wallet'.tr,
),
],
)),
);
}
}
class HomeView extends StatelessWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context) {
Map<String, dynamic> data;
return const Center(
child: Column(
children: [
Text('Home View'),
],
),
);
}
}
class ProfileView extends StatelessWidget {
const ProfileView({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Profile View'),
);
}
}
class StatisticsView extends StatelessWidget {
const StatisticsView({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Statistics View'),
);
}
}
class WalletView extends StatelessWidget {
const WalletView({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Wallet View'),
);
}
}

View File

@@ -0,0 +1,303 @@
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/functions/camer_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
class CameraWidgetCardId extends StatelessWidget {
final CameraClassController cameraClassController =
Get.put(CameraClassController());
CameraWidgetCardId({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Scan Id'.tr,
body: [
Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: GetBuilder<CameraClassController>(
builder: (cameraClassController) =>
cameraClassController.isCameraInitialized
? Stack(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: FittedBox(
fit: BoxFit.fitWidth,
child: SizedBox(
width: Get.width * .9,
height: Get.width *
.9, // Set the desired aspect ratio here
child: CameraPreview(
cameraClassController.cameraController,
),
),
),
),
Positioned(
top: 72,
child: Container(
width: 230,
height: 25,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.yellowColor,
width: 2),
),
),
),
Positioned(
top: 60,
right: 5,
child: Container(
width: 230,
height: 25,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 156,
right: 5,
child: Container(
width: 140,
height: 20,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 175,
right: 5,
child: Container(
width: 140,
height: 15,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 191,
right: 5,
child: Container(
width: 140,
height: 15,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 207,
right: 5,
child: Container(
width: 140,
height: 15,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 225,
right: 5,
child: Container(
width: 140,
height: 15,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 115,
left: 25,
child: Container(
width: 120,
height: 110,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
],
)
: Container(
decoration: AppStyle.boxDecoration,
height: Get.width * 3 / 4,
width: Get.width,
child: Center(
child: Text(
'Camera not initilaized yet'.tr,
style: AppStyle.title,
),
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Scan ID MklGoogle'.tr, onPressed: () {}),
// cameraClassController.takePictureAndMLGoogleScan()),
MyElevatedButton(
title: 'Scan ID Tesseract'.tr, onPressed: () {}),
],
),
MyElevatedButton(
title: 'Scan ID Api'.tr,
onPressed: () => cameraClassController.extractCardId()),
GetBuilder<CameraClassController>(
builder: (cameraClassController) => Expanded(
child:
Text(cameraClassController.scannedText.toString())))
],
)
],
isleading: true);
}
}
class CameraWidgetPassPort extends StatelessWidget {
final CameraClassController cameraClassController =
Get.put(CameraClassController());
CameraWidgetPassPort({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Scan Id'.tr,
body: [
Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: GetBuilder<CameraClassController>(
builder: (cameraClassController) =>
cameraClassController.isCameraInitialized
? Stack(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: FittedBox(
fit: BoxFit.fitWidth,
child: SizedBox(
width: Get.width * .9,
height: Get.width *
.9, // Set the desired aspect ratio here
child: CameraPreview(
cameraClassController.cameraController,
),
),
),
),
Positioned(
top: 35,
left: 35,
width: Get.width * .77,
height:
17, //left":95.0,"top":134.0,"width":2909.0,"height":175.0
child: Container(
// width: 230,
// height: 25,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.yellowColor,
width: 2),
),
),
),
Positioned(
top: 60,
right: 25,
width: 90,
height: 25,
child: Container(
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
Positioned(
top: 110,
right: 90,
child: Container(
width: 140,
height: 20,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.blueColor, width: 2),
),
),
),
],
)
: Container(
decoration: AppStyle.boxDecoration,
height: Get.width * 3 / 4,
width: Get.width,
child: Center(
child: Text(
'Camera not initilaized yet',
style: AppStyle.title,
),
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Scan ID MklGoogle'.tr, onPressed: () => {}),
// cameraClassController.takePictureAndMLGoogleScan()),
MyElevatedButton(
title: 'Scan ID Tesseract'.tr, onPressed: () {}),
],
),
MyElevatedButton(
title: 'Scan ID Api'.tr,
onPressed: () => cameraClassController.extractCardId()),
GetBuilder<CameraClassController>(
builder: (cameraClassController) => Expanded(
child:
Text(cameraClassController.scannedText.toString())))
],
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,173 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import '../../../constant/colors.dart';
import '../../../controller/functions/location_controller.dart';
import '../../Rate/rate_passenger.dart';
import '../../widgets/my_textField.dart';
import 'mapDriverWidgets/driver_end_ride_bar.dart';
import 'mapDriverWidgets/google_driver_map_page.dart';
import 'mapDriverWidgets/passenger_info_window.dart';
import 'mapDriverWidgets/sos_connect.dart';
class PassengerLocationMapPage extends StatelessWidget {
PassengerLocationMapPage({super.key});
final LocationController locationController = Get.put(LocationController());
final MapDriverController mapDriverController =
Get.put(MapDriverController());
@override
Widget build(BuildContext context) {
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول
// هذا يعطي GetX وقته لتجهيز كل شيء
WidgetsBinding.instance.addPostFrameCallback((_) {
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
mapDriverController.argumentLoading();
});
} else {
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ
WidgetsBinding.instance.addPostFrameCallback((_) {
Get.snackbar("Error", "No order data found.");
Get.back();
});
}
mapDriverController.argumentLoading();
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
return Scaffold(
// backgroundColor: AppColor.blueColor,
// title: 'Map Passenger'.tr,
body: SafeArea(
child: Stack(
children: [
GoogleDriverMap(locationController: locationController),
const PassengerInfoWindow(),
CancelWidget(mapDriverController: mapDriverController),
driverEndRideBar(),
const SosConnect(),
speedCircle(),
// const GoogleMapApp(),
const PricesWindow(),
],
),
));
}
}
class CancelWidget extends StatelessWidget {
const CancelWidget({
super.key,
required this.mapDriverController,
});
final MapDriverController mapDriverController;
@override
Widget build(BuildContext context) {
return Positioned(
top: 10,
left: 5,
child: GestureDetector(
onTap: () {
Get.defaultDialog(
title: "Are you sure you want to cancel this trip?".tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text("Why do you want to cancel this trip?".tr),
Form(
key: mapDriverController.formKeyCancel,
child: MyTextForm(
controller: mapDriverController.cancelTripCotroller,
label: "Write the reason for canceling the trip".tr,
hint: "Write the reason for canceling the trip".tr,
type: TextInputType.name,
))
],
),
confirm: MyElevatedButton(
title: 'Ok'.tr,
kolor: AppColor.redColor,
onPressed: () async {
// todo add cancel and inform passenger to get new driver
await mapDriverController
.cancelTripFromDriverAfterApplied();
Get.back();
}),
cancel: MyElevatedButton(
title: 'No'.tr,
// kolor: AppColor.redColor,
onPressed: () {
Get.back();
}));
},
child: Container(
decoration: BoxDecoration(
color: AppColor.redColor,
borderRadius: BorderRadius.circular(15)),
child: const Padding(
padding: EdgeInsets.all(3),
child: Icon(
Icons.clear,
size: 40,
color: AppColor.secondaryColor,
),
),
),
),
);
}
}
class PricesWindow extends StatelessWidget {
const PricesWindow({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(builder: (mapDriverController) {
return mapDriverController.isPriceWindow
? Positioned(
bottom: Get.height * 1.2,
// top: Get.height * 3,
left: Get.height * 1,
right: Get.height * 1,
child: Container(
height: Get.height * 3,
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(3),
child: Text(
'Total Price is '.tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
)),
const SizedBox(
height: 20,
),
MyElevatedButton(
title: 'ok'.tr,
onPressed: () =>
Get.to(() => RatePassenger(), arguments: {
'rideId': mapDriverController.rideId,
'passengerId': mapDriverController.passengerId,
'driverId': mapDriverController.driverId
}))
],
),
),
)
: const SizedBox();
});
}
}

View File

@@ -0,0 +1,149 @@
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../controller/auth/captin/history_captain.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
class HistoryCaptain extends StatelessWidget {
const HistoryCaptain({super.key});
@override
Widget build(BuildContext context) {
Get.put(HistoryCaptainController());
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('History Page'.tr),
leading: CupertinoNavigationBarBackButton(
onPressed: () => Get.back(),
),
),
child: SafeArea(
child: GetBuilder<HistoryCaptainController>(
builder: (historyCaptainController) => historyCaptainController
.isloading
? const Center(child: CupertinoActivityIndicator())
: historyCaptainController.historyData['message'].length < 1
? Center(
child: Text(
'No ride Yet.'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
)
: ListView.builder(
itemCount: historyCaptainController
.historyData['message'].length,
itemBuilder: (BuildContext context, int index) {
var list = historyCaptainController
.historyData['message'][index];
return Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: CupertinoColors.systemGrey, width: 1),
borderRadius:
const BorderRadius.all(Radius.circular(8.0)),
),
child: CupertinoButton(
onPressed: () {
if (list['status'] != 'Cancel') {
historyCaptainController
.getHistoryDetails(list['order_id']);
} else {
MyDialog().getDialog(
'This Trip Cancelled'.tr,
'This Trip Cancelled'.tr,
() => Get.back(),
);
}
},
child: Container(
margin: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'OrderId'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
Text(
EncryptionHelper.instance
.decryptData(list['order_id']),
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'created time'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
Text(
list['created_at'],
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
Text(
list['status'],
style: EncryptionHelper.instance
.decryptData(
list['status']) ==
'Apply'
? CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemGreen)
: EncryptionHelper.instance.decryptData(
list['status']) ==
'Refused'
? CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemRed)
: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemYellow),
),
],
),
),
),
),
);
},
),
),
),
);
}
}

View File

@@ -0,0 +1,252 @@
import 'package:sefer_driver/controller/functions/location_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/auth/captin/history_captain.dart';
import 'package:sefer_driver/controller/functions/launch.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/cupertino.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
class HistoryDetailsPage extends StatelessWidget {
HistoryDetailsPage({super.key});
HistoryCaptainController historyCaptainController =
Get.put(HistoryCaptainController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Trip Detail'.tr),
leading: CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.back),
onPressed: () => Navigator.pop(context),
),
),
child: GetBuilder<HistoryCaptainController>(
builder: (historyCaptainController) {
var res = historyCaptainController.historyDetailsData['data'];
return historyCaptainController.isloading
? const Center(
child: CupertinoActivityIndicator(),
)
: CupertinoScrollbar(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
CupertinoButton(
onPressed: () {
String mapUrl =
'https://www.google.com/maps/dir/${EncryptionHelper.instance.decryptData(res['start_location'])}/${EncryptionHelper.instance.decryptData(res['end_location'])}/';
showInBrowser(mapUrl);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.activeBlue,
width: 2),
),
child: Column(
children: [
const SizedBox(
height: 20,
),
SizedBox(
height: MediaQuery.of(context).size.height *
0.3,
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: Get.find<LocationController>()
.myLocation,
tilt: 80,
zoom: 13,
),
zoomControlsEnabled: true,
polylines: {
Polyline(
polylineId: const PolylineId('route'),
points: [
LatLng(
double.parse(EncryptionHelper
.instance
.decryptData(
res['start_location'])
.toString()
.split(',')[0]),
double.parse(EncryptionHelper
.instance
.decryptData(
res['start_location'])
.toString()
.split(',')[1]),
),
LatLng(
double.parse(EncryptionHelper
.instance
.decryptData(
res['end_location'])
.toString()
.split(',')[0]),
double.parse(EncryptionHelper
.instance
.decryptData(
res['end_location'])
.toString()
.split(',')[1]),
)
],
color: CupertinoColors.activeGreen,
width: 5,
),
},
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Order ID'.tr} ${EncryptionHelper.instance.decryptData(res['id'])}',
style: CupertinoTheme.of(context)
.textTheme
.navActionTextStyle,
),
Text(
res['date'].toString(),
style: CupertinoTheme.of(context)
.textTheme
.navActionTextStyle,
),
],
),
],
),
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.activeGreen, width: 2),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Price is'.tr} ${EncryptionHelper.instance.decryptData(res['price_for_driver'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'Distance is'.tr} ${EncryptionHelper.instance.decryptData(res['distance'])} KM',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
),
const SizedBox(height: 20),
Text(
'Times of Trip'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
const SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.destructiveRed,
width: 2),
),
child: Column(
children: [
Text(
'${'Time to Passenger is'.tr} ${res['DriverIsGoingToPassenger']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'TimeStart is'.tr} ${res['rideTimeStart']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'Time Finish is'.tr} ${res['rideTimeFinish']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.systemGreen, width: 2),
),
child: Center(
child: Text(
'${'Passenger Name is'.tr} ${EncryptionHelper.instance.decryptData(res['first_name'])} ${EncryptionHelper.instance.decryptData(res['last_name'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.systemYellow,
width: 2),
),
child: Center(
child: Text(
'${'Status is'.tr} ${EncryptionHelper.instance.decryptData(res['status'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
),
),
],
),
),
),
);
},
),
);
}
}

View File

@@ -0,0 +1,589 @@
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.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:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.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%20Us/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 '../../../../constant/colors.dart';
import '../../../../controller/functions/upload_image.dart';
import '../About Us/video_page.dart';
import '../assurance_health_page.dart';
import '../maintain_center_page.dart';
import 'package:flutter/cupertino.dart';
// class CupertinoDrawerCaptain extends StatelessWidget {
// final ImageController imageController = Get.put(ImageController());
// @override
// Widget build(BuildContext context) {
// return CupertinoPageScaffold(
// navigationBar: CupertinoNavigationBar(
// middle: Text('Menu'.tr),
// ),
// child: SafeArea(
// child: CustomScrollView(
// slivers: [
// const SliverToBoxAdapter(child: const UserAccountHeader()),
// SliverList(
// delegate: SliverChildListDelegate([
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.money_dollar,
// text: 'Wallet'.tr,
// onTap: () => Get.to(() => WalletCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.person,
// text: 'Profile'.tr,
// onTap: () => Get.to(() => ProfileCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.clock,
// text: 'History of Trip'.tr,
// onTap: () => Get.to(() => const HistoryCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.car_detailed,
// text: 'Available for rides'.tr,
// onTap: () => Get.to(() => const AvailableRidesPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.bell,
// text: 'Notifications'.tr,
// onTap: () => Get.to(() => const NotificationCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.question_circle,
// text: 'Helping Center'.tr,
// onTap: () => Get.to(() => HelpCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.share,
// text: 'Share App'.tr,
// onTap: () => Get.to(() => InviteScreen()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.wrench,
// text: "Maintenance Center".tr,
// onTap: () => Get.to(() => MaintainCenterPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon:
// CupertinoIcons.heart, // Updated icon to represent health
// text: "Health Insurance".tr, // Updated English text
// onTap: () => Get.to(() => AssuranceHealthPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.mail,
// text: "Contact Us".tr,
// onTap: () => Get.to(() => ContactUsPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon:
// Icons.play_circle_filled, // Icon representing video play
// text: 'Videos Tutorials'.tr,
// onTap: () => Get.to(() => VideoListPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: Icons.star, // Another option with a filled star icon
// text: "Rate Our App".tr,
// onTap: () => Get.to(() => RatingScreen()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.settings,
// text: 'Settings'.tr,
// onTap: () => Get.to(() => const SettingsCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.square_arrow_right,
// text: 'Sign Out'.tr,
// onTap: () => Get.to(() => const LogoutCaptain()),
// ),
// _buildDivider(),
// ]),
// ),
// ],
// ),
// ),
// );
// }
// Widget _buildDivider() {
// return const Divider(
// thickness: 1,
// height: 1,
// color: CupertinoColors.systemGrey4,
// );
// }
// Widget _buildDrawerItem({
// required IconData icon,
// required String text,
// required VoidCallback onTap,
// }) {
// return CupertinoButton(
// onPressed: onTap,
// child: Row(
// children: [
// Icon(icon, color: CupertinoColors.activeBlue),
// const SizedBox(width: 10),
// Text(text, style: const TextStyle(color: CupertinoColors.label)),
// const Spacer(),
// const Icon(CupertinoIcons.right_chevron,
// color: CupertinoColors.systemGrey),
// ],
// ),
// );
// }
// }
// class UserAccountHeader extends StatelessWidget {
// const UserAccountHeader({super.key});
// @override
// Widget build(BuildContext context) {
// return Container(
// padding: const EdgeInsets.all(16),
// decoration: const BoxDecoration(
// gradient: LinearGradient(
// colors: [AppColor.blueColor, AppColor.twitterColor],
// begin: Alignment.topLeft,
// end: Alignment.bottomRight,
// ),
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// GetBuilder<ImageController>(
// builder: (imageController) {
// return Stack(
// children: [
// imageController.isloading
// ? const CupertinoActivityIndicator()
// : Container(
// width: 100,
// height: 100,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// image: DecorationImage(
// fit: BoxFit.cover,
// image: NetworkImage(
// '${AppLink.seferCairoServer}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg',
// ),
// ),
// ),
// ),
// Positioned(
// right: 0,
// top: 0,
// child: CupertinoButton(
// onPressed: () {
// imageController.choosImagePicture(
// AppLink.uploadImage1, 'portrait');
// },
// child: const Icon(CupertinoIcons.pencil_circle_fill,
// color: CupertinoColors.white),
// ),
// ),
// ],
// );
// },
// ),
// const SizedBox(height: 10),
// Text(
// '${box.read(BoxName.nameDriver).toString().split(' ')[0]} ${box.read(BoxName.nameDriver).toString().split(' ')[1]}',
// style: const TextStyle(
// color: CupertinoColors.white,
// fontSize: 18,
// fontWeight: FontWeight.bold),
// ),
// const SizedBox(height: 5),
// Text(
// box.read(BoxName.emailDriver),
// style: const TextStyle(color: CupertinoColors.white, fontSize: 14),
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Text(
// Get.find<HomeCaptainController>().rating.toString(),
// style: const TextStyle(
// color: CupertinoColors.systemYellow, fontSize: 16),
// ),
// const SizedBox(width: 5),
// // You might want to replace this with a Cupertino-style rating widget
// // For now, we'll use text to represent stars
// // const Text('★★★★★',
// // style: TextStyle(color: CupertinoColors.systemYellow)),
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
// color: AppColor.greenColor,
// child: RatingBar.builder(
// initialRating: double.parse(
// Get.find<HomeCaptainController>().rating.toString()),
// minRating: 1,
// direction: Axis.horizontal,
// itemCount: 5,
// itemSize: 20,
// itemPadding: const EdgeInsets.symmetric(horizontal: 2),
// itemBuilder: (context, _) => const Icon(
// Icons.star,
// color: Colors.amber,
// ),
// onRatingUpdate: (rating) {},
// ),
// ),
// ],
// ),
// ],
// ),
// );
// }
// }
class CupertinoDrawerCaptain extends StatelessWidget {
final ImageController imageController = Get.put(ImageController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
backgroundColor: CupertinoColors.systemGroupedBackground,
navigationBar: CupertinoNavigationBar(
middle: Text('Menu'.tr,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
backgroundColor: Colors.transparent,
border: null,
),
child: SafeArea(
child: CustomScrollView(
slivers: [
const SliverToBoxAdapter(child: UserAccountHeader()),
SliverList(
delegate: SliverChildListDelegate([
const SizedBox(height: 10),
_buildSectionHeader('Account'.tr),
_buildDrawerItem(
icon: CupertinoIcons.money_dollar_circle_fill,
text: 'Wallet'.tr,
onTap: () => Get.to(() => WalletCaptainRefactored()),
),
_buildDrawerItem(
icon: CupertinoIcons.person_fill,
text: 'Profile'.tr,
onTap: () => Get.to(() => ProfileCaptain()),
),
_buildSectionHeader('Activities'.tr),
_buildDrawerItem(
icon: CupertinoIcons.clock_fill,
text: 'History of Trip'.tr,
onTap: () => Get.to(() => const HistoryCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.car_fill,
text: 'Available for rides'.tr,
onTap: () => Get.to(() => const AvailableRidesPage()),
),
_buildSectionHeader('Support'.tr),
_buildDrawerItem(
icon: CupertinoIcons.bell_fill,
text: 'Notifications'.tr,
onTap: () => Get.to(() => const NotificationCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.question_circle_fill,
text: 'Helping Center'.tr,
onTap: () => Get.to(() => HelpCaptain()),
),
_buildSectionHeader('More'.tr),
...moreMenuItems(),
]),
),
],
),
),
);
}
List<Widget> moreMenuItems() => [
_buildDrawerItem(
icon: CupertinoIcons.share_up,
text: 'Share App'.tr,
onTap: () => Get.to(() => InviteScreen()),
),
_buildDrawerItem(
icon: CupertinoIcons.wrench_fill,
text: "Maintenance Center".tr,
onTap: () => Get.to(() => MaintainCenterPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.heart_fill,
text: "Health Insurance".tr,
onTap: () => Get.to(() => AssuranceHealthPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.mail_solid,
text: "Contact Us".tr,
onTap: () => Get.to(() => ContactUsPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.play_circle_fill,
text: 'Videos Tutorials'.tr,
onTap: () => Get.to(() => VideoListPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.star_fill,
text: "Rate Our App".tr,
onTap: () => Get.to(() => RatingScreen()),
),
_buildDrawerItem(
icon: CupertinoIcons.settings_solid,
text: 'Settings'.tr,
onTap: () => Get.to(() => const SettingsCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.square_arrow_right,
text: 'Sign Out'.tr,
onTap: () => Get.to(() => const LogoutCaptain()),
isDestructive: true,
),
];
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: const TextStyle(
color: CupertinoColors.systemGrey,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildDrawerItem({
required IconData icon,
required String text,
required VoidCallback onTap,
bool isDestructive = false,
}) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: CupertinoColors.white,
borderRadius: BorderRadius.circular(10),
),
child: CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onPressed: onTap,
child: Row(
children: [
Icon(
icon,
color: isDestructive
? CupertinoColors.destructiveRed
: CupertinoColors.activeBlue,
size: 22,
),
const SizedBox(width: 12),
Text(
text,
style: AppStyle.title,
// TextStyle(
// color: isDestructive
// ? CupertinoColors.destructiveRed
// : CupertinoColors.label,
// fontSize: 16,
// ),
),
const Spacer(),
Icon(
CupertinoIcons.chevron_right,
color: CupertinoColors.systemGrey3,
size: 18,
),
],
),
),
);
}
}
class UserAccountHeader extends StatelessWidget {
const UserAccountHeader({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColor.blueColor, Color(0xFF2196F3)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GetBuilder<ImageController>(
builder: (imageController) {
return Stack(
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
spreadRadius: 2,
),
],
),
child: imageController.isloading
? const CupertinoActivityIndicator()
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
'${AppLink.seferCairoServer}/portrate_captain_image/${(box.read(BoxName.driverID))}.jpg',
fit: BoxFit.cover,
),
),
),
Positioned(
right: 0,
bottom: 0,
child: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () {
imageController.choosImagePicture(
AppLink.uploadImagePortrate, 'portrait');
},
child: Container(
padding: const EdgeInsets.all(6),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Icon(
CupertinoIcons.camera_fill,
color: AppColor.blueColor,
size: 20,
),
),
),
),
],
);
},
),
const SizedBox(height: 16),
Text(
box.read(BoxName.nameDriver).toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
color: Colors.black26,
blurRadius: 2,
offset: Offset(0, 1),
),
],
),
),
const SizedBox(height: 4),
Text(
box.read(BoxName.emailDriver),
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// const Icon(
// CupertinoIcons.star_fill,
// color: CupertinoColors.systemYellow,
// size: 18,
// ),
// const SizedBox(width: 4),
Text(
Get.find<HomeCaptainController>().rating.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 8),
RatingBar.builder(
initialRating: double.parse(
Get.find<HomeCaptainController>().rating.toString()),
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 16,
ignoreGestures: true,
itemBuilder: (context, _) => const Icon(
CupertinoIcons.star_fill,
color: CupertinoColors.systemYellow,
),
onRatingUpdate: (rating) {},
),
],
),
),
],
),
);
}
}

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,333 @@
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,
// ),
// ],
// ),
// ),
// ),
// );
// },
// ),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// );
// }
// }

View File

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

View File

@@ -0,0 +1,750 @@
import 'dart:io';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:sefer_driver/views/notification/available_rides_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.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 'package:bubble_head/bubble.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/info.dart';
import '../../../../constant/style.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 '../../../../print.dart';
import '../../../widgets/circle_container.dart';
import '../driver_map_page.dart';
import 'widget/connect.dart';
import 'widget/left_menu_map_captain.dart';
import '../../../../main.dart';
// =================================================================
// STEP 1: Modify your LocationController
// =================================================================
/*
In your `location_controller.dart` file, change `myLocation` and `heading`
to be observable by adding `.obs`. This will allow other parts of your app,
like the map, to automatically react to changes.
// BEFORE:
// LatLng myLocation = LatLng(....);
// double heading = 0.0;
// AFTER:
final myLocation = const LatLng(30.0444, 31.2357).obs; // Default to Cairo or a sensible default
final heading = 0.0.obs;
// When you update these values elsewhere in your controller,
// make sure to update their `.value` property.
// e.g., myLocation.value = newLatLng;
// e.g., heading.value = newHeading;
*/
// =================================================================
// STEP 2: Modify your HomeCaptainController
// =================================================================
/*
In your `home_captain_controller.dart` file, you need to add logic to
listen for changes from the LocationController and animate the camera.
class HomeCaptainController extends GetxController {
// ... your existing variables (mapController, carIcon, etc.)
// Make sure you have a reference to the GoogleMapController
GoogleMapController? mapHomeCaptainController;
@override
void onInit() {
super.onInit();
_setupLocationListener();
}
void onMapCreated(GoogleMapController controller) {
mapHomeCaptainController = controller;
// Any other map setup logic
}
// THIS IS THE NEW LOGIC TO ADD
void _setupLocationListener() {
final locationController = Get.find<LocationController>();
// The 'ever' worker from GetX listens for changes to an observable variable.
// Whenever `heading` or `myLocation` changes, it will call our method.
ever(locationController.heading, (_) => _updateCameraPosition());
ever(locationController.myLocation, (_) => _updateCameraPosition());
}
void _updateCameraPosition() {
final locationController = Get.find<LocationController>();
if (mapHomeCaptainController != null) {
final newPosition = CameraPosition(
target: locationController.myLocation.value,
zoom: 17.5, // A bit closer for a navigation feel
tilt: 50.0, // A nice 3D perspective
bearing: locationController.heading.value, // This rotates the map
);
// Animate the camera smoothly to the new position and rotation
mapHomeCaptainController!.animateCamera(
CameraUpdate.newCameraPosition(newPosition),
);
}
}
// ... rest of your controller code
}
*/
// =================================================================
// STEP 3: Update the HomeCaptain Widget
// =================================================================
class HomeCaptain extends StatelessWidget {
HomeCaptain({super.key});
final LocationController locationController = Get.put(LocationController());
final HomeCaptainController homeCaptainController =
Get.put(HomeCaptainController());
@override
Widget build(BuildContext context) {
Get.put(HomeCaptainController());
WidgetsBinding.instance.addPostFrameCallback((_) async {
closeOverlayIfFound();
checkForUpdate(context);
getPermissionOverlay();
showDriverGiftClaim(context);
});
return Scaffold(
appBar: AppBar(
elevation: 2,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.grey.shade50],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
),
],
),
),
title: Row(
children: [
Image.asset(
'assets/images/logo.gif',
height: 32,
width: 35,
),
const SizedBox(width: 8),
Text(
AppInformation.appName.split(' ')[0].toString().tr,
style: AppStyle.title.copyWith(
fontSize: 22,
fontWeight: FontWeight.w600,
color: AppColor.blueColor,
),
),
],
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: MyCircleContainer(
child: Text(
homeCaptainController.countRefuse.toString(),
style: AppStyle.title,
),
),
),
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(
icon: Icons.satellite_alt,
tooltip: 'Change Map Type'.tr,
onPressed: homeCaptainController.changeMapType,
),
_MapControlButton(
icon: Icons.streetview_sharp,
tooltip: 'Toggle Traffic'.tr,
onPressed: homeCaptainController.changeMapTraffic,
),
_MapControlButton(
icon: Icons.my_location, // Changed for clarity
tooltip: 'Center on Me'.tr,
onPressed: () {
// This button now just re-centers without changing rotation
if (homeCaptainController.mapHomeCaptainController !=
null) {
homeCaptainController.mapHomeCaptainController!
.animateCamera(CameraUpdate.newLatLngZoom(
Get.find<LocationController>().myLocation,
17.5,
));
}
},
),
],
),
),
const SizedBox(width: 8),
],
),
drawer: CupertinoDrawerCaptain(),
body: Stack(
children: [
// FIX: Replaced nested GetBuilder/Obx with a single GetX widget.
// GetX handles both observable (.obs) variables and standard controller updates.
GetBuilder<HomeCaptainController>(builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: GoogleMap(
onMapCreated: controller.onMapCreated,
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
initialCameraPosition: CameraPosition(
// Use .value to get the latest location from the reactive variable
target: locationController.myLocation,
zoom: 15,
),
onCameraMove: (position) {
CameraPosition(
target: locationController.myLocation,
zoom: 17.5, // A bit closer for a navigation feel
tilt: 50.0, // A nice 3D perspective
bearing:
locationController.heading, // This rotates the map
);
},
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
// Use .value for position and rotation from the reactive variable
position: locationController.myLocation,
rotation: locationController.heading,
// IMPORTANT: These two properties make the marker look
// correct when the map is tilted and rotating.
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
)
},
mapType: controller.mapType
? MapType.satellite
: MapType.terrain,
myLocationButtonEnabled: false, // Disable default button
myLocationEnabled: false, // We use our custom marker
trafficEnabled: controller.mapTrafficON,
buildingsEnabled: true,
mapToolbarEnabled: true,
zoomControlsEnabled: false, // Cleaner UI for navigation
);
}),
// The rest of your UI remains the same...
Positioned(
bottom: 10,
right: Get.width * .1,
left: Get.width * .1,
child: const ConnectWidget()),
Positioned(
top: 5,
right: Get.width * .05,
left: Get.width * .05,
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) {
return Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.white, Colors.white70],
),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
width: Get.width * .8,
height: 120,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.greenColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(
Entypo.wallet,
color: AppColor.greenColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'${"Today".tr}: ${(homeCaptainController.totalMoneyToday)}',
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.yellowColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(
Entypo.wallet,
color: AppColor.yellowColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'${AppInformation.appName}: ${(homeCaptainController.totalMoneyInSEFER)}',
style: AppStyle.title.copyWith(
color: AppColor.yellowColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Total Points is'.tr}: ${(homeCaptainController.totalPoints)}',
style: AppStyle.title.copyWith(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: int.parse((homeCaptainController
.countRideToday)) <
5
? AppColor.accentColor
: int.parse((homeCaptainController
.countRideToday)) >
5 &&
int.parse((homeCaptainController
.countRideToday)) <
10
? AppColor.yellowColor
: AppColor.greenColor,
),
child: Row(
children: [
const Icon(
Icons.directions_car_rounded,
color: Colors.white,
size: 18,
),
const SizedBox(width: 4),
Text(
'${"Ride Today : ".tr}: ${(homeCaptainController.countRideToday)}',
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
],
),
);
},
),
),
Positioned(
bottom: 65,
right: Get.width * .1,
left: Get.width * .1,
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) => Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.timer_outlined,
color: AppColor.greenColor),
const SizedBox(width: 8),
Text(
'Active Duration:'.tr,
style: AppStyle.title,
),
const SizedBox(width: 4),
Text(
(homeCaptainController.stringActiveDuration),
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.greenColor,
),
),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.access_time,
color: AppColor.accentColor),
const SizedBox(width: 8),
Text(
'Total Connection Duration:'.tr,
style: AppStyle.title,
),
const SizedBox(width: 4),
Text(
(homeCaptainController.totalDurationToday),
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.accentColor,
),
),
],
),
],
),
),
),
),
Positioned(
bottom: Get.height * .2,
right: 6,
child: 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,
),
GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) {
return box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Begin'
? Positioned(
bottom: Get.height * .2,
right: 6,
child: 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: GestureDetector(
onLongPress: () {
box.write(BoxName.rideStatus, 'delete');
homeCaptainController.update();
},
child: IconButton(
onPressed: () {
box.read(BoxName.rideStatus) == 'Applied'
? {
Get.to(
() => PassengerLocationMapPage(),
arguments: box
.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.changeRideToBeginToPassenger()
}
: {
Get.to(
() => PassengerLocationMapPage(),
arguments: box
.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.startRideFromStartApp()
};
},
icon: const Icon(
Icons.directions_rounded,
size: 29,
color: AppColor.blueColor,
),
),
),
),
)
: const SizedBox();
})
],
),
),
leftMainMenuCaptainIcons(),
],
),
);
}
}
// These helper widgets and functions remain unchanged
showFirstTimeOfferNotification(BuildContext context) async {
bool isFirstTime = _checkIfFirstTime();
if (isFirstTime) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: Offset(0.0, 10.0),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Welcome Offer!'.tr,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 15),
Text(
'As a new driver, you\'re eligible for a special offer!'.tr,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
Stack(
children: <Widget>[
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(15),
),
child: Text(
'300 LE'.tr,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
right: -10,
top: -10,
child: Container(
padding: const EdgeInsets.all(5),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Icon(
Icons.attach_money,
color: Colors.white,
size: 20,
),
),
),
],
),
const SizedBox(height: 20),
Text(
'for your first registration!'.tr,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Text(
"Get it Now!".tr,
style:
const TextStyle(fontSize: 18, color: Colors.white),
),
),
onPressed: () {
_markAsNotFirstTime();
Navigator.of(context).pop();
},
),
],
),
),
);
},
);
});
}
}
bool _checkIfFirstTime() {
if (box.read(BoxName.isFirstTime).toString() == '') {
return true;
} else {
return false;
}
}
void _markAsNotFirstTime() {
box.write(BoxName.isFirstTime, 'false');
}
class _MapControlButton extends StatelessWidget {
final IconData icon;
final VoidCallback onPressed;
final String tooltip;
const _MapControlButton({
required this.icon,
required this.onPressed,
required this.tooltip,
});
@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,
),
),
),
),
);
}
}

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,141 @@
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());
captainWalletController.getCaptainWalletFromBuyPoints();
return Center(
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) => double.parse(
(captainWalletController.totalPoints)) <
-300
? CupertinoButton(
onPressed: () {
Get.defaultDialog(
// backgroundColor: CupertinoColors.destructiveRed,
barrierDismissible: false,
title: double.parse(
(captainWalletController.totalPoints)) <
-300
? '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)) <
-300
? 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)) <
-300
? '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)) <
-300
? 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,
),
),
],
),
),
)),
);
}
}

View File

@@ -0,0 +1,308 @@
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/login_captin.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/home/my_wallet/points_captain.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import '../../../../../constant/colors.dart';
import '../../../../../constant/links.dart';
import '../../../../../controller/auth/google_sign.dart';
import '../../../../../controller/firebase/firbase_messge.dart';
import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../Rate/ride_calculate_driver.dart';
import '../../../../auth/captin/otp_page.dart';
import '../../../my_wallet/ecash.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();
box.read(BoxName.rideArgumentsFromBackground) != '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(
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}',
// '');
NotificationController1().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 {
// Get.to(PhoneNumberScreen());
// // payWithEcashDriver(context, '2000');
// // payWithMTNWallet(context, '1', 'SYP');
// // firebaseMessagesController.sendNotificationToDriverMAP(
// // 'title',
// // DateTime.now().toString(),
// // 'ffX7xVXpdE_Xq8JBH3lgS4:APA91bGBHp53E-ZuXdlLBpRZohzqR9sazqcn3pwpEDG7JxkVi9MBtFDlCipzLpPCvD6LHEtds88ugGyCty7pEJVyx6tQYvzHVDCh7l3_7axpyriTBs5iv9E',
// // [],
// // '');
// // box.write(BoxName.statusDriverLocation, 'off');
// },
// icon: const Icon(
// FontAwesome5.grin_tears,
// size: 29,
// color: AppColor.blueColor,
// ),
// );
// }),
// ),f
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},
);
// 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);
} 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;
final FirebaseMessagesController _firebaseMessagesController =
Get.put(FirebaseMessagesController());
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) {
_firebaseMessagesController.sendNotificationToDriverMAP('Accepted Ride',
'your ride is applied'.tr, token, bodyToPassenger, 'start.wav');
} else {}
} catch (e) {}
}

View File

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

View File

@@ -0,0 +1,271 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/captin/help/maintain_center_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
// class MaintainCenterPage extends StatelessWidget {
// MaintainCenterPage({super.key});
// MaintainCenterController maintainCenterController =
// Get.put(MaintainCenterController());
// @override
// Widget build(BuildContext context) {
// return MyScafolld(
// title: "Maintenance Center".tr,
// body: [
// GetBuilder<MaintainCenterController>(
// builder: (maintainCenterController) {
// return Padding(
// padding: const EdgeInsets.all(8.0),
// child: Column(
// children: [
// Text(
// "When you complete 600 trips, you will be eligible to receive offers for maintenance of your car."
// .tr),
// const SizedBox(
// height: 10,
// ),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// MyElevatedButton(
// title: "Show My Trip Count".tr,
// onPressed: () async {
// maintainCenterController.getTripCountByCaptain();
// }),
// _buildPriceAvatar(
// maintainCenterController.tripCount['count'] == null
// ? '0'
// : maintainCenterController.tripCount['count']
// .toString())
// ],
// ),
// const SizedBox(
// height: 10,
// ),
// Container(
// decoration: AppStyle.boxDecoration,
// child: Padding(
// padding: const EdgeInsets.all(14),
// child: Text(
// "We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family."
// .tr,
// style: AppStyle.title,
// ),
// ),
// ),
// const SizedBox(
// height: 10,
// ),
// MyElevatedButton(
// title: 'Show maintenance center near my location'.tr,
// onPressed: () {
// if (maintainCenterController.tripCount['count'] > 600) {
// } else {
// Get.snackbar("You should complete 600 trips".tr, '',
// backgroundColor: AppColor.yellowColor);
// }
// })
// ],
// ),
// );
// })
// ],
// isleading: true);
// }
// Widget _buildPriceAvatar(String count) {
// return Container(
// width: 80,
// height: 80,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// gradient: const RadialGradient(
// colors: [Color(0xFF4CAF50), Color(0xFF2E7D32)],
// center: Alignment.center,
// radius: 0.8,
// ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.2),
// blurRadius: 8,
// offset: const Offset(0, 4),
// ),
// ],
// ),
// child: Center(
// child: Text(
// count,
// style: const TextStyle(
// fontSize: 22,
// fontWeight: FontWeight.bold,
// color: Colors.white,
// ),
// ),
// ),
// );
// }
// }
class MaintainCenterPage extends StatelessWidget {
MaintainCenterPage({super.key});
final MaintainCenterController maintainCenterController =
Get.put(MaintainCenterController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: "Maintenance Center".tr,
body: [
GetBuilder<MaintainCenterController>(
builder: (controller) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Introduction Text with better styling
Text(
"When you complete 600 trips, you will be eligible to receive offers for maintenance of your car."
.tr,
style: Theme.of(context).textTheme.titleMedium!.copyWith(
color: Colors.grey[700],
height: 1.4,
),
),
const SizedBox(height: 20),
// Trip Count Section in a Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: MyElevatedButton(
title: "Show My Trip Count".tr,
onPressed: () async {
controller.getTripCountByCaptain();
},
),
),
const SizedBox(width: 16),
_buildPriceAvatar(
controller.tripCount['count'] == null
? '0'
: controller.tripCount['count'].toString(),
),
],
),
),
),
const SizedBox(height: 20),
// Maintenance Offer Information in a Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Maintenance Offer".tr,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Text(
"We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our Tripz app and be part of our Tripz family."
.tr,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Colors.grey[800],
height: 1.5,
),
),
],
),
),
),
const SizedBox(height: 20),
// Show Maintenance Center Button
SizedBox(
width: double.infinity,
child: MyElevatedButton(
title: 'Show maintenance center near my location'.tr,
onPressed: () {
if (controller.tripCount['count'] != null &&
controller.tripCount['count'] >= 600) {
// Implement navigation or action to show maintenance centers
// For now, let's print a message
print("Navigating to maintenance centers...");
} else {
Get.snackbar(
"Ineligible for Offer".tr,
"You should complete 500 trips to unlock this feature."
.tr,
backgroundColor: AppColor.yellowColor,
colorText: Colors.black,
snackPosition: SnackPosition.BOTTOM,
);
}
},
),
),
],
),
);
},
),
],
isleading: true,
);
}
Widget _buildPriceAvatar(String count) {
return Container(
width: 70, // Slightly reduced size
height: 70,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
// Changed to LinearGradient
colors: [Color(0xFF4CAF50), Color(0xFF2E7D32)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15), // Reduced opacity
blurRadius: 6, // Slightly reduced blur
offset: const Offset(0, 3),
),
],
),
child: Center(
child: Text(
count,
style: const TextStyle(
fontSize: 20, // Slightly reduced size
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
);
}
}

View File

@@ -0,0 +1,307 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:slide_to_act/slide_to_act.dart';
import 'package:vibration/vibration.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../widgets/elevated_btn.dart';
GetBuilder<MapDriverController> driverEndRideBar() {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 5,
top: 5,
right: 5,
child: Container(
decoration: AppStyle.boxDecoration1.copyWith(
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
padding: const EdgeInsets.all(10),
height: mapDriverController.remainingTimeTimerRideBegin < 60
? mapDriverController.driverEndPage = 190
: mapDriverController.carType == 'Mishwar Vip'
? 120
: 170,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
if (mapDriverController.carType != 'Mishwar Vip')
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildInfoColumn(
icon: Icons.social_distance,
text: '${mapDriverController.distance} ${'KM'.tr}',
),
_buildInfoColumn(
icon: Icons.timelapse,
text: mapDriverController.hours > 1
? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
: '${mapDriverController.minutes} ${'m'.tr}',
),
_buildInfoColumn(
icon: Icons.money_sharp,
text:
'${mapDriverController.paymentAmount} ${'\$'.tr}',
),
],
),
if (mapDriverController.carType != 'Speed' &&
mapDriverController.carType != 'Awfar Car' &&
mapDriverController.carType != 'Scooter')
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoBox(
icon: Icons.timer,
text:
mapDriverController.stringRemainingTimeRideBegin1,
),
_buildInfoBox(
icon: Icons.location_on,
text:
'${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
),
_buildInfoBox(
icon: Icons.attach_money,
text: mapDriverController.price.toStringAsFixed(2),
),
],
),
_builtTimerAndCarType(),
Container(
width: Get.width * 0.8,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppColor.redColor.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: SlideAction(
height: 50,
borderRadius: 15,
elevation: 4,
text: 'Slide to End Trip'.tr,
textStyle: AppStyle.title.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
outerColor: AppColor.redColor,
innerColor: Colors.white,
sliderButtonIcon: const Icon(
Icons.arrow_forward_ios,
color: AppColor.redColor,
size: 24,
),
sliderRotate: false,
onSubmit: () {
HapticFeedback.mediumImpact();
mapDriverController.finishRideFromDriver();
},
),
)
],
),
),
)
: const SizedBox(),
);
}
class _builtTimerAndCarType extends StatelessWidget {
const _builtTimerAndCarType({
super.key,
});
@override
Widget build(BuildContext context) {
final mapDriverController = Get.find<MapDriverController>();
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
decoration: AppStyle.boxDecoration1.copyWith(
boxShadow: [
BoxShadow(
color: AppColor.accentColor.withOpacity(0.2),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
mapDriverController.carType,
style: AppStyle.title,
),
),
if (mapDriverController.carType != 'Comfort' &&
mapDriverController.carType != 'Mishwar Vip' &&
mapDriverController.carType != 'Lady')
Container(
width: Get.width * 0.6,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
mapDriverController.remainingTimeTimerRideBegin < 60
? AppColor.redColor.withOpacity(0.8)
: AppColor.greenColor.withOpacity(0.8),
mapDriverController.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor,
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
boxShadow: [
BoxShadow(
color: (mapDriverController.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor)
.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
children: [
LinearProgressIndicator(
backgroundColor: Colors.white.withOpacity(0.2),
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white.withOpacity(0.5),
),
minHeight: 40,
value:
mapDriverController.progressTimerRideBegin.toDouble(),
),
Center(
child: AnimatedDefaultTextStyle(
duration: Duration(milliseconds: 300),
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize:
mapDriverController.remainingTimeTimerRideBegin < 60
? 18
: 16,
shadows: [
Shadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 4,
),
],
),
child: Text(
mapDriverController.stringRemainingTimeRideBegin,
),
),
),
],
),
),
),
],
);
}
}
Widget _buildInfoColumn({required IconData icon, required String text}) {
return Column(
children: [
Icon(icon),
Text(
text,
style: AppStyle.title,
),
],
);
}
Widget _buildInfoBox({required IconData icon, required String text}) {
return Container(
width: Get.width * .2,
decoration: AppStyle.boxDecoration1,
padding: const EdgeInsets.all(4),
child: Row(
children: [
Icon(icon),
SizedBox(width: 4),
Text(
text,
style: AppStyle.number,
),
],
),
);
}
GetBuilder<MapDriverController> speedCircle() {
if (Get.find<MapDriverController>().speed > 100) {
if (Platform.isIOS) {
HapticFeedback.selectionClick();
} else {
Vibration.vibrate(duration: 1000);
}
Get.defaultDialog(
barrierDismissible: false,
titleStyle: AppStyle.title,
title: 'Speed Over'.tr,
middleText: 'Please slow down'.tr,
middleTextStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'I will slow down'.tr,
onPressed: () => Get.back(),
),
);
}
return GetBuilder<MapDriverController>(
builder: (mapDriverController) {
return mapDriverController.isRideStarted
? Positioned(
bottom: 25,
right: 100,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: mapDriverController.speed > 100
? Colors.red
: AppColor.secondaryColor,
border: Border.all(width: 3, color: AppColor.redColor),
),
height: 60,
width: 60,
child: Center(
child: Text(
mapDriverController.speed.toStringAsFixed(0),
style: AppStyle.number,
),
),
),
)
: const SizedBox();
},
);
}

View File

@@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../../controller/functions/location_controller.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
class GoogleDriverMap extends StatelessWidget {
const GoogleDriverMap({
super.key,
required this.locationController,
});
final LocationController locationController;
@override
Widget build(BuildContext context) {
Get.put(MapDriverController());
return Padding(
padding: const EdgeInsets.all(8.0),
child: GetBuilder<MapDriverController>(
builder: (controller) => Column(
children: [
SizedBox(
height: Get.height * .92,
child: GoogleMap(
onMapCreated: controller.onMapCreated,
zoomControlsEnabled: true,
initialCameraPosition: CameraPosition(
target: locationController.myLocation,
zoom: 13,
bearing: locationController.heading,
tilt: 40,
),
cameraTargetBounds:
CameraTargetBounds.unbounded, // Allow unrestricted movement
onCameraMove: (position) {
CameraPosition(
target: locationController.myLocation,
zoom: 13,
bearing: locationController.heading,
tilt: 40,
);
//todo
// locationController.myLocation = position.target;
//
// controller.mapController
// ?.animateCamera(CameraUpdate.newCameraPosition(position));
},
minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
myLocationEnabled: true,
myLocationButtonEnabled: true,
compassEnabled: true,
mapType: MapType.terrain,
rotateGesturesEnabled: true,
scrollGesturesEnabled: true,
trafficEnabled: false,
buildingsEnabled: true,
mapToolbarEnabled: true,
fortyFiveDegreeImageryEnabled: true,
zoomGesturesEnabled: true,
polylines: {
Polyline(
zIndex: 2,
geodesic: true,
polylineId: const PolylineId('route1'),
points: controller.polylineCoordinates,
color: const Color.fromARGB(255, 163, 81, 246),
width: 5,
),
Polyline(
zIndex: 2,
geodesic: true,
polylineId: const PolylineId('route'),
points: controller.polylineCoordinatesDestination,
color: const Color.fromARGB(255, 10, 29, 126),
width: 5,
),
},
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: locationController.myLocation,
draggable: true,
icon: controller.carIcon,
rotation: locationController.heading,
),
Marker(
markerId: MarkerId('start'.tr),
position: controller.latLngPassengerLocation,
draggable: true,
icon: controller.startIcon,
),
Marker(
markerId: MarkerId('end'.tr),
position: controller.latLngPassengerDestination,
draggable: true,
icon: controller.endIcon,
),
},
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
class GoogleMapApp extends StatelessWidget {
const GoogleMapApp({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 150,
bottom: 20,
child: Container(
decoration: AppStyle.boxDecoration,
child: IconButton(
onPressed: () async {
var startLat = Get.find<MapDriverController>()
.latLngPassengerLocation
.latitude;
var startLng = Get.find<MapDriverController>()
.latLngPassengerLocation
.longitude;
var endLat = Get.find<MapDriverController>()
.latLngPassengerDestination
.latitude;
var endLng = Get.find<MapDriverController>()
.latLngPassengerDestination
.longitude;
String url =
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
throw 'Could not launch google maps';
}
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
size: 45,
color: AppColor.blueColor,
),
)),
)
: const SizedBox());
}
}

View File

@@ -0,0 +1,407 @@
import 'dart:io';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/info.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../main.dart';
import '../../../../print.dart';
class PassengerInfoWindow extends StatelessWidget {
const PassengerInfoWindow({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (controller) => controller.isPassengerInfoWindow == true
? Positioned(
bottom: 10,
left: 10,
right: 10,
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Go to passenger Location'.tr,
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold,
),
),
if (!controller.isRideBegin)
Wrap(
spacing: 16.0,
children: [
IconButton(
onPressed: () async {
controller.isSocialPressed = true;
await controller.driverCallPassenger();
makePhoneCall(
controller.passengerPhone.toString());
},
icon: const Icon(
Icons.phone,
color: AppColor.blueColor,
),
tooltip: 'Call Passenger',
),
IconButton(
onPressed: () {
Get.bottomSheet(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20)),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: _buildMessageOptions(controller),
),
);
},
icon: const Icon(
Icons.message,
color: AppColor.redColor,
),
tooltip: 'Send Message',
),
IconButton(
onPressed: () async {
if (Platform.isAndroid) {
Bubble().startBubbleHead(
sendAppToBackground: true);
}
await controller
.openGoogleMapFromDriverToPassenger();
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
size: 28,
color: AppColor.blueColor,
),
tooltip: 'Open in Maps',
),
],
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.timer,
text: controller.hours > 1
? '${controller.hours}h ${controller.minutes}m'
: '${controller.minutes}m',
label: 'Duration',
),
_buildInfoTile(
icon: Icons.map,
text: '${controller.distance} km',
label: 'Distance',
),
_buildInfoTile(
icon: Icons.person,
text: controller.passengerName,
label: 'Passenger',
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.attach_money,
text: controller.totalPricePassenger,
label: 'Cost',
),
_buildInfoTile(
icon: Icons.directions_car,
text: controller.carType.tr,
label: 'Car Type',
),
],
),
const SizedBox(height: 16),
if (!controller.isRideBegin)
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: MyElevatedButton(
title: 'Start the Ride'.tr,
kolor: AppColor.greenColor,
onPressed: () {
MyDialog().getDialog(
"Is the Passenger in your Car?".tr,
"Don't start trip if passenger not in your car"
.tr,
() async {
await controller
.startRideFromDriver();
Get.back();
},
);
},
),
),
const SizedBox(width: 8),
if (controller.isArrivedSend)
Expanded(
child: MyElevatedButton(
title: 'I Arrive'.tr,
kolor: AppColor.yellowColor,
onPressed: () async {
if (await controller
.calculateDistanceBetweenDriverAndPassengerLocation() <
140) {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'Hi ,I Arrive your site',
'I Arrive at your site'.tr,
controller.tokenPassenger,
[],
'ding.wav',
);
controller
.startTimerToShowDriverWaitPassengerDuration();
controller.isArrivedSend = false;
} else {
MyDialog().getDialog(
'You are not near the passenger location'
.tr,
'Please go to the pickup location exactly'
.tr, () {
Get.back();
});
}
},
),
),
],
),
const SizedBox(height: 12),
if (controller.remainingTimeInPassengerLocatioWait <
300 &&
controller
.remainingTimeInPassengerLocatioWait !=
0)
Stack(
alignment: Alignment.center,
children: [
LinearProgressIndicator(
backgroundColor: AppColor.greyColor,
color: controller
.remainingTimeInPassengerLocatioWait <
60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 20,
borderRadius: BorderRadius.circular(10),
value: controller
.progressInPassengerLocationFromDriver
.toDouble(),
),
Text(
controller
.stringRemainingTimeWaitingPassenger,
style: AppStyle.title,
),
],
),
const SizedBox(height: 12),
if (controller.isdriverWaitTimeEnd)
MyElevatedButton(
title:
'You Can Cancel the Trip and get Cost From '
.tr +
AppInformation.appName.tr,
kolor: AppColor.deepPurpleAccent,
onPressed: () {
MyDialog().getDialog(
'Are you sure to cancel?'.tr, '',
() async {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'Driver Cancelled Your Trip',
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
.tr,
controller.tokenPassenger,
[],
'cancel.wav',
);
Log.print(
'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}');
box.write(BoxName.rideStatus, 'Cancel');
await controller
.addWaitingTimeCostFromPassengerToDriverWallet();
controller.isdriverWaitTimeEnd = false;
Get.back();
});
},
),
],
),
],
),
),
),
)
: const SizedBox(),
);
}
Widget _buildInfoTile({
required IconData icon,
required String text,
required String label,
}) {
return Column(
children: [
Icon(icon, color: Colors.grey[700]),
const SizedBox(height: 4),
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Text(label.tr, style: AppStyle.title),
],
);
}
Widget _buildMessageOptions(MapDriverController controller) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Select a quick message'.tr, style: AppStyle.title),
const SizedBox(height: 16),
_buildMessageTile(
text: "Where are you, sir?".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Where are you, sir?".tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text: "I've been trying to reach you but your phone is off.".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"I've been trying to reach you but your phone is off.".tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text:
"Please don't be late, I'm waiting for you at the specified location."
.tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Please don't be late, I'm waiting for you at the specified location."
.tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text: "Please don't be late".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Please don't be late".tr,
controller.tokenPassenger,
[],
'cancel.wav',
);
Get.back();
},
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Form(
key: controller.formKey2,
child: MyTextForm(
controller: controller.messageToPassenger,
label: 'Type something'.tr,
hint: 'Type something'.tr,
type: TextInputType.text,
),
),
),
IconButton(
onPressed: () {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'message From Driver',
controller.messageToPassenger.text,
controller.tokenPassenger,
[],
'ding.wav',
);
controller.messageToPassenger.clear();
Get.back();
},
icon: const Icon(Icons.send),
),
],
),
],
);
}
Widget _buildMessageTile(
{required String text, required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
),
child: Text(text, style: AppStyle.title),
),
);
}
}

View File

@@ -0,0 +1,193 @@
import 'dart:io';
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/info.dart';
import 'package:sefer_driver/controller/functions/location_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../../main.dart';
class SosConnect extends StatelessWidget {
const SosConnect({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 16,
bottom: 16,
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: SizedBox(
height: 60,
width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
onPressed: () {
_handleSosCall(mapDriverController);
},
icon: const Icon(
Icons.sos_sharp,
size: 32,
color: AppColor.redColor,
),
tooltip: 'SOS - Call Emergency',
),
VerticalDivider(
color: Colors.grey[300],
thickness: 1,
),
IconButton(
onPressed: () {
_handleWhatsApp(mapDriverController);
},
icon: const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
size: 32,
),
tooltip: 'SOS - Send WhatsApp Message',
),
VerticalDivider(
color: Colors.grey[300],
thickness: 1,
),
IconButton(
onPressed: () {
_handleGoogleMap(mapDriverController);
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
color: AppColor.primaryColor,
size: 32,
),
tooltip: 'Google Maps - Navigate',
),
],
),
),
),
)
: const SizedBox(),
);
}
void _handleSosCall(MapDriverController mapDriverController) {
if (box.read(BoxName.sosPhoneDriver) == null) {
Get.defaultDialog(
title: 'Insert Emergency Number'.tr,
content: Form(
key: mapDriverController.formKey1,
child: MyTextForm(
controller: mapDriverController.sosEmergincyNumberCotroller,
label: 'Emergency Number'.tr,
hint: 'Enter phone number'.tr,
type: TextInputType.phone,
),
),
confirm: MyElevatedButton(
title: 'Save'.tr,
onPressed: () {
if (mapDriverController.formKey1.currentState!.validate()) {
box.write(BoxName.sosPhoneDriver,
mapDriverController.sosEmergincyNumberCotroller.text);
Get.back(); // Close the dialog
launchCommunication(
'phone', box.read(BoxName.sosPhoneDriver), '');
}
},
),
);
} else {
launchCommunication('phone', box.read(BoxName.sosPhoneDriver), '');
}
}
void _handleWhatsApp(MapDriverController mapDriverController) {
if (box.read(BoxName.sosPhoneDriver) == null) {
Get.defaultDialog(
title: 'Insert Emergency Number'.tr,
content: Form(
key: mapDriverController.formKey1,
child: MyTextForm(
controller: mapDriverController.sosEmergincyNumberCotroller,
label: 'Emergency Number'.tr,
hint: 'Enter phone number'.tr,
type: TextInputType.phone,
),
),
confirm: MyElevatedButton(
title: 'Save'.tr,
onPressed: () {
if (mapDriverController.formKey1.currentState!.validate()) {
box.write(BoxName.sosPhoneDriver,
mapDriverController.sosEmergincyNumberCotroller.text);
Get.back(); // Close the dialog
_sendWhatsAppMessage(mapDriverController);
}
},
),
);
} else {
_sendWhatsAppMessage(mapDriverController);
}
}
void _handleGoogleMap(MapDriverController mapDriverController) {
() async {
if (Platform.isAndroid) {
Bubble().startBubbleHead(sendAppToBackground: true);
}
var startLat =
Get.find<MapDriverController>().latLngPassengerLocation.latitude;
var startLng =
Get.find<MapDriverController>().latLngPassengerLocation.longitude;
var endLat =
Get.find<MapDriverController>().latLngPassengerDestination.latitude;
var endLng =
Get.find<MapDriverController>().latLngPassengerDestination.longitude;
String url =
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
throw 'Could not launch google maps';
}
}();
}
void _sendWhatsAppMessage(MapDriverController mapDriverController) {
final sosNumber = box.read(BoxName.sosPhoneDriver);
if (sosNumber != null) {
launchCommunication(
'whatsapp',
'+2$sosNumber', // Consider international format
"${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
"${"My current location is:".tr} "
"https://www.google.com/maps/place/"
"${Get.find<LocationController>().myLocation.latitude},"
"${Get.find<LocationController>().myLocation.longitude} "
"${"\nI have a trip on".tr} ${AppInformation.appName} "
"${"app with passenger".tr} ${mapDriverController.passengerName}.",
);
}
}
}

View File

@@ -0,0 +1,25 @@
// import 'package:flutter/material.dart';
// import 'package:ride/constant/api_key.dart';
// import 'package:ride/constant/box_name.dart';
// import 'package:ride/main.dart';
// import 'package:zego_uikit_prebuilt_call/zego_uikit_prebuilt_call.dart';
// class CallPage extends StatelessWidget {
// const CallPage({Key? key, required this.callID}) : super(key: key);
// final String callID;
// @override
// Widget build(BuildContext context) {
// return ZegoUIKitPrebuiltCall(
// appID: AK
// .zegoCloudAppID, // Fill in the appID that you get from ZEGOCLOUD Admin Console.
// appSign: AK
// .zegoCloudAppSIGN, // Fill in the appSign that you get from ZEGOCLOUD Admin Console.
// userID: box.read(BoxName.passengerID) ?? box.read(BoxName.driverID),
// userName: box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
// callID: callID,
// // You can also use groupVideo/groupVoice/oneOnOneVoice to make more types of calls.
// config: ZegoUIKitPrebuiltCallConfig.oneOnOneVoiceCall(),
// );
// }
// }

View File

@@ -0,0 +1,811 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:sefer_driver/constant/api_key.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart';
import '../../../../controller/firebase/local_notification.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../main.dart';
import '../../../../models/model/order_data.dart';
import '../../../../print.dart';
// === Enhanced Colors for Better Readability ===
class AppColors {
static const primary = Color(0xFF1A252F);
static const card = Color(0xFF2C3E50);
static const white = Colors.white;
static const gray = Color(0xFFBDC3C7);
static const lightGray = Color(0xFFECF0F1);
static const accent = Color(0xFF00BCD4);
static const accept = Color(0xFF4CAF50);
static const reject = Color(0xFFFF5722);
static const highlight = Color(0xFFFFC107);
static const priceHighlight = Color(0xFF00E676);
static const urgentRed = Color(0xFFD32F2F);
}
class OrderOverlay extends StatefulWidget {
const OrderOverlay({Key? key}) : super(key: key);
@override
State<OrderOverlay> createState() => _OrderOverlayState();
}
class _OrderOverlayState extends State<OrderOverlay>
with WidgetsBindingObserver {
// === State Variables ===
OrderData? orderData;
Timer? timer;
int remainingSeconds = 10;
final AudioPlayer audioPlayer = AudioPlayer();
bool buttonsEnabled = true;
final String mapApiKey = AK.mapAPIKEY;
final CRUD _crud = CRUD();
final NotificationController notificationController =
Get.put(NotificationController());
// === Getters ===
bool get canShowMap {
if (orderData == null || mapApiKey.isEmpty) return false;
final start = orderData!.startCoordinates;
final end = orderData!.endCoordinates;
return start?['lat'] != null &&
start?['lng'] != null &&
end?['lat'] != null &&
end?['lng'] != null;
}
String get staticMapUrl {
if (!canShowMap) return "";
final start = orderData!.startCoordinates!;
final end = orderData!.endCoordinates!;
final startMarker = Uri.encodeComponent("${start['lat']},${start['lng']}");
final endMarker = Uri.encodeComponent("${end['lat']},${end['lng']}");
return "https://maps.googleapis.com/maps/api/staticmap?"
"size=600x150&maptype=roadmap"
"&markers=color:green%7Clabel:S%7C$startMarker"
"&markers=color:red%7Clabel:D%7C$endMarker"
"&path=color:0x007bff%7Cweight:5%7C$startMarker%7C$endMarker"
"&key=$mapApiKey";
}
// === Lifecycle ===
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
FlutterOverlayWindow.overlayListener.listen((event) {
if (mounted) _processEventData(event);
});
}
@override
void dispose() {
timer?.cancel();
_stopAudio();
audioPlayer.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_checkOverlayStatus();
}
}
List myList = [];
// === Setup & Listeners ===
void _setupOverlayListener() {
FlutterOverlayWindow.overlayListener.listen((event) {
if (mounted) _processEventData(event);
});
}
void _processEventData(dynamic event) {
_log("Received event: $event");
if (event is List<dynamic>) {
try {
myList = event;
final newOrder = OrderData.fromList(event);
_log("Parsed OrderData: ${newOrder.toMap()}");
setState(() {
orderData = newOrder;
});
_resetAndStartTimer();
} catch (e, s) {
_log("Error parsing OrderData: $e\nStackTrace: $s");
}
} else {
_log("Unexpected data format: $event");
}
}
void _checkOverlayStatus() async {
bool isActive = await FlutterOverlayWindow.isActive();
if (isActive && mounted && orderData != null) {
if (remainingSeconds > 0 && (timer == null || !timer!.isActive)) {
_resetAndStartTimer();
}
}
}
// === Timer Management ===
void _resetAndStartTimer() {
timer?.cancel();
audioPlayer.stop();
setState(() {
buttonsEnabled = true;
remainingSeconds = _calculateTimerDuration();
});
_playAudio();
_startTimer();
}
int _calculateTimerDuration() {
if (orderData?.durationToPassengerMinutes != null &&
orderData!.durationToPassengerMinutes > 0) {
int duration = orderData!.durationToPassengerMinutes * 60;
return duration > 10 ? 10 : duration;
}
return 10;
}
void _startTimer() {
if (orderData == null) return;
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (!mounted) {
timer.cancel();
_stopAudio();
return;
}
setState(() {
if (remainingSeconds > 0) {
remainingSeconds--;
} else {
timer.cancel();
_stopAudio();
if (buttonsEnabled) _handleOrderTimeout();
}
});
});
}
// === Audio Management ===
void _playAudio() async {
try {
await audioPlayer.setAsset('assets/order.mp3', preload: true);
await audioPlayer.setLoopMode(LoopMode.one);
await audioPlayer.play();
} catch (e) {
_log('Error playing audio: $e');
}
}
void _stopAudio() {
audioPlayer.stop();
}
String _getData(int index, {String defaultValue = ''}) {
if (myList.length > index && myList[index] != null) {
return myList[index].toString();
}
return defaultValue;
}
// === Order Actions ===
Future<void> _acceptOrder() async {
if (!buttonsEnabled || orderData == null) return;
_disableButtonsAndProcess();
_log("Order ACCEPTED: ${orderData!.orderId}");
try {
final driverId = box.read(BoxName.driverID)?.toString();
if (driverId == null) {
_log("Error: Driver ID is null. Closing overlay.");
await _closeOverlay();
return;
}
var res = await CRUD().post(link: AppLink.updateStausFromSpeed, payload: {
'id': orderData!.orderId,
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': orderData!.orderId,
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
final payload = {
// بيانات أساسية
'driver_id': driverId,
'status': 'Apply',
'passengerLocation': _getData(0),
'passengerDestination': _getData(1),
'Duration': _getData(4),
'totalCost': _getData(26),
'Distance': _getData(5),
'name': _getData(8),
'phone': _getData(10),
'email': _getData(28),
'WalletChecked': _getData(13),
'tokenPassenger': _getData(9),
'direction': staticMapUrl.toString(),
'DurationToPassenger': _getData(15),
'rideId': orderData!.orderId,
'passengerId': _getData(7),
'durationOfRideValue': _getData(19),
'paymentAmount': _getData(2),
'paymentMethod': _getData(13) == 'true' ? 'visa' : 'cash',
'isHaveSteps': _getData(20),
'step0': myList[21].toString(),
'step1': myList[22].toString(),
'step2': myList[23].toString(),
'step3': myList[24].toString(),
'step4': myList[25].toString(),
'passengerWalletBurc': myList[26].toString(),
'carType': myList[31].toString(),
'kazan': myList[32].toString(),
'startNameLocation': myList[29].toString(),
'endNameLocation': myList[30].toString(),
// الحقول الإضافية التي يجب تضمينها
'timeOfOrder': DateTime.now().toIso8601String(),
'totalPassenger': _getData(2),
};
Log.print('myList: ${myList}');
Log.print('payload: ${payload}');
CRUD().post(
link: AppLink.addOverLayStatus,
payload: payload,
);
if (res != "failure") {
// Using rideId (_getData(16)) for order_id consistently
CRUD().post(link: AppLink.addDriverOrder, payload: {
'driver_id': driverId, // Driver ID from the order data
'order_id': orderData!.orderId,
'status': 'Apply'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/add.php",
payload: {
'driver_id': driverId,
'order_id': orderData!.orderId,
'status': 'Apply'
});
}
_log("Server update successful. Writing to storage.");
notificationController.showNotification(
"Order Accepted".tr,
"Open app and go to passenger".tr,
'ding',
'',
);
await _closeOverlay();
} else {
_log("Failed to update order status on server: $res");
notificationController.showNotification(
"Order Accepted by another driver".tr,
"Open app and go to passenger".tr,
'ding',
'',
);
await _closeOverlay();
}
} catch (e, s) {
_log(
"A critical error occurred during server update: $e\nStackTrace: $s");
if (mounted) setState(() => buttonsEnabled = true);
return;
}
}
// Your list parsing for 'customerToken' should be something like:
// customerToken: list.length > a_certain_index ? list[a_certain_index].toString() : null,
Future<void> _rejectOrder() async {
if (!buttonsEnabled || orderData == null) return;
_disableButtonsAndProcess();
_log("Order REJECTED: ${orderData!.orderId}");
box.write(BoxName.rideStatus, 'reject');
Log.print('rideStatus from overlay 303 : ${box.read(BoxName.rideStatus)}');
await _apiRefuseOrder(orderData!.orderId);
await _closeOverlay();
}
void _handleOrderTimeout() {
if (orderData == null) return;
_log("Order TIMED OUT: ${orderData!.orderId}");
_rejectOrder();
}
Future<void> _apiRefuseOrder(String orderID) async {
if (orderID == "N/A") {
_log("Cannot refuse order with N/A ID");
return;
}
try {
final driverId = box.read(BoxName.driverID)?.toString();
if (driverId == null) {
_log("Driver ID is null, cannot refuse order");
return;
}
await _crud.post(link: AppLink.addDriverOrder, payload: {
'driver_id': driverId,
'order_id': orderID,
'status': 'Refused'
});
await _crud.post(link: AppLink.updateRides, payload: {
'id': orderID,
'status': 'Refused',
'driver_id': driverId,
});
_log("Order $orderID refused successfully");
} catch (e) {
_log("Error in _apiRefuseOrder for $orderID: $e");
}
}
// === Helper Methods ===
void _disableButtonsAndProcess() {
setState(() => buttonsEnabled = false);
timer?.cancel();
_stopAudio();
}
Future<void> _closeOverlay() async {
_stopAudio();
timer?.cancel();
if (await FlutterOverlayWindow.isActive()) {
await FlutterOverlayWindow.closeOverlay();
}
}
void _log(String message) {
// A simple logger to distinguish overlay logs
print("OVERLAY_LOG: $message");
}
// === UI Build Methods ===
@override
Widget build(BuildContext context) {
// ... (Your entire UI build method remains unchanged) ...
// The UI code is excellent and doesn't need modification.
if (orderData == null) {
return const Material(
color: Colors.transparent,
child: Center(
child: CircularProgressIndicator(color: AppColors.accent)));
}
return Material(
color: Colors.black.withOpacity(0.4),
child: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.circular(20.0),
border: Border.all(
color: AppColors.accent.withOpacity(0.3), width: 1.5),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.6),
blurRadius: 15,
spreadRadius: 2,
)
],
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildQuickHeader(),
const SizedBox(height: 12),
_buildPrimaryInfo(),
const SizedBox(height: 12),
if (canShowMap) _buildCompactMap(),
if (canShowMap) const SizedBox(height: 12),
_buildSecondaryInfo(),
const SizedBox(height: 16),
_buildEnhancedActionButtons(),
],
),
),
),
),
);
}
// All your _build... widget methods (_buildQuickHeader, _buildPrimaryInfo, etc.)
// are perfectly fine and do not need to be changed.
// ... Paste all your existing _build... methods here ...
Widget _buildQuickHeader() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
remainingSeconds <= 3
? AppColors.urgentRed
: remainingSeconds <= 5
? AppColors.highlight
: AppColors.accent,
remainingSeconds <= 3
? AppColors.urgentRed.withOpacity(0.7)
: remainingSeconds <= 5
? AppColors.highlight.withOpacity(0.7)
: AppColors.accent.withOpacity(0.7),
],
),
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.drive_eta_rounded, color: AppColors.white, size: 24),
const SizedBox(width: 8),
Text(
"طلب جديد".tr,
style: const TextStyle(
color: AppColors.white,
fontSize: 18,
fontWeight: FontWeight.w600),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: AppColors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(20),
),
child: Text(
"$remainingSeconds ث",
style: TextStyle(
color: remainingSeconds <= 3
? AppColors.urgentRed
: remainingSeconds <= 5
? AppColors.highlight
: AppColors.accent,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
Widget _buildPrimaryInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.6),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.priceHighlight.withOpacity(0.3), width: 1),
),
child: Column(
children: [
// Price and Distance - Most Important Info
Row(
children: [
Expanded(
flex: 3,
child: _buildHighlightInfo("\$${order.price}", "السعر".tr,
Icons.monetization_on_rounded, AppColors.priceHighlight,
isLarge: true),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: _buildHighlightInfo(
"${order.tripDistanceKm.toStringAsFixed(1)} كم",
"المسافة".tr,
Icons.straighten_rounded,
AppColors.accent,
),
),
],
),
const SizedBox(height: 12),
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 1),
const SizedBox(height: 12),
// Passenger Info and ETA
Row(
children: [
Expanded(
flex: 2,
child: _buildPassengerQuickInfo(),
),
const SizedBox(width: 12),
Expanded(
child: _buildHighlightInfo(
"${order.durationToPassengerMinutes} د",
"للوصول".tr,
Icons.access_time_filled_rounded,
order.durationToPassengerMinutes <= 3
? AppColors.priceHighlight
: AppColors.gray,
),
),
],
),
],
),
);
}
Widget _buildHighlightInfo(
String value, String label, IconData icon, Color color,
{bool isLarge = false}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: color.withOpacity(0.3), width: 1),
),
child: Column(
children: [
Icon(icon, color: color, size: isLarge ? 24 : 20),
const SizedBox(height: 4),
Text(
value,
style: TextStyle(
color: AppColors.white,
fontSize: isLarge ? 20 : 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
Text(
label,
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.7),
fontSize: 11,
),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _buildPassengerQuickInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: BoxDecoration(
color: AppColors.highlight.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
border:
Border.all(color: AppColors.highlight.withOpacity(0.3), width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.person_rounded, color: AppColors.highlight, size: 18),
const SizedBox(width: 4),
Expanded(
child: Text(
order.customerName,
style: const TextStyle(
color: AppColors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 2),
Text(
order.rideType,
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.7),
fontSize: 11,
),
),
],
),
);
}
Widget _buildCompactMap() {
return ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: Image.network(
staticMapUrl,
height: 100, // Reduced from 110
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 100,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.3),
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child:
Icon(Icons.map_outlined, color: AppColors.gray, size: 32)),
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return SizedBox(
height: 100,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
color: AppColors.accent,
strokeWidth: 2.0,
),
),
);
},
),
);
}
Widget _buildSecondaryInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.4),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
_buildLocationRow(
Icons.trip_origin_rounded,
"من".tr,
order.startLocationAddress,
Colors.green.shade300,
),
const SizedBox(height: 8),
_buildLocationRow(
Icons.flag_rounded,
"إلى".tr,
order.endLocationAddress,
Colors.red.shade300,
),
if (order.tripDurationMinutes > 0) ...[
const SizedBox(height: 8),
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 0.5),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.timer_outlined, color: AppColors.accent, size: 16),
const SizedBox(width: 4),
Text(
"مدة الرحلة: ${order.tripDurationMinutes} دقيقة".tr,
style: const TextStyle(
color: AppColors.lightGray,
fontSize: 12,
),
),
],
),
]
],
),
);
}
Widget _buildLocationRow(
IconData icon, String label, String address, Color iconColor) {
return Row(
children: [
Icon(icon, color: iconColor, size: 16),
const SizedBox(width: 8),
Text(
"$label: ",
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.8),
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
Expanded(
child: Text(
address,
style: const TextStyle(
color: AppColors.white,
fontSize: 12,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
);
}
Widget _buildEnhancedActionButtons() {
return Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: buttonsEnabled ? _rejectOrder : null,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.reject,
foregroundColor: AppColors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors.reject.withOpacity(0.3),
elevation: 3,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.close_rounded, size: 20),
const SizedBox(width: 6),
Text(
"رفض".tr,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600),
),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: buttonsEnabled ? _acceptOrder : null,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.accept,
foregroundColor: AppColors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors.accept.withOpacity(0.3),
elevation: 3,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.check_circle_rounded, size: 20),
const SizedBox(width: 6),
Text(
"قبول".tr,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600),
),
],
),
),
),
],
);
}
}

View File

@@ -0,0 +1,431 @@
import 'dart:convert';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math' as math;
import '../../../../constant/colors.dart';
import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../widgets/elevated_btn.dart';
class OrderRequestPage extends StatefulWidget {
const OrderRequestPage({super.key});
@override
State<OrderRequestPage> createState() => _OrderRequestPageState();
}
class _OrderRequestPageState extends State<OrderRequestPage> {
final OrderRequestController orderRequestController =
Get.put(OrderRequestController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Order Request'.tr),
centerTitle: true,
),
body: GetBuilder<OrderRequestController>(
builder: (controller) {
if (controller.myList == null) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
SizedBox(
height: Get.height * 0.3,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
zoom: 14.0,
),
myLocationButtonEnabled: true,
onMapCreated: controller.onMapCreated,
myLocationEnabled: true,
markers: {
Marker(
markerId: const MarkerId('startLocation'),
position: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
icon: controller.startIcon,
),
Marker(
markerId: const MarkerId('destinationLocation'),
position: LatLng(controller.latPassengerDestination,
controller.lngPassengerDestination),
icon: controller.endIcon,
),
},
polylines: {
Polyline(
polylineId: const PolylineId('route'),
color: AppColor.primaryColor,
width: 5,
points: controller.pointsDirection,
),
},
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
elevation: 4,
child: ListTile(
leading: Icon(
controller.myList[13].toString() == 'true'
? Icons.credit_card
: Icons.money,
color: controller.myList[13].toString() == 'true'
? AppColor.deepPurpleAccent
: AppColor.greenColor,
),
title: Text(
'Payment Method'.tr,
style: Theme.of(context).textTheme.titleMedium,
),
trailing: Text(
controller.myList[13].toString() == 'true'
? 'Visa'
: 'Cash',
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: ListTile(
leading: const Icon(Icons.account_circle,
color: AppColor.secondaryColor),
title: Text(
controller.myList[8],
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Row(
children: [
const Icon(Icons.star,
size: 16, color: Colors.amber),
Text(
controller.myList[33].toString(),
style: const TextStyle(color: Colors.amber),
),
],
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.location_on,
color: AppColor.greenColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[29],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
const Divider(),
Row(
children: [
const Icon(Icons.flag,
color: AppColor.redColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[30],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
],
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_InfoTile(
icon: Icons.timer,
label:
'${(double.parse(controller.myList[12]) / 60).toStringAsFixed(0)} ${'min'.tr}',
),
_InfoTile(
icon: Icons.directions_car,
label:
'${(double.parse(controller.myList[11]) / 1000).toStringAsFixed(1)} ${'km'.tr}',
),
_InfoTile(
icon: Icons.monetization_on,
label: '${controller.myList[2]}',
),
],
),
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Accept Order'.tr,
onPressed: () async {
Get.put(HomeCaptainController()).changeRideId();
box.write(BoxName.statusDriverLocation, 'on');
controller.endTimer();
controller.changeApplied();
var res = await CRUD().post(
link: AppLink.updateStausFromSpeed,
payload: {
'id': (controller.myList[16]),
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': (controller.myList[16]),
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
if (res == 'failure') {
MyDialog().getDialog(
"This ride is already applied by another driver."
.tr,
'', () {
Get.back();
});
} else {
await CRUD().postFromDialogue(
link: AppLink.addDriverOrder,
payload: {
'driver_id':
(controller.myList[6].toString()),
'order_id':
(controller.myList[16].toString()),
'status': 'Apply'
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().postFromDialogue(
link:
'${AppLink.endPoint}/rides/driver_order/add.php',
payload: {
'driver_id':
(controller.myList[6].toString()),
'order_id':
(controller.myList[16].toString()),
'status': 'Apply'
});
}
List<String> bodyToPassenger = [
controller.myList[6].toString(),
controller.myList[8].toString(),
controller.myList[9].toString(),
];
FirebaseMessagesController()
.sendNotificationToPassengerToken(
"Accepted Ride".tr,
'your ride is Accepted'.tr,
controller.myList[9].toString(),
bodyToPassenger,
'start.wav');
Get.back();
box.write(BoxName.rideArguments, {
'passengerLocation':
controller.myList[0].toString(),
'passengerDestination':
controller.myList[1].toString(),
'Duration': controller.myList[4].toString(),
'totalCost': controller.myList[26].toString(),
'Distance': controller.myList[5].toString(),
'name': controller.myList[8].toString(),
'phone': controller.myList[10].toString(),
'email': controller.myList[28].toString(),
'WalletChecked':
controller.myList[13].toString(),
'tokenPassenger':
controller.myList[9].toString(),
'direction':
'https://www.google.com/maps/dir/${controller.myList[0]}/${controller.myList[1]}/',
'DurationToPassenger':
controller.myList[15].toString(),
'rideId': (controller.myList[16].toString()),
'passengerId':
(controller.myList[7].toString()),
'driverId': (controller.myList[18].toString()),
'durationOfRideValue':
controller.myList[19].toString(),
'paymentAmount':
controller.myList[2].toString(),
'paymentMethod':
controller.myList[13].toString() == 'true'
? 'visa'
: 'cash',
'isHaveSteps': controller.myList[20].toString(),
'step0': controller.myList[21].toString(),
'step1': controller.myList[22].toString(),
'step2': controller.myList[23].toString(),
'step3': controller.myList[24].toString(),
'step4': controller.myList[25].toString(),
'passengerWalletBurc':
controller.myList[26].toString(),
'timeOfOrder': DateTime.now().toString(),
'totalPassenger':
controller.myList[2].toString(),
'carType': controller.myList[31].toString(),
'kazan': controller.myList[32].toString(),
'startNameLocation':
controller.myList[29].toString(),
'endNameLocation':
controller.myList[30].toString(),
});
Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments));
}
},
),
GetBuilder<OrderRequestController>(
builder: (timerController) {
final isNearEnd = timerController.remainingTime <=
5; // Define a threshold for "near end"
return Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: timerController.progress,
// Set the color based on the "isNearEnd" condition
color: isNearEnd ? Colors.red : Colors.blue,
),
Text(
'${timerController.remainingTime}',
style: AppStyle.number,
),
],
);
},
),
MyElevatedButton(
title: 'Refuse Order'.tr,
onPressed: () async {
controller.endTimer();
List<String> bodyToPassenger = [
box.read(BoxName.driverID).toString(),
box.read(BoxName.nameDriver).toString(),
box.read(BoxName.tokenDriver).toString(),
];
FirebaseMessagesController()
.sendNotificationToPassengerToken(
'Order Under Review'.tr,
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
controller.myList[9].toString(),
bodyToPassenger,
'notification.wav');
controller.refuseOrder(
EncryptionHelper.instance.encryptData(
controller.myList[16].toString()),
);
controller.addRideToNotificationDriverString(
controller.myList[16].toString(),
controller.myList[29].toString(),
controller.myList[30].toString(),
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
'${DateTime.now().hour}:${DateTime.now().minute}',
controller.myList[2].toString(),
controller.myList[7].toString(),
'wait',
controller.myList[31].toString(),
controller.myList[33].toString(),
controller.myList[2].toString(),
controller.myList[5].toString(),
controller.myList[4].toString());
},
kolor: AppColor.redColor,
),
],
),
],
),
),
],
);
},
),
);
}
}
class _InfoTile extends StatelessWidget {
final IconData icon;
final String label;
const _InfoTile({required this.icon, required this.label});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, color: AppColor.primaryColor),
const SizedBox(height: 4),
Text(
label,
style: Theme.of(context).textTheme.bodyMedium,
),
],
);
}
}

View File

@@ -0,0 +1,603 @@
import 'dart:convert'; // Though not directly used in this version's UI logic, often kept for model interactions.
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/main.dart'; // For `box`
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../../constant/colors.dart'; // Your AppColor
import '../../../../constant/links.dart'; // Your AppLink
import '../../../../constant/style.dart'; // Your AppStyle
import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../widgets/elevated_btn.dart'; // Your MyElevatedButton
class OrderSpeedRequest extends StatelessWidget {
OrderSpeedRequest({super.key});
final OrderRequestController orderRequestController =
Get.put(OrderRequestController());
// Helper to make myList access more readable and safer
String _getData(int index, {String defaultValue = ''}) {
if (orderRequestController.myList.length > index &&
orderRequestController.myList[index] != null) {
return orderRequestController.myList[index].toString();
}
return defaultValue;
}
@override
Widget build(BuildContext context) {
// Define AppBar first to get its height for body calculations
final appBar = AppBar(
title: Text('Speed Order'.tr),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
backgroundColor: AppColor.primaryColor, // Example color, adjust as needed
elevation: 2.0,
);
final double appBarHeight = appBar.preferredSize.height;
final MediaQueryData mediaQueryData = MediaQuery.of(context);
final double screenHeight = mediaQueryData.size.height;
final double statusBarHeight = mediaQueryData.padding.top;
final double bottomSystemPadding = mediaQueryData.padding.bottom;
// Calculate available height for the Scaffold's body content
// Subtracting status bar, app bar, and bottom system padding (like navigation bar)
final double availableBodyHeight =
screenHeight - appBarHeight - statusBarHeight - bottomSystemPadding;
// Define overall padding for the body content
const EdgeInsets bodyContentPadding =
EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0);
// Calculate the height for the main content Column, considering the body's own padding
double mainColumnHeight = availableBodyHeight - bodyContentPadding.vertical;
if (mainColumnHeight < 0) mainColumnHeight = 0;
return GetBuilder<OrderRequestController>(
builder: (controller) {
// Pre-extract data for readability and safety
final String price =
double.tryParse(_getData(2))?.toStringAsFixed(2) ?? 'N/A';
final bool isComfortTrip = _getData(31) == 'Comfort';
final String carType = _getData(31).tr;
final String pickupName = _getData(12);
final String pickupDetails = '(${_getData(11)})';
final String pickupFullAddress = _getData(29);
final String dropoffName = _getData(5);
final String dropoffDetails = '(${_getData(4)})';
final String dropoffFullAddress = _getData(30);
final String passengerName = _getData(8);
final String passengerRating = _getData(33);
final bool isVisaPayment = _getData(13) == 'true';
final bool hasSteps = _getData(20) == 'haveSteps';
final String mapUrl =
'https://www.google.com/maps/dir/${_getData(0)}/${_getData(1)}/';
final String rideId = _getData(16); // Commonly used ID
return Scaffold(
appBar: appBar,
backgroundColor: AppColor.secondaryColor ??
Colors.grey[100], // Background for the page
body: SafeArea(
// Ensures content is not obscured by system UI
child: Padding(
padding:
bodyContentPadding, // Apply overall padding to the body's content area
child: SizedBox(
// Constrain the height of the main layout Column
height: mainColumnHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// --- MAP SECTION ---
SizedBox(
height:
Get.height * 0.28, // Relative to total screen height
child: ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 12,
target:
Get.find<HomeCaptainController>().myLocation),
cameraTargetBounds:
CameraTargetBounds(controller.bounds),
myLocationButtonEnabled: false,
trafficEnabled: false,
buildingsEnabled: false,
mapToolbarEnabled: false,
myLocationEnabled: true,
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: LatLng(
controller.latPassengerLocation,
controller.lngPassengerLocation),
icon: controller.startIcon),
Marker(
markerId: MarkerId('Destination'.tr),
position: LatLng(
controller.latPassengerDestination,
controller.lngPassengerDestination),
icon: controller.endIcon),
},
polylines: {
Polyline(
zIndex: 1,
consumeTapEvents: true,
geodesic: true,
endCap: Cap.buttCap,
startCap: Cap.buttCap,
visible: true,
polylineId: const PolylineId('routeOrder'),
points: controller.pointsDirection,
color: AppColor.primaryColor,
width: 3,
),
},
),
),
),
const SizedBox(height: 8),
// --- PRICE & TRIP TYPE SECTION ---
Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
price,
style: AppStyle.headTitle.copyWith(
color: AppColor.primaryColor,
fontWeight: FontWeight.bold,
fontSize: 28),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
carType,
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold),
),
if (isComfortTrip)
Row(
children: [
const Icon(Icons.ac_unit,
color: AppColor.blueColor, size: 18),
const SizedBox(width: 4),
Text('Air condition Trip'.tr,
style: AppStyle.subtitle
.copyWith(fontSize: 13)),
],
),
],
),
],
),
),
),
const SizedBox(height: 8),
// --- EXPANDED SECTION FOR SCROLLABLE (BUT NOT USER-SCROLLABLE) CONTENT ---
Expanded(
child: SingleChildScrollView(
physics:
const NeverScrollableScrollPhysics(), // Prevents user scrolling
child: Column(
mainAxisSize: MainAxisSize
.min, // Takes minimum vertical space needed
children: [
_buildLocationCard(
icon: Icons.arrow_circle_up,
iconColor: AppColor.greenColor,
title: pickupName,
subtitle: pickupDetails,
fullAddress: pickupFullAddress,
),
const SizedBox(height: 8),
_buildLocationCard(
icon: Icons.arrow_circle_down,
iconColor: AppColor.redColor,
title: dropoffName,
subtitle: dropoffDetails,
fullAddress: dropoffFullAddress,
),
const SizedBox(height: 8),
// --- PAYMENT, STEPS & DIRECTIONS INFO ---
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
isVisaPayment
? Icons.credit_card
: Icons
.payments_outlined, // Using payments_outlined for cash
color: isVisaPayment
? AppColor.deepPurpleAccent
: AppColor.greenColor,
size: 24,
),
const SizedBox(width: 8),
Text(
isVisaPayment ? 'Visa'.tr : 'Cash'.tr,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600),
),
],
),
if (hasSteps)
Expanded(
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
const Icon(
Icons
.format_list_numbered_rtl_outlined,
color: AppColor.bronze,
size: 24),
const SizedBox(width: 4),
Flexible(
child: Text(
'Trip has Steps'.tr,
style: AppStyle.title.copyWith(
color: AppColor.bronze,
fontSize: 13),
overflow: TextOverflow.ellipsis,
)),
],
),
),
TextButton.icon(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
tapTargetSize:
MaterialTapTargetSize.shrinkWrap,
alignment: Alignment.centerRight,
),
onPressed: () => showInBrowser(mapUrl),
icon: const Icon(
Icons.directions_outlined,
color: AppColor.blueColor,
size: 20),
label: Text("Directions".tr,
style: AppStyle.subtitle.copyWith(
color: AppColor.blueColor,
fontSize: 13)),
),
],
),
),
),
const SizedBox(height: 8),
// --- PASSENGER INFO ---
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 10.0),
child: Row(
children: [
const Icon(Icons.person_outline,
color: AppColor.greyColor, size: 22),
const SizedBox(width: 10),
Expanded(
child: Text(
passengerName,
style: AppStyle.title,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 10),
const Icon(Icons.star_rounded,
color: Colors.amber, size: 20),
Text(
passengerRating,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
),
),
// const SizedBox(height: 8), // Spacer before action buttons if needed
// --- ACTION BUTTONS & TIMER ---
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Accept Order'.tr,
onPressed: () async {
Get.put(HomeCaptainController()).changeRideId();
box.write(BoxName.statusDriverLocation, 'on');
var res = await CRUD().post(
link: AppLink.updateStausFromSpeed,
payload: {
'id': rideId,
'rideTimeStart':
DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': rideId,
'rideTimeStart':
DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
if (res != "failure") {
box.write(BoxName.statusDriverLocation, 'on');
controller.changeApplied();
List<String> bodyToPassenger = [
box.read(BoxName.driverID).toString(),
box.read(BoxName.nameDriver).toString(),
box.read(BoxName.tokenDriver).toString(),
rideId.toString(),
];
Get.put(FirebaseMessagesController())
.sendNotificationToDriverMAP(
'Accepted Ride',
'your ride is applied'.tr,
controller.arguments?['DriverList']
?[9]
?.toString() ??
_getData(9), // Safer access
bodyToPassenger,
'start.wav');
// Using rideId (_getData(16)) for order_id consistently
CRUD().postFromDialogue(
link: AppLink.addDriverOrder,
payload: {
'driver_id': _getData(
6), // Driver ID from the order data
'order_id': rideId,
'status': 'Apply'
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/driver_order/add.php",
payload: {
'driver_id': _getData(6),
'order_id': rideId,
'status': 'Apply'
});
}
Get.back(); // Go back from order request screen
box.write(BoxName.rideArguments, {
'passengerLocation': _getData(0),
'passengerDestination': _getData(1),
'Duration': _getData(4),
'totalCost': _getData(26),
'Distance': _getData(5),
'name': _getData(8),
'phone': _getData(10),
'email': _getData(28),
'WalletChecked': _getData(13),
'tokenPassenger': _getData(9),
'direction': mapUrl,
'DurationToPassenger': _getData(15),
'rideId': rideId,
'passengerId': _getData(7),
'driverId': box
.read(BoxName.driverID)
.toString(), // Current driver accepting
'durationOfRideValue': _getData(19),
'paymentAmount': _getData(2),
'paymentMethod': _getData(13) == 'true'
? 'visa'
: 'cash',
'isHaveSteps': _getData(20),
'step0': _getData(21),
'step1': _getData(22),
'step2': _getData(23),
'step3': _getData(24),
'step4': _getData(25),
'passengerWalletBurc': _getData(26),
'timeOfOrder': DateTime.now().toString(),
'totalPassenger': _getData(
2), // This is likely trip cost for passenger
'carType': _getData(31),
'kazan':
_getData(32), // Driver's commission/cut
'startNameLocation': _getData(29),
'endNameLocation': _getData(30),
});
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments));
} else {
Get.defaultDialog(
title:
"This ride is already taken by another driver."
.tr,
middleText: '',
titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () {
Get.back(); // Close dialog
Get.back(); // Close order request screen
}));
}
},
),
),
const SizedBox(width: 10),
// --- TIMER ---
GetBuilder<OrderRequestController>(
id: 'timerUpdate', // Ensure controller calls update(['timerUpdate']) for this
builder: (timerCtrl) {
final isNearEnd =
timerCtrl.remainingTimeSpeed <= 5;
return SizedBox(
width: 60,
height: 60,
child: Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: timerCtrl.progressSpeed,
color: isNearEnd
? Colors.redAccent
: AppColor.primaryColor,
strokeWidth: 5,
backgroundColor: Colors.grey.shade300,
),
Text('${timerCtrl.remainingTimeSpeed}',
style: AppStyle.headTitle2.copyWith(
color: isNearEnd
? Colors.redAccent
: AppColor.writeColor ??
Colors.black,
)),
],
),
);
},
),
const SizedBox(width: 10),
Expanded(
child: MyElevatedButton(
title: 'Refuse Order'.tr,
onPressed: () async {
controller.endTimer();
controller.refuseOrder(rideId);
controller.addRideToNotificationDriverString(
rideId,
_getData(29),
_getData(30),
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
'${DateTime.now().hour}:${DateTime.now().minute}',
_getData(2),
_getData(7),
'wait',
_getData(31),
_getData(33),
_getData(2),
_getData(5),
_getData(4));
// Get.back(); // refuseOrder or endTimer should handle navigation if needed.
// If not, add Get.back(); here.
},
kolor: AppColor.redColor,
),
),
],
),
),
],
),
),
),
),
);
},
);
}
// Helper widget for location cards to reduce repetition and improve readability
Widget _buildLocationCard(
{required IconData icon,
required Color iconColor,
required String title,
required String subtitle,
required String fullAddress}) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
margin: const EdgeInsets.symmetric(
vertical: 4), // Add a little vertical margin between cards
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: iconColor, size: 28),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"$title $subtitle"
.trim(), // Trim to avoid extra spaces if subtitle is empty
style: AppStyle.title.copyWith(fontWeight: FontWeight.w600),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (fullAddress.isNotEmpty) ...[
const SizedBox(height: 3),
Text(
fullAddress,
style: AppStyle.subtitle
.copyWith(fontSize: 13, color: AppColor.greyColor),
maxLines: 2, // Allow up to 2 lines for address
overflow: TextOverflow.ellipsis,
),
]
],
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,230 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math' as math;
import '../../../../constant/colors.dart';
import '../../../../controller/home/captin/home_captain_controller.dart';
class OrderRequestPageTest extends StatefulWidget {
const OrderRequestPageTest({super.key});
@override
State<OrderRequestPageTest> createState() => _OrderRequestPageTestState();
}
class _OrderRequestPageTestState extends State<OrderRequestPageTest> {
late OrderRequestController _orderRequestController;
@override
void initState() {
super.initState();
// Initialize the controller and process arguments
_initializeController();
}
void _initializeController() {
// Get the controller or create a new one if not exists
_orderRequestController = Get.put(OrderRequestController());
// Process arguments passed to the page
final arguments = Get.arguments;
final myListString = arguments['myListString'];
var myList =
arguments['DriverList'] == null || arguments['DriverList'].isEmpty
? jsonDecode(myListString)
: arguments['DriverList'];
// Parse coordinates and prepare map data
_orderRequestController.parseCoordinates(myList);
// Start timer and calculate fuel consumption
_orderRequestController.startTimer(
myList[6].toString(),
myList[16].toString(),
);
_orderRequestController.calculateConsumptionFuel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Container(
color: const Color.fromARGB(255, 241, 238, 238),
child: ListView(
children: [
SizedBox(
height: Get.height * .33,
child: Obx(() => GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 12,
target:
Get.find<HomeCaptainController>().myLocation,
),
cameraTargetBounds: CameraTargetBounds(
_orderRequestController.mapBounds.value),
myLocationButtonEnabled: true,
trafficEnabled: false,
buildingsEnabled: false,
mapToolbarEnabled: true,
myLocationEnabled: true,
markers: _orderRequestController.markers.value,
polylines: _orderRequestController.polylines.value,
onMapCreated: (GoogleMapController controller) {
_orderRequestController.mapController.value =
controller;
},
onCameraMove: (CameraPosition position) {
_orderRequestController
.updateCameraPosition(position);
},
)),
),
// Rest of your UI components
],
),
),
),
],
),
);
}
}
class OrderRequestController extends GetxController {
// Reactive variables for map-related data
Rx<LatLngBounds> mapBounds = Rx<LatLngBounds>(LatLngBounds(
southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)));
Rx<Set<Marker>> markers = Rx<Set<Marker>>({});
Rx<Set<Polyline>> polylines = Rx<Set<Polyline>>({});
Rx<GoogleMapController?> mapController = Rx<GoogleMapController?>(null);
// Icons for start and end markers
late BitmapDescriptor startIcon;
late BitmapDescriptor endIcon;
// Coordinates for passenger location and destination
Rx<LatLng?> passengerLocation = Rx<LatLng?>(null);
Rx<LatLng?> passengerDestination = Rx<LatLng?>(null);
@override
void onInit() {
super.onInit();
// Initialize marker icons
_initializeMarkerIcons();
}
void _initializeMarkerIcons() async {
// Load custom marker icons
startIcon = await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)),
'assets/start_marker.png');
endIcon = await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)), 'assets/end_marker.png');
}
void parseCoordinates(List myList) {
// Parse coordinates from the input list
var cords = myList[0].split(',');
var cordDestination = myList[1].split(',');
double latPassengerLocation = double.parse(cords[0]);
double lngPassengerLocation = double.parse(cords[1]);
double latPassengerDestination = double.parse(cordDestination[0]);
double lngPassengerDestination = double.parse(cordDestination[1]);
// Update passenger location and destination
passengerLocation.value =
LatLng(latPassengerLocation, lngPassengerLocation);
passengerDestination.value =
LatLng(latPassengerDestination, lngPassengerDestination);
// Create markers
_createMarkers();
// Create polyline
_createPolyline();
// Calculate map bounds
_calculateMapBounds();
}
void _createMarkers() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
markers.value = {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: passengerLocation.value!,
draggable: true,
icon: startIcon,
),
Marker(
markerId: MarkerId('Destination'.tr),
position: passengerDestination.value!,
draggable: true,
icon: endIcon,
),
};
}
void _createPolyline() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
polylines.value = {
Polyline(
zIndex: 1,
consumeTapEvents: true,
geodesic: true,
endCap: Cap.buttCap,
startCap: Cap.buttCap,
visible: true,
polylineId: const PolylineId('routeOrder'),
points: [passengerLocation.value!, passengerDestination.value!],
color: AppColor.primaryColor,
width: 2,
),
};
}
void _calculateMapBounds() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
double minLatitude = math.min(passengerLocation.value!.latitude,
passengerDestination.value!.latitude);
double maxLatitude = math.max(passengerLocation.value!.latitude,
passengerDestination.value!.latitude);
double minLongitude = math.min(passengerLocation.value!.longitude,
passengerDestination.value!.longitude);
double maxLongitude = math.max(passengerLocation.value!.longitude,
passengerDestination.value!.longitude);
mapBounds.value = LatLngBounds(
southwest: LatLng(minLatitude, minLongitude),
northeast: LatLng(maxLatitude, maxLongitude),
);
}
void updateCameraPosition(CameraPosition position) {
// Implement any specific logic for camera position updates
}
void startTimer(String param1, String param2) {
// Implement timer start logic
}
void calculateConsumptionFuel() {
// Implement fuel consumption calculation
}
}

View File

@@ -0,0 +1,237 @@
import 'dart:convert';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../main.dart';
import '../../../../print.dart';
class VipOrderPage extends StatelessWidget {
const VipOrderPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(VipOrderController());
return MyScafolld(
title: 'VIP Order'.tr,
body: [
GetBuilder<VipOrderController>(builder: (vipOrderController) {
if (vipOrderController.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (vipOrderController.tripData.isEmpty) {
return Center(
child: Text('No orders available'.tr),
);
}
final order = vipOrderController.tripData[0];
Log.print('order: ${order}');
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Passenger Section
_buildSectionTitle('Passenger Information'.tr),
_buildInfoCard(
children: [
_buildDetailRow('Name'.tr,
'${order['passengerName'] ?? 'Unknown'} ${order['passengerLastName'] ?? ''}'),
_buildDetailRow(
'Phone'.tr, order['passengerPhone'] ?? 'Unknown'),
_buildDetailRow(
'Gender'.tr, order['passengergender'] ?? 'Unknown'),
_buildDetailRow('time Selected'.tr,
order['timeSelected'] ?? 'Unknown'),
_buildDetailRow(
'Ride Status'.tr, order['status'] ?? 'Unknown'),
// _buildDetailRow('Ride Status'.tr,
// vipOrderController.myList[4] ?? 'Unknown'),
IconButton(
onPressed: () {
// print(vipOrderController.myList);
},
icon: const Icon(Icons.add),
),
],
),
const SizedBox(height: 24),
// Action Buttons
_buildActionButtons(context),
],
),
),
);
})
],
isleading: true,
);
}
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
);
}
Widget _buildInfoCard({required List<Widget> children}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
),
);
}
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 2,
child: Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
),
Expanded(
flex: 3,
child: Text(
value,
style: const TextStyle(color: Colors.black54),
),
),
],
),
);
}
Widget _buildActionButtons(BuildContext context) {
return Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => _onReject(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Reject'.tr,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () => _onApply(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Apply'.tr,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
void _onReject(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ride Rejected'),
backgroundColor: Colors.red,
),
);
}
void _onApply(BuildContext context) {
// TODO: Implement application logic
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ride Applied'),
backgroundColor: Colors.green,
),
);
}
class VipOrderController extends GetxController {
bool isLoading = false;
List tripData = [];
fetchOrder() async {
isLoading = true; // Set loading state
update(); // Notify listeners
var res = await CRUD().get(link: AppLink.getMishwariDriver, payload: {
'driverId': box.read(BoxName.driverID).toString(),
});
if (res != 'failure') {
tripData = jsonDecode(res)['message'];
} else {
tripData = [];
}
isLoading = false; // Loading complete
update(); // Notify listeners
}
@override
void onInit() async {
fetchOrder();
super.onInit();
}
}

View File

@@ -0,0 +1,46 @@
// import 'dart:io';
//
// import 'package:flutter/material.dart';
// import 'package:get/get.dart';
// import '../../../controller/functions/ocr_controller.dart';
//
// class PassportDataExtractorWidget extends StatelessWidget {
// final PassportDataExtractor passportDataExtractor =
// Get.put(PassportDataExtractor());
// final PassportDataController controller = Get.put(PassportDataController());
//
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(
// title: const Text('Passport Data Extractor'),
// ),
// body: Column(
// children: [
// ElevatedButton(
// onPressed: controller.extractDataAndDrawBoundingBoxes,
// child: const Text('Extract Data'),
// ),
// Expanded(
// child: Center(
// child: Stack(
// children: [
// GetBuilder<PassportDataController>(
// builder: (controller) => CustomPaint(
// painter: BoundingBoxPainter(
// controller.extractedTextWithCoordinates),
// child: GetBuilder<PassportDataExtractor>(
// builder: (controller) =>
// Image.file(File(passportDataExtractor.image!.path)),
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// );
// }
// }

View File

@@ -0,0 +1,175 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../controller/functions/ocr_controller.dart';
class TextRecognizerAPI extends StatelessWidget {
const TextRecognizerAPI({super.key});
@override
Widget build(BuildContext context) {
Get.put(ScanDocumentsByApi());
return GetBuilder<ScanDocumentsByApi>(
builder: (controller) => Scaffold(
appBar: AppBar(
title: Text('Api'),
actions: [
IconButton(
onPressed: () {
controller.matchFaceApi();
},
icon: const Icon(Icons.face),
),
],
),
body: Center(
child: controller.isLoading
? const MyCircularProgressIndicator()
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.memory(
controller.imagePortrait,
width: 60,
),
Image.memory(
controller.imageSignature,
width: 60,
),
Image.memory(
controller.imageDocumentFrontSide,
width: 250,
),
],
),
Text(controller.responseMap['authenticity_meta']
.toString()),
Text(
'countryName: ${controller.responseMap['data']['countryName'].toString()}'),
Text(
'Expiry Date: ${controller.responseMap['data']['ocr']['dateOfExpiry'].toString()}'),
// Add more Text widgets to display other record values
Text(
'Address: ${controller.responseMap['data']['ocr']['address'].toString()}'),
Text(
'City: ${controller.responseMap['data']['ocr']['addressCity'].toString()}'),
Text(
'Jurisdiction Code: ${controller.responseMap['data']['ocr']['addressJurisdictionCode'].toString()}'),
Text(
'Postal Code: ${controller.responseMap['data']['ocr']['addressPostalCode'].toString()}'),
Text(
'Street: ${controller.responseMap['data']['ocr']['addressStreet'].toString()}'),
Text(
'Date of Birth: ${controller.responseMap['data']['ocr']['dateOfBirth'].toString()}'),
Text(
'Date of Issue: ${controller.responseMap['data']['ocr']['dateOfIssue'].toString()}'),
Text(
'Drivers License Class: ${controller.responseMap['data']['ocr']['dlClass'].toString()}'),
Text(
'Document Number: ${controller.responseMap['data']['ocr']['documentNumber'].toString()}'),
Text(
'Eye Color: ${controller.responseMap['data']['ocr']['eyesColor'].toString()}'),
Text(
'Given Names: ${controller.responseMap['data']['ocr']['givenNames'].toString()}'),
Text(
'Height: ${controller.responseMap['data']['ocr']['height'].toString()}'),
Text(
'Issuing State Code: ${controller.responseMap['data']['ocr']['issuingStateCode'].toString()}'),
Text(
'Name: ${controller.responseMap['data']['ocr']['name'].toString()}'),
Text(
'Sex: ${controller.responseMap['data']['ocr']['sex'].toString()}'),
Text(
'Surname: ${controller.responseMap['data']['ocr']['surname'].toString()}'),
Text(
'Valid State: ${controller.responseMap['data']['ocr']['validState'].toString()}'),
],
),
))));
}
}
// class TextExtractionView extends StatelessWidget {
// TextExtractionView({super.key});
//
// @override
// Widget build(BuildContext context) {
// Get.put(TextExtractionController());
// return Scaffold(
// appBar: AppBar(
// title: const Text('Text Extraction'),
// ),
// body: GetBuilder<TextExtractionController>(builder: (controller) {
// return Center(
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// ElevatedButton(
// onPressed: () {},
// child: const Text('Pick Image and Extract Text'),
// ),
// const SizedBox(height: 20),
// controller.isloading
// ? const MyCircularProgressIndicator()
// : Text(controller.extractedText),
// ],
// ),
// );
// }),
// );
// }
// }
// class TextRecognizerWidget extends StatelessWidget {
// const TextRecognizerWidget({super.key});
//
// @override
// Widget build(BuildContext context) {
// Get.put(TextMLGoogleRecognizerController());
// return GetBuilder<TextMLGoogleRecognizerController>(
// builder: (controller) => Scaffold(
// appBar: AppBar(),
// body: Center(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text('${controller.decode['DRIVER_LICENSE'].toString()}'),
// Text('DL: ${controller.decode['dl_number'].toString()}'),
// Text(
// 'Expiry Date: ${controller.decode['expiry_date'].toString()}'),
// Text('Last Name: ${controller.decode['lastName'].toString()}'),
// Text(
// 'First Name: ${controller.decode['firstName'].toString()}'),
// Text('Address: ${controller.decode['address'].toString()}'),
// Text('Date of Birth: ${controller.decode['dob'].toString()}'),
// Text('RSTR: ${controller.decode['rstr'].toString()}'),
// Text('Class: ${controller.decode['class'].toString()}'),
// Text('End: ${controller.decode['end'].toString()}'),
// Text('DD: ${controller.decode['dd'].toString()}'),
// Text('Sex: ${controller.decode['sex'].toString()}'),
// Text('Hair: ${controller.decode['hair'].toString()}'),
// Text('Eyes: ${controller.decode['eyes'].toString()}'),
// // and so on for other fields
// ],
// ))));
// }
// }
// class PassportPage extends StatelessWidget {
// PassportPage({super.key});
// PassportRecognizerController passportRecognizerController =
// Get.put(PassportRecognizerController());
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(
// title: const Text('Driver License'),
// ),
// body: const Center(child: Text('data')));
// }
// }

View File

@@ -0,0 +1,140 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class BankController extends GetxController {
String selectedBank = '';
Map<String, String> bankNames = {
'Ahli United Bank'.tr: 'AUB',
'Citi Bank N.A. Egypt'.tr: 'CITI',
'MIDBANK'.tr: 'MIDB',
'Banque Du Caire'.tr: 'BDC',
'HSBC Bank Egypt S.A.E'.tr: 'HSBC',
'Credit Agricole Egypt S.A.E'.tr: 'ECAE',
'Egyptian Gulf Bank'.tr: 'EGB',
'The United Bank'.tr: 'UB',
'Qatar National Bank Alahli'.tr: 'QNB',
'Arab Bank PLC'.tr: 'ARAB',
'Emirates National Bank of Dubai'.tr: 'ENBD',
'Al Ahli Bank of Kuwait Egypt'.tr: 'ABK',
'National Bank of Kuwait Egypt'.tr: 'NBK',
'Arab Banking Corporation - Egypt S.A.E'.tr: 'EABC',
'First Abu Dhabi Bank'.tr: 'FAB',
'Abu Dhabi Islamic Bank Egypt'.tr: 'ADIB',
'Commercial International Bank - Egypt S.A.E'.tr: 'CIB',
'Housing And Development Bank'.tr: 'HDB',
'Banque Misr'.tr: 'MISR',
'Arab African International Bank'.tr: 'AAIB',
'Egyptian Arab Land Bank'.tr: 'EALB',
'Export Development Bank of Egypt'.tr: 'EDBE',
'Faisal Islamic Bank of Egypt'.tr: 'FAIB',
'Blom Bank'.tr: 'BLOM',
'Abu Dhabi Commercial Bank Egypt'.tr: 'ADCB',
'Alex Bank Egypt'.tr: 'BOA',
'Societe Arabe Internationale De Banque'.tr: 'SAIB',
'National Bank of Egypt'.tr: 'NBE',
'Al Baraka Bank Egypt B.S.C.'.tr: 'ABRK',
'Egypt Post'.tr: 'POST',
'Nasser Social Bank'.tr: 'NSB',
'Industrial Development Bank'.tr: 'IDB',
'Suez Canal Bank'.tr: 'SCB',
'Mashreq Bank'.tr: 'MASHA',
'Arab Investment Bank'.tr: 'AIB',
'General Authority For Supply Commodities'.tr: 'GASCA',
'Arab International Bank'.tr: 'AIB',
'Agricultural Bank of Egypt'.tr: 'PDAC',
'National Bank of Greece'.tr: 'NBG',
'Central Bank Of Egypt'.tr: 'CBE',
'ATTIJARIWAFA BANK Egypt'.tr: 'BBE',
};
@override
void onInit() {
super.onInit();
selectedBank = bankNames.values.first;
}
void updateSelectedBank(String? bankShortName) {
selectedBank = bankShortName ?? '';
update();
}
List<DropdownMenuItem<String>> getDropdownItems() {
return bankNames.keys.map<DropdownMenuItem<String>>((bankFullName) {
return DropdownMenuItem<String>(
value: bankNames[bankFullName],
child: Text(bankFullName),
);
}).toList();
}
void showBankPicker(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => CupertinoActionSheet(
title: Text('Select a Bank'.tr),
actions: bankNames.keys.map((String bankFullName) {
return CupertinoActionSheetAction(
child: Text(bankFullName),
onPressed: () {
updateSelectedBank(bankNames[bankFullName]);
Navigator.pop(context);
},
);
}).toList(),
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
class BankDropdown extends StatelessWidget {
final BankController bankController = Get.put(BankController());
@override
Widget build(BuildContext context) {
return GetBuilder<BankController>(
init: bankController,
builder: (controller) {
return CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () => controller.showBankPicker(context),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
border: Border.all(color: CupertinoColors.systemGrey4),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
controller.selectedBank != null
? controller.bankNames.keys.firstWhere(
(key) =>
controller.bankNames[key] ==
controller.selectedBank,
orElse: () => 'Select a Bank'.tr,
)
: 'Select a Bank'.tr,
style: TextStyle(
color: controller.selectedBank != null
? CupertinoColors.black
: CupertinoColors.systemGrey,
),
),
const Icon(CupertinoIcons.chevron_down, size: 20),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,299 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/payment/captain_wallet_controller.dart';
import '../../../controller/home/payment/paymob_payout.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_textField.dart';
// تذكير: ستحتاج إلى إضافة حزمة flutter_svg إلى ملف pubspec.yaml
// dependencies:
// flutter_svg: ^2.0.7
/// بطاقة المحفظة بتصميم سوري فاخر مستوحى من فن الأرابيسك والفسيفساء
class CardSeferWalletDriver extends StatelessWidget {
const CardSeferWalletDriver({super.key});
// SVG لنقشة أرابيسك هندسية لاستخدامها كخلفية
final String arabesquePattern = '''
<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="arabesque" patternUnits="userSpaceOnUse" width="50" height="50" patternTransform="scale(1.2)">
<g fill="#E7C582" fill-opacity="0.1">
<path d="M25 0 L35.35 9.65 L50 25 L35.35 40.35 L25 50 L14.65 40.35 L0 25 L14.65 9.65 Z"/>
<path d="M50 0 L60.35 9.65 L75 25 L60.35 40.35 L50 50 L39.65 40.35 L25 25 L39.65 9.65 Z"/>
<path d="M0 50 L9.65 39.65 L25 25 L9.65 10.35 L0 0 L-9.65 10.35 L-25 25 L-9.65 39.65 Z"/>
</g>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#arabesque)"/>
</svg>
''';
@override
Widget build(BuildContext context) {
return Center(
child: GetBuilder<CaptainWalletController>(
builder: (captainWalletController) {
return GestureDetector(
onTap: () => _showCashOutDialog(context, captainWalletController),
child: Container(
width: Get.width * 0.9,
height: Get.height * 0.25,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
boxShadow: [
BoxShadow(
color: const Color(0xFF003C43).withOpacity(0.5),
blurRadius: 25,
spreadRadius: -5,
offset: const Offset(0, 10),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(28),
child: Stack(
children: [
// الخلفية الرئيسية
Container(color: const Color(0xFF003C43)),
// طبقة النقشة
SvgPicture.string(arabesquePattern, fit: BoxFit.cover),
// طبقة التأثير الزجاجي (Glassmorphism)
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0),
child: Container(color: Colors.black.withOpacity(0.1)),
),
// محتوى البطاقة
_buildCardContent(captainWalletController),
],
),
),
),
);
},
),
);
}
Widget _buildCardContent(CaptainWalletController captainWalletController) {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'محفظة انطلق',
style: AppStyle.headTitle.copyWith(
fontFamily: 'Amiri', // خط يوحي بالفخامة
color: Colors.white,
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
// أيقونة شريحة البطاقة
const Icon(Icons.sim_card_outlined,
color: Color(0xFFE7C582), size: 30),
],
),
Column(
children: [
Text(
'الرصيد الحالي'.tr,
style: AppStyle.title.copyWith(
color: Colors.white.withOpacity(0.7),
fontSize: 16,
),
),
const SizedBox(height: 4),
// استخدام AnimatedSwitcher لإضافة حركة عند تحديث الرصيد
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (child, animation) {
return FadeTransition(opacity: animation, child: child);
},
child: Text(
'${captainWalletController.totalAmountVisa} ${'ل.س'.tr}',
key:
ValueKey<String>(captainWalletController.totalAmountVisa),
style: AppStyle.headTitle2.copyWith(
color: const Color(0xFFE7C582), // Antique Gold
fontSize: 40,
fontWeight: FontWeight.w900,
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
box.read(BoxName.nameDriver).toString().split(' ')[0],
style: AppStyle.title.copyWith(
color: Colors.white.withOpacity(0.9),
fontSize: 16,
letterSpacing: 0.5,
),
),
Text(
"سحب الرصيد".tr,
style: AppStyle.title.copyWith(
color: Colors.white.withOpacity(0.9),
fontSize: 16,
),
),
],
),
],
),
);
}
void _showCashOutDialog(
BuildContext context, CaptainWalletController captainWalletController) {
double minAmount = 20.0; // الحد الأدنى للسحب
if (double.parse(captainWalletController.totalAmountVisa) >= minAmount) {
Get.defaultDialog(
barrierDismissible: false,
title: 'هل تريد سحب أرباحك؟'.tr,
titleStyle: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.account_balance_wallet,
color: AppColor.primaryColor, size: 30),
const SizedBox(height: 15),
Text(
'${'رصيدك الإجمالي:'.tr} ${captainWalletController.totalAmountVisa} ${'ل.س'.tr}',
style: AppStyle.title.copyWith(fontSize: 16),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'طريقة الدفع:'.tr,
style: AppStyle.title.copyWith(fontSize: 16),
),
const SizedBox(width: 10),
const MyDropDownSyria(),
],
),
const SizedBox(height: 20),
Form(
key: captainWalletController.formKey,
child: MyTextForm(
controller: captainWalletController.phoneWallet,
label: "أدخل رقم محفظتك".tr,
hint: "مثال: 0912345678".tr,
type: TextInputType.phone,
),
),
],
),
confirm: MyElevatedButton(
title: 'تأكيد'.tr,
onPressed: () async {
if (captainWalletController.formKey.currentState!.validate()) {
Get.back();
String amountAfterFee =
(double.parse(captainWalletController.totalAmountVisa) - 5)
.toStringAsFixed(0);
await Get.put(PaymobPayout()).payToWalletDriverAll(
amountAfterFee,
Get.find<SyrianPayoutController>().dropdownValue.toString(),
captainWalletController.phoneWallet.text.toString(),
);
}
},
kolor: AppColor.greenColor,
),
cancel: MyElevatedButton(
title: 'إلغاء'.tr,
onPressed: () {
Get.back();
},
kolor: AppColor.redColor,
));
} else {
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text("تنبيه".tr),
content: Text(
'${'المبلغ في محفظتك أقل من الحد الأدنى للسحب وهو'.tr} $minAmount ${'ل.س'.tr}',
),
actions: <Widget>[
CupertinoDialogAction(
isDefaultAction: true,
child: Text("موافق".tr),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
// هذا الكود من الملف الأصلي وهو ضروري لعمل الحوار
class MyDropDownSyria extends StatelessWidget {
const MyDropDownSyria({super.key});
@override
Widget build(BuildContext context) {
Get.put(SyrianPayoutController());
return GetBuilder<SyrianPayoutController>(builder: (controller) {
return DropdownButton<String>(
value: controller.dropdownValue,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple, fontSize: 16),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
controller.changeValue(newValue);
},
items: <String>['syriatel', 'mtn']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
);
});
}
}
// هذا المتحكم ضروري لعمل القائمة المنسدلة
class SyrianPayoutController extends GetxController {
String dropdownValue = 'syriatel';
void changeValue(String? newValue) {
if (newValue != null) {
dropdownValue = newValue;
update();
}
}
}

View File

@@ -0,0 +1,170 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/crud.dart';
import '../../../main.dart';
// --- ملاحظات هامة ---
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
// static const String payWithEcashDriver = "$server/payment/payWithEcashDriver.php";
//
// 2. تأكد من أنك تخزن 'driverId' في الـ box الخاص بك، مثلاً:
// box.read(BoxName.driverID)
/// دالة جديدة لبدء عملية الدفع للسائق عبر ecash
Future<void> payWithEcashDriver(BuildContext context, String amount) async {
try {
// يمكنك استخدام نفس طريقة التحقق بالبصمة إذا أردت
bool isAvailable = await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'Use Touch ID or Face ID to confirm payment'.tr,
);
if (didAuthenticate) {
// استدعاء الـ Endpoint الجديد على السيرفر الخاص بك
var res = await CRUD().postWallet(
link: AppLink.payWithEcashDriver,
payload: {
// أرسل البيانات التي يحتاجها السيرفر
"amount": amount,
// "driverId": box.read(BoxName.driverID), // تأكد من وجود هذا المتغير
"driverId": box.read(BoxName.driverID), // تأكد من وجود هذا المتغير
},
);
// التأكد من أن السيرفر أعاد رابط الدفع بنجاح
if (res != null &&
res['status'] == 'success' &&
res['message'] != null) {
final String paymentUrl = res['message'];
// الانتقال إلى شاشة الدفع الجديدة الخاصة بـ ecash للسائق
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
EcashDriverPaymentScreen(paymentUrl: paymentUrl),
),
);
} else {
// عرض رسالة خطأ في حال فشل السيرفر في إنشاء الرابط
Get.defaultDialog(
title: 'Error'.tr,
content: Text(
'Failed to initiate payment. Please try again.'.tr,
style: AppStyle.title,
),
);
}
}
}
} catch (e) {
Get.defaultDialog(
title: 'Error'.tr,
content: Text(
'An error occurred during the payment process.'.tr,
style: AppStyle.title,
),
);
}
}
/// شاشة جديدة ومبسطة خاصة بدفع السائقين عبر ecash
class EcashDriverPaymentScreen extends StatefulWidget {
final String paymentUrl;
const EcashDriverPaymentScreen({required this.paymentUrl, Key? key})
: super(key: key);
@override
State<EcashDriverPaymentScreen> createState() =>
_EcashDriverPaymentScreenState();
}
class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) {
print('Ecash Driver WebView URL Finished: $url');
// هنا نتحقق فقط من أن المستخدم عاد إلى صفحة النجاح
// لا حاجة لاستدعاء أي API هنا، فالـ Webhook يقوم بكل العمل
if (url.contains("success.php")) {
showProcessingDialog();
}
},
))
..loadRequest(Uri.parse(widget.paymentUrl));
}
// دالة لعرض رسالة "العملية قيد المعالجة"
void showProcessingDialog() {
showDialog(
barrierDismissible: false,
context: Get.context!,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
title: Row(
children: [
Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
Text(
"Payment Successful".tr,
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold,
),
),
],
),
content: Text(
"Your payment is being processed and your wallet will be updated shortly."
.tr,
style: const TextStyle(fontSize: 16),
),
actions: [
TextButton(
onPressed: () {
// أغلق مربع الحوار، ثم أغلق شاشة الدفع
Navigator.pop(context); // Close the dialog
Navigator.pop(context); // Close the payment screen
},
style: TextButton.styleFrom(
backgroundColor: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
child: Text(
"OK".tr,
style: const TextStyle(color: Colors.white),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Complete Payment'.tr)),
body: WebViewWidget(controller: _controller),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../controller/payment/driver_payment_controller.dart';
class PaymentHistoryDriverPage extends StatelessWidget {
const PaymentHistoryDriverPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: ListView.builder(
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int index) {
var list = controller.archive[index];
return Padding(
padding: const EdgeInsets.all(4),
child: Container(
decoration: BoxDecoration(
color: double.parse(list['amount']) < 0
? AppColor.redColor.withOpacity(.4)
: AppColor.greenColor.withOpacity(.4)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
list['amount'],
style: AppStyle.title,
),
Text(
list['created_at'],
style: AppStyle.title,
),
],
),
),
);
},
),
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,601 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/controller/payment/payment_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_textField.dart';
import 'ecash.dart';
class PointsCaptain extends StatelessWidget {
PaymentController paymentController = Get.put(PaymentController());
CaptainWalletController captainWalletController =
Get.put(CaptainWalletController());
PointsCaptain({
super.key,
required this.kolor,
required this.countPoint,
required this.pricePoint,
});
final Color kolor;
final String countPoint;
double pricePoint;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
Get.defaultDialog(
title: 'Which method you will pay'.tr,
titleStyle: AppStyle.title,
content: Column(
children: [
Text(
'${'you can buy '.tr}$countPoint ${'L.S'.tr}${'by '.tr}${'$pricePoint'.tr}',
style: AppStyle.title,
),
MyElevatedButton(
title: 'Pay with Credit Card'.tr,
onPressed: () async {
Get.back();
payWithEcashDriver(context, pricePoint.toString());
// var d = jsonDecode(res);
}, //51524
),
// Add some spacing between buttons
MyElevatedButton(
kolor: AppColor.redColor,
title: 'Pay with Wallet'.tr,
onPressed: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: paymentController.formKey,
child: MyTextForm(
controller:
paymentController.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: 'Insert Wallet phone number'.tr,
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (paymentController.formKey.currentState!
.validate()) {
box.write(
BoxName.phoneWallet,
paymentController
.walletphoneController.text);
await payWithMTNWallet(
context, pricePoint.toString(), 'SYP');
}
}));
},
),
],
));
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
child: Container(
width: Get.width * .22,
height: Get.width * .22,
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
kolor.withOpacity(0.3),
kolor,
kolor.withOpacity(0.7),
kolor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
border: Border.all(color: AppColor.accentColor),
borderRadius: BorderRadius.circular(12),
shape: BoxShape.rectangle,
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'$countPoint ${'L.S'.tr}',
style: AppStyle.subtitle
.copyWith(color: AppColor.secondaryColor),
),
Text(
'$pricePoint ${'L.S'.tr}',
style:
AppStyle.title.copyWith(color: AppColor.secondaryColor),
textAlign: TextAlign.center,
),
],
),
),
),
),
);
}
}
class PaymentScreen extends StatefulWidget {
final String iframeUrl;
final String countPrice;
const PaymentScreen(
{required this.iframeUrl, Key? key, required this.countPrice})
: super(key: key);
@override
State<PaymentScreen> createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
late final WebViewController _controller;
final controller = Get.find<CaptainWalletController>();
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) {
if (url.contains("success")) {
_fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع
} else if (url.contains("failed")) {
showCustomDialog(
title: "Error".tr,
message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم
isSuccess: false,
);
}
},
))
..loadRequest(Uri.parse(widget.iframeUrl));
}
Future<void> _fetchPaymentStatus() async {
final String userId = box.read(BoxName.phoneDriver);
await Future.delayed(const Duration(seconds: 2));
try {
final response = await CRUD().postWallet(
link: AppLink.paymetVerifyDriver,
payload: {
'user_id': userId,
'driverID': box.read(BoxName.driverID),
'paymentMethod': 'visa-in',
},
);
if (response != 'failure' && response != 'token_expired') {
if (response['status'] == 'success') {
final payment = response['message'];
final amount = payment['amount'].toString();
final bonus = payment['bonus'].toString();
final paymentID = payment['paymentID'].toString();
await controller.getCaptainWalletFromBuyPoints();
showCustomDialog(
title: "payment_success".tr,
message:
"${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}",
isSuccess: true,
);
} else {
showCustomDialog(
title: "transaction_failed".tr,
message: response['message'].toString(),
isSuccess: false,
);
}
} else {
showCustomDialog(
title: "connection_failed".tr,
message: response.toString(),
isSuccess: false,
);
}
} catch (e) {
showCustomDialog(
title: "server_error".tr,
message: "server_error_message".tr,
isSuccess: false,
);
}
}
void showCustomDialog({
required String title,
required String message,
required bool isSuccess,
}) {
showDialog(
barrierDismissible: false,
context: Get.context!,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
title: Row(
children: [
Icon(
isSuccess ? Icons.check_circle : Icons.error,
color: isSuccess ? Colors.green : Colors.red,
),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
color: isSuccess ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
],
),
content: Text(
message,
style: const TextStyle(fontSize: 16),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
style: TextButton.styleFrom(
backgroundColor: isSuccess ? Colors.green : Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
child: Text(
"OK",
style: const TextStyle(color: Colors.white),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('إتمام الدفع')),
body: WebViewWidget(controller: _controller),
);
}
}
class PaymentScreenWallet extends StatefulWidget {
final String iframeUrl;
final String countPrice;
const PaymentScreenWallet(
{required this.iframeUrl, Key? key, required this.countPrice})
: super(key: key);
@override
State<PaymentScreenWallet> createState() => _PaymentScreenWalletState();
}
class _PaymentScreenWalletState extends State<PaymentScreenWallet> {
late final WebViewController _controller;
final controller = Get.find<CaptainWalletController>();
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) {
if (url.contains("success")) {
_fetchPaymentStatus(); // ✅ استدعاء الويب هوك بعد نجاح الدفع
} else if (url.contains("failed")) {
showCustomDialog(
title: "Error".tr,
message: 'Payment Failed'.tr, // يتم جلب رسالة الخطأ من الخادم
isSuccess: false,
);
}
},
))
..loadRequest(Uri.parse(widget.iframeUrl));
}
Future<void> _fetchPaymentStatus() async {
final String userId = '+963' + box.read(BoxName.phoneWallet);
await Future.delayed(const Duration(seconds: 2));
try {
final response = await CRUD().postWallet(
link: AppLink.paymetVerifyDriver,
payload: {
'user_id': userId,
'driverID': box.read(BoxName.driverID),
'paymentMethod': 'visa-in',
},
);
if (response != 'failure' && response != 'token_expired') {
if (response['status'] == 'success') {
final payment = response['message'];
final amount = payment['amount'].toString();
final bonus = payment['bonus'].toString();
final paymentID = payment['paymentID'].toString();
await controller.getCaptainWalletFromBuyPoints();
showCustomDialog(
title: "payment_success".tr,
message:
"${"transaction_id".tr}: $paymentID\n${"amount_paid".tr}: $amount EGP\n${"bonus_added".tr}: $bonus ${"points".tr}",
isSuccess: true,
);
} else {
showCustomDialog(
title: "transaction_failed".tr,
message: response['message'].toString(),
isSuccess: false,
);
}
} else {
showCustomDialog(
title: "connection_failed".tr,
message: response.toString(),
isSuccess: false,
);
}
} catch (e) {
showCustomDialog(
title: "server_error".tr,
message: "server_error_message".tr,
isSuccess: false,
);
}
}
void showCustomDialog({
required String title,
required String message,
required bool isSuccess,
}) {
showDialog(
barrierDismissible: false,
context: Get.context!,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
title: Row(
children: [
Icon(
isSuccess ? Icons.check_circle : Icons.error,
color: isSuccess ? Colors.green : Colors.red,
),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
color: isSuccess ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
],
),
content: Text(
message,
style: const TextStyle(fontSize: 16),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
style: TextButton.styleFrom(
backgroundColor: isSuccess ? Colors.green : Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
child: Text(
"OK",
style: const TextStyle(color: Colors.white),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('إتمام الدفع')),
body: WebViewWidget(controller: _controller),
);
}
}
Future<void> payWithMTNWallet(
BuildContext context, String amount, String currency) async {
// استخدام مؤشر تحميل لتجربة مستخدم أفضل
Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false);
try {
String phone = box.read(BoxName.phoneWallet) ?? '963992952235';
String driverID = box.read(BoxName.driverID).toString();
String formattedAmount = double.parse(amount).toStringAsFixed(0);
print("🚀 بدء عملية دفع MTN");
print(
"📦 Payload: driverID: $driverID, amount: $formattedAmount, phone: $phone");
// التحقق من البصمة (اختياري)
bool isAuthSupported = await LocalAuthentication().isDeviceSupported();
if (isAuthSupported) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (!didAuthenticate) {
if (Get.isDialogOpen ?? false) Get.back();
print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
return;
}
}
// 1⃣ استدعاء mtn_start_payment.php (الملف الجديد)
var responseData = await CRUD().postWallet(
link: AppLink.payWithMTNStart,
payload: {
"amount": formattedAmount,
"passengerId": driverID,
"phone": phone,
},
);
print("✅ استجابة الخادم (mtn_start_payment.php):");
print(responseData);
// --- بداية التعديل المهم ---
// التحقق القوي من الاستجابة لتجنب الأخطاء
Map<String, dynamic> startRes;
if (responseData is Map<String, dynamic>) {
// إذا كانت الاستجابة بالفعل Map، استخدمها مباشرة
startRes = responseData;
} else if (responseData is String) {
// إذا كانت نص، حاول تحليلها كـ JSON
try {
startRes = json.decode(responseData);
} catch (e) {
throw Exception(
"فشل في تحليل استجابة الخادم. الاستجابة: $responseData");
}
} else {
// نوع غير متوقع
throw Exception("تم استلام نوع بيانات غير متوقع من الخادم.");
}
if (startRes['status'] != 'success') {
String errorMsg = startRes['message']?.toString() ??
"فشل بدء عملية الدفع. حاول مرة أخرى.";
throw Exception(errorMsg);
}
// --- نهاية التعديل المهم ---
// استخراج البيانات بأمان
final messageData = startRes["message"];
final invoiceNumber = messageData["invoiceNumber"].toString();
final operationNumber = messageData["operationNumber"].toString();
final guid = messageData["guid"].toString();
print(
"📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid");
if (Get.isDialogOpen ?? false)
Get.back(); // إغلاق مؤشر التحميل قبل عرض حوار OTP
// 2⃣ عرض واجهة إدخال OTP
String? otp = await showDialog<String>(
context: context,
builder: (context) {
String input = "";
return AlertDialog(
title: const Text("أدخل كود التحقق"),
content: TextField(
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: "كود OTP"),
onChanged: (val) => input = val,
),
actions: [
TextButton(
child: const Text("تأكيد"),
onPressed: () => Navigator.of(context).pop(input),
),
TextButton(
child: const Text("إلغاء"),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
if (otp == null || otp.isEmpty) {
print("❌ لم يتم إدخال OTP");
return;
}
print("🔐 تم إدخال OTP: $otp");
Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false);
// 3⃣ استدعاء mtn_confirm.php
var confirmRes = await CRUD().postWallet(
link: AppLink.payWithMTNConfirm,
payload: {
"invoiceNumber": invoiceNumber,
"operationNumber": operationNumber,
"guid": guid,
"otp": otp,
"phone": phone,
},
);
if (Get.isDialogOpen ?? false) Get.back();
print("✅ استجابة mtn_confirm.php:");
print(confirmRes);
if (confirmRes != null && confirmRes['status'] == 'success') {
Get.defaultDialog(
title: "✅ نجاح",
content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."),
);
} else {
String errorMsg =
confirmRes?['message']?.toString() ?? "فشل في تأكيد الدفع";
Get.defaultDialog(
title: "❌ فشل",
content: Text(errorMsg),
);
}
} catch (e, s) {
print("🔥 خطأ أثناء الدفع عبر MTN:");
print(e);
print(s);
if (Get.isDialogOpen ?? false) Get.back();
Get.defaultDialog(
title: 'حدث خطأ',
content: Text(e.toString().replaceFirst("Exception: ", "")),
);
}
}

View File

@@ -0,0 +1,145 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controller/home/payment/captain_wallet_controller.dart';
class TransferBudgetPage extends StatelessWidget {
const TransferBudgetPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(CaptainWalletController());
return MyScafolld(
title: "Transfer budget".tr,
body: [
GetBuilder<CaptainWalletController>(
builder: (captainWalletController) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .7,
width: double.infinity,
child: Form(
key: captainWalletController.formKeyTransfer,
child: Column(
children: [
const SizedBox(
height: 20,
),
MyTextForm(
controller: captainWalletController
.newDriverPhoneController,
label: 'phone number of driver'.tr,
hint: 'phone number of driver',
type: TextInputType.phone),
MyTextForm(
controller: captainWalletController
.amountFromBudgetController,
label: 'insert amount'.tr,
hint:
'${'You have in account'.tr} ${captainWalletController.totalAmountVisa}',
type: TextInputType.number),
captainWalletController.isNewTransfer
? const MyCircularProgressIndicator()
: captainWalletController
.amountToNewDriverMap.isEmpty
? MyElevatedButton(
title: 'Next'.tr,
onPressed: () async {
await captainWalletController
.detectNewDriverFromMyBudget();
})
: const SizedBox(),
captainWalletController.amountToNewDriverMap.isNotEmpty
? Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Container(
width: double.maxFinite,
decoration: AppStyle.boxDecoration1,
child: Text(
'Name :'.tr +
captainWalletController
.amountToNewDriverMap[0]['name']
.toString(),
textAlign: TextAlign.center,
style: AppStyle.title,
),
),
const SizedBox(
height: 5,
),
Container(
width: double.maxFinite,
decoration: AppStyle.boxDecoration1,
child: Text(
"${"NationalID".tr} ${captainWalletController.amountToNewDriverMap[0]['national_number']}",
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
const SizedBox(
height: 5,
),
Container(
width: double.maxFinite,
decoration: AppStyle.boxDecoration1,
child: Text(
"${"amount".tr} ${captainWalletController.amountFromBudgetController.text} ${'LE'.tr}",
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
const SizedBox(
height: 15,
),
captainWalletController
.amountToNewDriverMap.isNotEmpty
? MyElevatedButton(
title: 'Transfer'.tr,
onPressed: () async {
if (double.parse(
captainWalletController
.amountFromBudgetController
.text) <
double.parse(
captainWalletController
.totalAmountVisa) -
5) {
await captainWalletController
.addTransferDriversWallet(
'TransferFrom',
'TransferTo',
);
} else {
MyDialog().getDialog(
"You dont have money in your Wallet"
.tr,
"You dont have money in your Wallet or you should less transfer 5 LE to activate"
.tr, () {
Get.back();
});
}
})
: const SizedBox()
],
),
)
: const SizedBox()
],
)),
),
);
}),
],
isleading: true);
}
}

View File

@@ -0,0 +1,610 @@
import 'package:local_auth/local_auth.dart';
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/controller/functions/tts.dart';
import 'package:sefer_driver/views/home/my_wallet/payment_history_driver_page.dart';
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/info.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../controller/payment/driver_payment_controller.dart';
import '../../widgets/my_scafold.dart';
import 'card_wallet_widget.dart';
import 'points_captain.dart';
import 'transfer_budget_page.dart';
import 'weekly_payment_page.dart';
class WalletCaptainRefactored extends StatelessWidget {
WalletCaptainRefactored({super.key});
final CaptainWalletController captainWalletController =
Get.put(CaptainWalletController());
// دالة مساعدة لتحديد لون خلفية النقاط
Color _getPointsColor(String pointsStr) {
final points = double.tryParse(pointsStr) ?? 0.0;
if (points < -30000) {
return AppColor.redColor;
} else if (points < 0 && points >= -30000) {
return AppColor.yellowColor;
} else {
return AppColor.greenColor;
}
}
@override
Widget build(BuildContext context) {
captainWalletController.refreshCaptainWallet();
return MyScafolld(
title: 'Driver Wallet'.tr,
isleading: true,
action: IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => captainWalletController.refreshCaptainWallet(),
tooltip: 'Refresh'.tr,
),
body: [
GetBuilder<CaptainWalletController>(
builder: (controller) {
if (controller.isLoading) {
return const MyCircularProgressIndicator();
}
return SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildTotalPointsSection(context, controller),
const SizedBox(height: 16),
const CardSeferWalletDriver(), // This can be redesigned if needed
const SizedBox(height: 16),
_buildWalletDetailsCard(context, controller),
const SizedBox(height: 24),
_buildPromoSection(controller),
const SizedBox(height: 24),
_buildNavigationButtons(),
],
),
);
},
)
],
);
}
/// القسم العلوي لعرض النقاط الإجمالية
Widget _buildTotalPointsSection(
BuildContext context, CaptainWalletController controller) {
return Card(
elevation: 4,
color: _getPointsColor(controller.totalPoints.toString()),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: InkWell(
onTap: () {
showCupertinoDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text('Info'.tr),
content: Text(
'The 30000 points equal 30000 S.P for you \nSo go and gain your money'
.tr),
actions: [
CupertinoDialogAction(
child: Text("OK".tr),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Column(
children: [
Text(
'${'Total Points is'.tr} 💎',
style: AppStyle.headTitle2
.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
controller.totalPoints.toString(),
style: AppStyle.headTitle2.copyWith(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.w900),
textAlign: TextAlign.center,
),
if (double.parse(controller.totalPoints.toString()) < -30000)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: CupertinoButton(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 20),
onPressed: () {
// Add your charge account logic here
},
child: Text(
'Charge your Account'.tr,
style: TextStyle(
color: AppColor.redColor,
fontWeight: FontWeight.bold),
),
),
),
],
),
),
),
);
}
/// بطاقة لعرض تفاصيل المحفظة وخيارات الشراء
Widget _buildWalletDetailsCard(
BuildContext context, CaptainWalletController controller) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_BudgetInfoRow(
title: 'Total Budget from trips is '.tr,
amount: controller.totalAmount,
onTap: () {
Get.snackbar(
icon: InkWell(
onTap: () async => await Get.put(TextToSpeechController())
.speakText(
'This amount for all trip I get from Passengers'
.tr),
child: const Icon(Icons.headphones)),
'${'Total Amount:'.tr} ${controller.totalAmount} ${'S.P'.tr}',
'This amount for all trip I get from Passengers'.tr,
duration: const Duration(seconds: 6),
backgroundColor: AppColor.yellowColor,
snackPosition: SnackPosition.BOTTOM,
);
},
),
const Divider(height: 32),
_BudgetInfoRow(
title: 'Total Budget from trips by\nCredit card is '.tr,
amount: controller.totalAmountVisa,
onTap: () {
Get.snackbar(
icon: InkWell(
onTap: () async => await Get.find<TextToSpeechController>()
.speakText(
'This amount for all trip I get from Passengers and Collected For me in'
.tr +
' SAFAR Wallet'.tr),
child: const Icon(Icons.headphones),
),
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
'This amount for all trip I get from Passengers and Collected For me in'
.tr +
' ${AppInformation.appName} Wallet'.tr,
duration: const Duration(seconds: 6),
backgroundColor: AppColor.redColor,
snackPosition: SnackPosition.BOTTOM,
);
},
),
const SizedBox(height: 24),
_buildBuyPointsButton(controller),
const SizedBox(height: 16),
// _buildTransferBudgetButton(controller), // Uncomment if needed
_buildPurchaseInstructions(),
const SizedBox(height: 8),
_buildPointsOptions(),
],
),
),
);
}
/// قسم العروض الترويجية
Widget _buildPromoSection(CaptainWalletController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Today's Promo".tr, style: AppStyle.headTitle),
const SizedBox(height: 10),
_PromoProgressCard(
title: 'Morning Promo'.tr,
timePromo: 'Morning Promo',
count: (controller.walletDate['message'][0]['morning_count']),
maxCount: 5,
description:
"this is count of your all trips in the morning promo today from 7:00am to 10:00am"
.tr,
controller: controller,
),
const SizedBox(height: 16),
_PromoProgressCard(
title: 'Afternoon Promo'.tr,
timePromo: 'Afternoon Promo',
count: (controller.walletDate['message'][0]['afternoon_count']),
maxCount: 5,
description:
"this is count of your all trips in the Afternoon promo today from 3:00pm to 6:00 pm"
.tr,
controller: controller,
),
],
);
}
/// أزرار التنقل السفلية
Widget _buildNavigationButtons() {
return Row(
children: [
Expanded(
child: MyElevatedButton(
kolor: AppColor.blueColor,
title: 'Payment History'.tr,
onPressed: () async {
await Get.put(DriverWalletHistoryController())
.getArchivePayment();
Get.to(() => const PaymentHistoryDriverPage(),
transition: Transition.size);
},
),
),
const SizedBox(width: 16),
Expanded(
child: MyElevatedButton(
kolor: AppColor.blueColor,
title: 'Weekly Budget'.tr,
onPressed: () async {
await Get.put(DriverWalletHistoryController())
.getWeekllyArchivePayment();
Get.to(() => const WeeklyPaymentPage(),
transition: Transition.size);
},
),
),
],
);
}
// --- حافظت على هذه الدوال كما هي لأنها تحتوي على منطق مهم ---
Widget _buildBuyPointsButton(CaptainWalletController controller) {
return MyElevatedButton(
title: 'You can buy points from your budget'.tr,
onPressed: () {
Get.defaultDialog(
title: 'Pay from my budget'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.amountFromBudgetController,
label:
'${'You have in account'.tr} ${controller.totalAmountVisa}',
hint: '${'You have in account'.tr} ${controller.totalAmountVisa}',
type: TextInputType.number,
),
),
confirm: MyElevatedButton(
title: 'Pay'.tr,
onPressed: () async {
bool isAvailable =
await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
// Authenticate the user
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason:
'Use Touch ID or Face ID to confirm payment'.tr,
options: const AuthenticationOptions(
biometricOnly: true,
sensitiveTransaction: true,
));
if (didAuthenticate) {
if (double.parse(controller.amountFromBudgetController.text) <
double.parse(controller.totalAmountVisa)) {
await controller.payFromBudget();
} else {
Get.back();
mySnackeBarError('Your Budget less than needed'.tr);
}
} else {
// Authentication failed, handle accordingly
MyDialog().getDialog('Authentication failed'.tr, ''.tr, () {
Get.back();
});
}
} else {
MyDialog().getDialog('Biometric Authentication'.tr,
'You should use Touch ID or Face ID to confirm payment'.tr,
() {
Get.back();
});
}
},
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () {
Get.back();
},
),
);
},
);
}
Widget _buildPurchaseInstructions() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.accentColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.accentColor.withOpacity(0.3)),
),
child: Column(
children: [
Text(
"You can purchase a budget to enable online access through the options listed below"
.tr,
textAlign: TextAlign.center,
style: AppStyle.title.copyWith(fontSize: 14),
),
],
),
),
);
}
Widget _buildPointsOptions() {
return SizedBox(
height: Get.height * 0.19,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
PointsCaptain(
kolor: AppColor.greyColor,
pricePoint: 10000,
countPoint: '10000'),
PointsCaptain(
kolor: AppColor.bronze, pricePoint: 20000, countPoint: '21000'),
PointsCaptain(
kolor: AppColor.goldenBronze,
pricePoint: 40000,
countPoint: '45000'),
PointsCaptain(
kolor: AppColor.gold, pricePoint: 100000, countPoint: '110000'),
],
),
);
}
}
/// ويدجت مُحسّن لعرض صف معلومات الرصيد
class _BudgetInfoRow extends StatelessWidget {
final String title;
final String amount;
final VoidCallback onTap;
const _BudgetInfoRow({
required this.title,
required this.amount,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
title,
style: AppStyle.title.copyWith(fontSize: 16),
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
color: AppColor.accentColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Text(
'$amount ${'S.P'.tr}',
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
],
),
),
);
}
}
/// ويدجت مُحسّن لعرض بطاقة العرض الترويجي
class _PromoProgressCard extends StatelessWidget {
final String title;
final String timePromo;
final int count;
final int maxCount;
final String description;
final CaptainWalletController controller;
const _PromoProgressCard({
required this.title,
required this.timePromo,
required this.count,
required this.maxCount,
required this.description,
required this.controller,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
MyDialog().getDialog(title, description, () async {
if (count >= maxCount) {
controller.addDriverWalletFromPromo(timePromo, 50);
}
Get.back();
});
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title,
style:
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Text(
'$count / $maxCount',
style: AppStyle.title.copyWith(color: AppColor.blueColor),
),
],
),
const SizedBox(height: 12),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
minHeight: 12,
value: count / maxCount,
backgroundColor: AppColor.accentColor.withOpacity(0.2),
color: count >= maxCount
? AppColor.greenColor
: AppColor.blueColor,
),
),
],
),
),
),
);
}
}
// --- الدوال والويدجتس الخاصة بالدفع في سوريا تبقى كما هي ---
// This function is a placeholder for adding Syrian payment methods.
// You would implement the UI and logic for mobile wallets or other local options here.
Future<dynamic> addSyrianPaymentMethod(
CaptainWalletController captainWalletController) {
return Get.defaultDialog(
title: "Insert Payment Details".tr,
content: Form(
key: captainWalletController.formKeyAccount,
child: Column(
children: [
Text(
"Insert your mobile wallet details to receive your money weekly"
.tr),
MyTextForm(
controller: captainWalletController
.cardBank, // Re-using for mobile number
label: "Insert mobile wallet number".tr,
hint: '0912 345 678',
type: TextInputType.phone),
const SizedBox(
height: 10,
),
MyDropDownSyria() // Dropdown for Syrian providers
],
)),
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () async {
if (captainWalletController.formKeyAccount.currentState!
.validate()) {
Get.back();
// Replace with your actual API endpoint and payload for Syria
var res =
await CRUD().post(link: AppLink.updateAccountBank, payload: {
"paymentProvider":
Get.find<SyrianPayoutController>().dropdownValue.toString(),
"accountNumber":
captainWalletController.cardBank.text.toString(),
"id": box.read(BoxName.driverID).toString()
});
print('res: $res');
if (res != 'failure') {
mySnackbarSuccess('Payment details added successfully'.tr);
}
}
}));
}
// A new GetX controller for the Syrian payout dropdown
class SyrianPayoutController extends GetxController {
String dropdownValue = 'syriatel';
void changeValue(String? newValue) {
if (newValue != null) {
dropdownValue = newValue;
update();
}
}
}
// A new Dropdown widget for Syrian mobile wallet providers
class MyDropDownSyria extends StatelessWidget {
const MyDropDownSyria({super.key});
@override
Widget build(BuildContext context) {
Get.put(SyrianPayoutController());
return GetBuilder<SyrianPayoutController>(builder: (controller) {
return DropdownButton<String>(
value: controller.dropdownValue,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
isExpanded: true,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
controller.changeValue(newValue);
},
items: <String>['syriatel', 'mtn']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
);
});
}
}

View File

@@ -0,0 +1,141 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:intl/intl.dart';
import '../../../controller/payment/driver_payment_controller.dart';
class WeeklyPaymentPage extends StatelessWidget {
const WeeklyPaymentPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: Column(
children: [
Container(
width: Get.width * .8,
decoration: AppStyle.boxDecoration1,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
controller.weeklyList.isEmpty
? '0'
: controller.weeklyList[0]
['totalAmount']
.toString(),
style: AppStyle.number,
),
),
),
),
Text(
' Total weekly is '.tr,
style: AppStyle.title,
),
],
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: SizedBox(
height: Get.height * .75,
child: controller.weeklyList.isNotEmpty
? ListView.builder(
itemCount: controller.weeklyList.length,
itemBuilder:
(BuildContext context, int index) {
var list = controller.weeklyList[index];
return Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
Card(
elevation: 2,
color: list['paymentMethod'] ==
'visa'
? AppColor.blueColor
: AppColor.secondaryColor,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Text(
list['paymentMethod'] ==
'Remainder'
? 'Remainder'.tr
: list['paymentMethod'] ==
'fromBudget'
? 'fromBudget'.tr
: list[
'paymentMethod'],
style: AppStyle.title,
),
),
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Card(
child: Padding(
padding:
const EdgeInsets.all(
8.0),
child: Text(
list['amount'],
style: AppStyle.number,
),
),
),
Text(
DateFormat(
'yyyy-MM-dd hh:mm a')
.format(DateTime.parse(
list[
'dateUpdated'])),
style: AppStyle.number,
),
],
),
],
),
),
),
);
},
)
: const SizedBox(),
),
),
],
),
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,33 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/auth/onboarding_controller.dart';
import 'package:sefer_driver/onbording_page.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class OnBoardingPage extends StatelessWidget {
OnBoardingControllerImp onBoardingControllerImp =
Get.put(OnBoardingControllerImp());
OnBoardingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.secondaryColor,
body: SafeArea(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
SizedBox(
height: Get.height * .7,
child: const CustomSliderOnBoarding(),
),
const CustomDotControllerOnBoarding(),
// const Spacer(flex: 2),
const SizedBox(height: 20),
MyElevatedButton(
onPressed: () => onBoardingControllerImp.next(),
title: 'Next'.tr,
)
]),
));
}
}

View File

@@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controller/home/captin/behavior_controller.dart';
class BehaviorPage extends StatelessWidget {
const BehaviorPage({
super.key,
});
@override
Widget build(BuildContext context) {
final controller = Get.put(DriverBehaviorController());
controller.fetchDriverBehavior();
return Scaffold(
appBar: AppBar(
title: Text('Driver Behavior'.tr),
centerTitle: true,
),
body: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text("Overall Behavior Score",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Text(
"${controller.overallScore.value.toStringAsFixed(1)} / 100",
style: const TextStyle(
fontSize: 28, color: Colors.blue)),
],
),
),
),
const SizedBox(height: 20),
const Text("Last 10 Trips",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: controller.lastTrips.length,
itemBuilder: (context, index) {
var trip = controller.lastTrips[index];
return Card(
elevation: 3,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Text("${index + 1}",
style: const TextStyle(color: Colors.white)),
),
title: Text("Trip ID: ${trip['trip_id']}"),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Behavior Score: ${trip['behavior_score']}"),
Text("Max Speed: ${trip['max_speed']} km/h"),
Text("Hard Brakes: ${trip['hard_brakes']}"),
Text("Distance: ${trip['total_distance']} km"),
],
),
),
);
},
),
],
),
);
}),
);
}
}

View File

@@ -0,0 +1,130 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../auth/captin/driver_car_controller.dart';
import '../../widgets/elevated_btn.dart';
import 'cars_inserting_page.dart';
class CaptainsCars extends StatelessWidget {
const CaptainsCars({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverCarController());
return MyScafolld(
title: "Add new car".tr,
body: [
Column(
children: [
MyElevatedButton(
title: "Add new car".tr,
onPressed: () async {
Get.to(() => CarsInsertingPage());
},
),
Expanded(
child: GetBuilder<DriverCarController>(
builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: ListView.builder(
itemCount: controller.cars.length,
itemBuilder: (context, index) {
final car = controller.cars[index];
return Padding(
padding: const EdgeInsets.all(4.0),
child: Card(
color: car['isDefault'].toString() == '0'
? AppColor.accentColor
: AppColor.blueColor,
elevation: 2,
child: ListTile(
leading: Icon(
Fontisto.car,
size: 50,
color: Color(int.parse(car['color_hex']
.replaceFirst('#', '0xff'))),
),
title: Text(
car['make'],
style: AppStyle.title,
), // Assuming `make` is a field in each car item
subtitle: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
car['model'],
style: AppStyle.title,
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: AppColor.blueColor)),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4),
child: Text(
(car['car_plate']),
style: AppStyle.title,
),
),
),
Text(
car['year'],
style: AppStyle.title,
),
],
), // Assuming `model` is a field in each car item
// trailing: IconButton(
// icon: const Icon(
// Icons.delete,
// color: AppColor.redColor,
// ),
// onPressed: () {
// // Add logic here to remove a car
// MyDialog()
// .getDialog('Are you sure to delete this car', '', () {
// controller
// .removeCar(car['id'].toString());
// });
// },
// ),
onTap: () {
MyDialog().getDialog(
'Are you sure to make this car as default'
.tr,
'', () {
Get.back();
//make it default
controller.updateCarRegistration(
car['id'].toString(),
box.read(BoxName.driverID).toString(),
);
});
// Add logic to view or edit the car details
},
),
),
);
},
);
},
),
),
],
)
],
isleading: true);
}
}

View File

@@ -0,0 +1,300 @@
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/gemeni.dart';
import '../../auth/captin/driver_car_controller.dart';
class CarsInsertingPage extends StatelessWidget {
CarsInsertingPage({super.key});
final driverCarController = Get.put(DriverCarController());
@override
Widget build(BuildContext context) {
Get.put(AI());
return MyScafolld(
title: 'Insert New Car'.tr,
body: [
Container(
color: AppColor.accentColor.withOpacity(.2),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
egyptCarLicenceFront(),
egyptCarLicenceBack(),
const SizedBox(height: 10),
Text('Please make sure to read the license carefully.'.tr),
Text(
'If your car license has the new design, upload the front side with two images.'
.tr),
const SizedBox(height: 10),
MyElevatedButton(
title: 'Please upload this license.'.tr,
onPressed: () {
final aiFront =
Get.find<AI>().responseIdCardDriverEgyptFront;
final aiBack =
Get.find<AI>().responseIdCardDriverEgyptBack;
driverCarController.addCarsForDrivers(
aiBack['chassis'].toString(),
aiBack['car_plate'].toString(),
aiBack['make'].toString(),
aiBack['model'].toString(),
aiBack['year'].toString(),
aiFront['LicenseExpirationDate'].toString(),
aiBack['color'].toString(),
aiBack['color_hex'].toString(),
aiFront['address'].toString(),
aiFront['owner'].toString(),
aiBack['inspection_date'].toString(),
aiBack['engine'].toString(),
aiBack['fuel'].toString(),
);
})
],
),
),
)
],
isleading: true);
}
}
GetBuilder<AI> egyptCarLicenceFront() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.responseIdCardDriverEgyptFront.isNotEmpty) {
// No need to access ai.responseIdCardDriverEgyptBack anymore
final licenseExpiryDate = DateTime.parse(
ai.responseIdCardDriverEgyptFront['LicenseExpirationDate']);
// Check if license has expired
final today = DateTime.now();
final isLicenseExpired = licenseExpiryDate.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('Vehicle Details Front'.tr,
style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
ai.allMethodForAI((ai.prompts[3]['prompt'].toString()),
AppLink.uploadEgypt, 'car_front');
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
// Removed Make, Model, etc. as they are not available
Text(
'${'Plate Number'.tr}: ${ai.responseIdCardDriverEgyptFront['car_plate']}',
),
const SizedBox(height: 8.0),
Text(
'${'Owner Name'.tr}: ${ai.responseIdCardDriverEgyptFront['owner']}',
),
const SizedBox(height: 8.0),
Text(
'${'Address'.tr}: ${ai.responseIdCardDriverEgyptFront['address']}',
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'License Expiry Date'.tr}: ${licenseExpiryDate.toString().substring(0, 10)}',
style: TextStyle(
color: isLicenseExpired ? Colors.red : Colors.green,
),
),
// Removed Fuel as it's not available
],
),
// Removed Inspection Date as it's not available
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
ai.allMethodForAINewCar((ai.prompts[3]['prompt'].toString()),
AppLink.uploadEgypt1, 'car_front', 'carId'); //todo
},
child: Column(
children: [
Image.asset(
'assets/images/3.png',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your car license front '.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<AI> egyptCarLicenceBack() {
return GetBuilder<AI>(
builder: (ai) {
if (ai.responseIdCardDriverEgyptBack.isNotEmpty) {
// Get the tax expiry date from the response
final taxExpiryDate =
ai.responseIdCardDriverEgyptBack['tax_expiry'].toString();
// final displacement = ai.responseIdCardDriverEgyptBack['displacement'];
// if (int.parse(displacement) < 1000) {}
// Get the inspection date from the response
final inspectionDate =
ai.responseIdCardDriverEgyptBack['inspection_date'].toString();
final year = int.parse(inspectionDate.toString().split('-')[0]);
// Set inspectionDateTime to December 31st of the given year
final inspectionDateTime = DateTime(year, 12, 31);
String carBackLicenseExpired =
inspectionDateTime.toString().split(' ')[0];
// Get the current date
final today = DateTime.now();
// Try parsing the tax expiry date. If it fails, set it to null.
final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? '');
final isExpired =
taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today);
// Check if the inspection date is before today
bool isInspectionExpired = inspectionDateTime.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Vehicle Details Back'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
ai.allMethodForAI((ai.prompts[4]['prompt'].toString()),
AppLink.uploadEgypt, 'car_back');
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Make'.tr}: ${ai.responseIdCardDriverEgyptBack['make']}'),
Text(
'${'Model'.tr}: ${ai.responseIdCardDriverEgyptBack['model']}'),
],
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Year'.tr}: ${ai.responseIdCardDriverEgyptBack['year']}'),
Text(
'${'Chassis'.tr}: ${ai.responseIdCardDriverEgyptBack['chassis']}'),
],
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Color'.tr}: ${ai.responseIdCardDriverEgyptBack['color']}'),
Text(
'${'Displacement'.tr}: ${ai.responseIdCardDriverEgyptBack['displacement']} cc'),
],
),
const SizedBox(height: 8.0),
Text(
'${'Fuel'.tr}: ${ai.responseIdCardDriverEgyptBack['fuel']}'),
const SizedBox(height: 8.0),
if (taxExpiryDateTime != null)
Text(
'${'Tax Expiry Date'.tr}: $taxExpiryDate',
style: TextStyle(
color: isExpired ? Colors.red : Colors.green,
),
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Inspection Date'.tr}: $carBackLicenseExpired',
style: TextStyle(
color: isInspectionExpired ? Colors.red : Colors.green,
),
),
],
),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
ai.allMethodForAI((ai.prompts[4]['prompt'].toString()),
AppLink.uploadEgypt, 'car_back');
},
child: Column(
children: [
Image.asset(
'assets/images/4.png',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your car license back'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/home/profile/feed_back_controller.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../widgets/elevated_btn.dart';
class FeedBackPage extends StatelessWidget {
FeedBackPage({super.key});
FeedBackController feedBackController = Get.put(FeedBackController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Feed Back'.tr,
body: [
Padding(
padding: const EdgeInsets.all(26),
child: Form(
key: feedBackController.formKey,
child: Column(
children: [
TextFormField(
controller: feedBackController.feedbackController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: 'Enter your feedback here'.tr,
labelText: 'Feedback',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your feedback.';
}
return null;
},
),
const SizedBox(height: 20),
feedBackController.isLoading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () {
if (feedBackController.formKey.currentState!
.validate()) {
feedBackController.addFeedBack();
// Clear the feedback form
feedBackController.formKey.currentState!.reset();
}
},
title: 'Submit '.tr,
),
],
),
),
),
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,303 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/profile/profile_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../controller/functions/log_out.dart';
class PassengerProfilePage extends StatelessWidget {
PassengerProfilePage({super.key});
LogOutController logOutController = Get.put(LogOutController());
@override
Widget build(BuildContext context) {
Get.put(ProfileController());
return MyScafolld(
isleading: true,
title: 'My Profile'.tr,
body: [
GetBuilder<ProfileController>(
builder: (controller) => controller.isloading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: SizedBox(
height: Get.height,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Edit Profile'.tr,
style: AppStyle.headTitle2,
),
ListTile(
title: Text(
'Name'.tr,
style: AppStyle.title,
),
leading: const Icon(
Icons.person_pin_rounded,
size: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
'${controller.prfoileData['first_name']} ${controller.prfoileData['last_name']}'),
onTap: () {
controller.updatField(
'first_name', TextInputType.name);
},
),
ListTile(
title: Text(
'Gender'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/gender.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
controller.prfoileData['gender'].toString()),
onTap: () {
Get.defaultDialog(
title: 'Update Gender'.tr,
content: Column(
children: [
GenderPicker(),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'gender': controller.gender,
});
Get.back();
},
)
],
));
// controller.updatField('gender');
},
),
ListTile(
title: Text(
'Education'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/education.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller.prfoileData['education']
.toString()),
onTap: () {
Get.defaultDialog(
barrierDismissible: true,
title: 'Update Education'.tr,
content: SizedBox(
height: 200,
child: Column(
children: [
EducationDegreePicker(),
],
),
),
confirm: MyElevatedButton(
title: 'Update Education'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'education':
controller.selectedDegree,
});
Get.back();
},
));
},
),
ListTile(
title: Text(
'Employment Type'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/employmentType.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['employmentType']
.toString()),
onTap: () {
controller.updatField(
'employmentType', TextInputType.name);
},
),
ListTile(
title: Text(
'Marital Status'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/maritalStatus.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['maritalStatus']
.toString()),
onTap: () {
controller.updatField(
'maritalStatus', TextInputType.name);
},
),
ListTile(
title: Text(
'SOS Phone'.tr,
style: AppStyle.title,
),
leading: const Icon(
Icons.sos,
color: AppColor.redColor,
size: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller.prfoileData['sosPhone']
.toString()),
onTap: () async {
await controller.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
controller.prfoileData['sosPhone']);
},
),
// const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Sign Out'.tr,
onPressed: () {
LogOutController().logOutPassenger();
}),
GetBuilder<LogOutController>(
builder: (logOutController) {
return MyElevatedButton(
title: 'Delete My Account'.tr,
onPressed: () {
Get.defaultDialog(
title:
'Are you sure to delete your account?'
.tr,
content: Form(
key: logOutController.formKey1,
child: MyTextForm(
controller: logOutController
.emailTextController,
label: 'Type your Email'.tr,
hint: 'Type your Email'.tr,
type:
TextInputType.emailAddress,
),
),
confirm: MyElevatedButton(
title: 'Delete My Account'.tr,
kolor: AppColor.redColor,
onPressed: () async {
await logOutController
.deletePassengerAccount();
}),
cancel: MyElevatedButton(
title: 'No I want'.tr,
onPressed: () {
logOutController
.emailTextController
.clear();
logOutController.update();
Get.back();
}));
});
}),
],
),
],
),
),
),
)),
],
);
}
}
class GenderPicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> genderOptions = ['Male'.tr, 'Female'.tr, 'Other'.tr];
GenderPicker({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
child: CupertinoPicker(
itemExtent: 32.0,
onSelectedItemChanged: (int index) {
controller.setGender(genderOptions[index]);
},
children: genderOptions.map((String value) {
return Text(value);
}).toList(),
),
);
}
}
class EducationDegreePicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> degreeOptions = [
'High School Diploma'.tr,
'Associate Degree'.tr,
'Bachelor\'s Degree'.tr,
'Master\'s Degree'.tr,
'Doctoral Degree'.tr,
];
EducationDegreePicker({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
child: CupertinoPicker(
// backgroundColor: AppColor.accentColor,
// looping: true,
squeeze: 2,
// diameterRatio: 5,
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setDegree(degreeOptions[index]);
},
children: degreeOptions.map((String value) {
return Text(value);
}).toList(),
),
);
}
}

View File

@@ -0,0 +1,377 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/profile/captain_profile_controller.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../my_wallet/walet_captain.dart';
import 'behavior_page.dart';
import 'captains_cars.dart';
class ProfileCaptain extends StatelessWidget {
ProfileCaptain({super.key});
CaptainWalletController captainWalletController = CaptainWalletController();
@override
Widget build(BuildContext context) {
Get.put(CaptainProfileController());
return MyScafolld(
title: 'My Profile'.tr,
body: [
GetBuilder<CaptainProfileController>(
builder: (controller) => Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Center(
child: controller.isLoading
? const MyCircularProgressIndicator()
: Column(
children: [
Material(
elevation: 2,
borderRadius: BorderRadius.circular(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () async {
// addBankCodeEgypt(captainWalletController);
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Add bank Account'.tr,
style: AppStyle.title,
),
),
),
InkWell(
onTap: () async {
Get.to(() => BehaviorPage());
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Show behavior page'.tr,
style: AppStyle.title,
),
),
),
],
),
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Show my Cars'.tr,
onPressed: () async {
Get.to(() => CaptainsCars());
},
),
// const SizedBox(height: 15),
MyElevatedButton(
title: 'Add criminal page'.tr,
onPressed: () async {
Get.to(() => CriminalDocumemtPage());
},
),
],
),
SizedBox(
height: Get.height * .7,
child: DriverProfileCard(
driverId:
controller.captainProfileData['driverID'] ??
'',
name:
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}',
phoneNumber:
controller.captainProfileData['phone'] ?? '',
email:
controller.captainProfileData['email'] ?? '',
birthdate: controller
.captainProfileData['birthdate'] is String
? controller.captainProfileData['birthdate']
: '',
gender: controller.captainProfileData['gender']
is String
? controller.captainProfileData['gender']
: '',
education: controller
.captainProfileData['education'] is String
? controller.captainProfileData['education']
: '',
carMake:
controller.captainProfileData['make'] ?? '',
carModel:
controller.captainProfileData['model'] ?? '',
carPlate:
controller.captainProfileData['car_plate'] ??
'',
carColor:
controller.captainProfileData['color'] ?? '',
vin: controller.captainProfileData['vin'] ?? '',
registrationDate: controller.captainProfileData[
'registration_date'] ??
'',
expirationDate: controller
.captainProfileData['expiration_date'] ??
'',
ratingCount: int.tryParse(controller
.captainProfileData['ratingCount']
.toString()) ??
0,
ratingDriver: controller
.captainProfileData['ratingDriver'] !=
null
? double.tryParse(controller
.captainProfileData['ratingDriver']
.toString()) ??
0
: null,
age: int.tryParse(controller
.captainProfileData['age']
.toString()) ??
0,
),
),
],
),
),
),
),
)
],
isleading: true,
);
}
}
class DriverProfileCard extends StatelessWidget {
final String driverId;
final String name;
final String phoneNumber;
final String email;
final String birthdate;
final String gender;
final String education;
final String carMake;
final String carModel;
final String carPlate;
final String carColor;
final String vin;
final String registrationDate;
final String expirationDate;
final int ratingCount;
final double? ratingDriver;
final int age;
DriverProfileCard({
required this.driverId,
required this.name,
required this.phoneNumber,
required this.email,
required this.birthdate,
required this.gender,
required this.education,
required this.carMake,
required this.carModel,
required this.carPlate,
required this.carColor,
required this.vin,
required this.registrationDate,
required this.expirationDate,
required this.ratingCount,
required this.ratingDriver,
required this.age,
});
@override
Widget build(BuildContext context) {
return Container(
// elevation: 8,
decoration: AppStyle.boxDecoration1,
margin: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
style: AppStyle.title,
name,
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.phone),
const SizedBox(width: 8),
Text(style: AppStyle.title, phoneNumber),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'birthdate'.tr} : $birthdate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.wc),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'gender'.tr} : $gender',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.school),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'education'.tr} : $education',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.car_repair),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'Make'.tr} : $carMake',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.model_training),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_model'.tr} : $carModel',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.drive_eta),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_plate'.tr} : $carPlate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.color_lens),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_color'.tr} : $carColor',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.confirmation_number),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'vin'.tr} : $vin',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'registration_date'.tr} : $registrationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'expiration_date'.tr} : $expirationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'rating_count'.tr} : $ratingCount',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star_rate),
const SizedBox(width: 8),
ratingDriver != null
? Text(
style: AppStyle.title,
'${'rating_driver'.tr} : $ratingDriver',
)
: Text(
style: AppStyle.title,
'${'rating_driver'.tr} : 0',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.person),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'age'.tr} : $age',
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,112 @@
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/home/profile/promos_controller.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../widgets/mycircular.dart';
class PromosPassengerPage extends StatelessWidget {
const PromosPassengerPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(PromosController());
return MyScafolld(
title: 'Promos For today'.tr,
isleading: true,
body: [
GetBuilder<PromosController>(
builder: (orderHistoryController) => orderHistoryController.isLoading
? const MyCircularProgressIndicator()
: ListView.builder(
itemCount: orderHistoryController.promoList.length,
itemBuilder: (BuildContext context, int index) {
final rides = orderHistoryController.promoList[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: AppColor.secondaryColor,
boxShadow: [
BoxShadow(
color: AppColor.accentColor,
offset: Offset(-3, -3),
blurRadius: 0,
spreadRadius: 0,
blurStyle: BlurStyle.outer),
BoxShadow(
color: AppColor.accentColor,
offset: Offset(3, 3),
blurRadius: 0,
spreadRadius: 0,
blurStyle: BlurStyle.outer)
]),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
AnimatedTextKit(
animatedTexts: [
ScaleAnimatedText(rides['promo_code'],
textStyle: AppStyle.title),
WavyAnimatedText(rides['promo_code'],
textStyle: AppStyle.title),
FlickerAnimatedText(
rides['promo_code'],
textStyle: AppStyle.title),
WavyAnimatedText(rides['promo_code'],
textStyle: AppStyle.title),
],
isRepeatingAnimation: true,
onTap: () {},
),
Text(
rides['description'],
style: AppStyle.title,
),
],
),
Column(
children: [
Text(
rides['validity_start_date'],
style: AppStyle.title,
),
Text(
rides['validity_end_date'],
style: AppStyle.title,
),
],
),
],
),
Text(
'Copy this Promo to use it in your Ride!'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle2
.copyWith(color: AppColor.accentColor),
)
],
),
),
),
);
},
),
)
],
);
}
}

View File

@@ -0,0 +1,88 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
class TaarifPage extends StatelessWidget {
const TaarifPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(isleading: true, title: 'Tariffs'.tr, body: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ListView(
// mainAxisAlignment: MainAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.stretch,
clipBehavior: Clip.hardEdge,
children: [
Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
border: TableBorder.symmetric(),
textBaseline: TextBaseline.alphabetic,
children: [
TableRow(
// decoration: AppStyle.boxDecoration,
children: [
Text('Minimum fare'.tr, style: AppStyle.title),
box.read(BoxName.countryCode) == 'Jordan'
? Text('1 ${'JOD'.tr}', style: AppStyle.title)
: Text('20 ${'LE'.tr}', style: AppStyle.title),
],
),
TableRow(
children: [
Text('Maximum fare'.tr, style: AppStyle.title),
box.read(BoxName.countryCode) == 'Jordan'
? Text('200 ${'JOD'.tr}', style: AppStyle.title)
: Text('15000 ${'LE'.tr}', style: AppStyle.title),
],
),
TableRow(
children: [
Text('Flag-down fee'.tr, style: AppStyle.title),
box.read(BoxName.countryCode) == 'Jordan'
? Text('0.47 ${'JOD'.tr}', style: AppStyle.title)
: Text('15 ${'LE'.tr}', style: AppStyle.title),
],
),
TableRow(
children: [
box.read(BoxName.countryCode) == 'Jordan'
? Text('0.05 ${'JOD'.tr}/min and 0.21 ${'JOD'.tr}/km',
style: AppStyle.title)
: Text('1 ${'LE'.tr}/min and 4 ${'LE'.tr}/km',
style: AppStyle.title),
Text('Including Tax'.tr, style: AppStyle.title),
],
),
],
),
const SizedBox(height: 10),
Text('BookingFee'.tr, style: AppStyle.headTitle2),
const SizedBox(height: 10),
Text('10%', style: AppStyle.title),
const SizedBox(height: 20),
Text('Morning'.tr, style: AppStyle.headTitle2),
const SizedBox(height: 10),
Text(
'from 07:30 till 10:30 (Thursday, Friday, Saturday, Monday)'.tr,
style: AppStyle.title),
const SizedBox(height: 20),
Text('Evening'.tr, style: AppStyle.headTitle2),
const SizedBox(height: 10),
Text(
'from 12:00 till 15:00 (Thursday, Friday, Saturday, Monday)'.tr,
style: AppStyle.title),
const SizedBox(height: 20),
Text('Night'.tr, style: AppStyle.headTitle2),
const SizedBox(height: 10),
Text('from 23:59 till 05:30'.tr, style: AppStyle.title),
],
),
),
]);
}
}