flutter(rider): rebuild lib on Cubit/Bloc + Tripz backend (core + auth slice)

- keep store identity (com.mobileapp.store.ride, shorebird, firebase, fonts); old lib -> lib_old_getx
- core: config/di/router(go_router)/theme/api_client(dio+tenant+bearer)/token_store(secure)
- auth: repo + AuthCubit + OTP phone/otp screens; home: flutter_map (OSM placeholder, انطلق TODO)
- android usesCleartextTraffic=true for http API; maps=flutter_map (no Google), state=Cubit

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:47:55 +03:00
co-authored by Claude Opus 4.8
parent 6234e66471
commit 6116d23d0c
203 changed files with 573 additions and 83094 deletions
@@ -1,233 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
class AboutPage extends StatelessWidget {
const AboutPage({super.key});
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('About Us'.tr),
),
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Company Logo
Center(
child: Image.asset(
'assets/images/logo.gif', // Replace with your logo image asset path
height: 80.0,
),
),
const SizedBox(height: 20),
// Company Name and Introduction
Text(
'Intaleq LLC',
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
"Syria's pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you – both our valued passengers and our dedicated captains."
.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
// Key Features Section
Text(
'Why Choose Intaleq?'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 15),
// Nearest Availability
Row(
children: [
const Icon(CupertinoIcons.location_solid,
color: CupertinoColors.activeBlue),
const SizedBox(width: 10),
Expanded(
child: Text(
'Closest to You'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Text(
'We connect you with the nearest drivers for faster pickups and quicker journeys.'
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(color: CupertinoColors.secondaryLabel),
),
const SizedBox(height: 20),
// High-Level Security
Row(
children: [
const Icon(CupertinoIcons.shield_fill,
color: CupertinoColors.activeGreen),
const SizedBox(width: 10),
Expanded(
child: Text(
'Uncompromising Security'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(CupertinoIcons.person_2_fill,
size: 18, color: CupertinoColors.activeGreen),
const SizedBox(width: 5),
Text(
'Lady Captains Available'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(fontSize: 15),
),
],
),
const SizedBox(height: 5),
Row(
children: [
const Icon(CupertinoIcons.recordingtape,
size: 18, color: CupertinoColors.activeGreen),
const SizedBox(width: 5),
Text(
'Recorded Trips (Voice & AI Analysis)'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(fontSize: 15),
),
],
),
],
),
const SizedBox(height: 20),
// Fast Support
Row(
children: [
const Icon(CupertinoIcons.bolt_horizontal_fill,
color: CupertinoColors.systemOrange),
const SizedBox(width: 10),
Expanded(
child: Text(
'Fastest Complaint Response'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Text(
'Our dedicated customer service team ensures swift resolution of any issues.'
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(color: CupertinoColors.secondaryLabel),
),
const SizedBox(height: 20),
// Affordable Pricing
Row(
children: [
const Icon(CupertinoIcons.money_dollar_circle_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 10),
Expanded(
child: Text(
'Affordable for Everyone'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Text(
'Enjoy competitive prices across all trip options, making travel accessible.'
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(color: CupertinoColors.secondaryLabel),
),
const SizedBox(height: 20),
// Trip Options
Row(
children: [
const Icon(CupertinoIcons.car_detailed,
color: CupertinoColors.systemPurple),
const SizedBox(width: 10),
Expanded(
child: Text(
'Variety of Trip Choices'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Text(
'Choose the trip option that perfectly suits your needs and preferences.'
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(color: CupertinoColors.secondaryLabel),
),
const SizedBox(height: 20),
// Passenger Choice
Row(
children: [
Icon(CupertinoIcons.hand_draw_fill,
color: CupertinoColors.systemGreen),
const SizedBox(width: 10),
Expanded(
child: Text(
'Your Choice, Our Priority'.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
),
),
],
),
const SizedBox(height: 10),
Text(
'Because we are near, you have the flexibility to choose the ride that works best for you.'
.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(color: CupertinoColors.secondaryLabel),
),
],
),
),
),
),
);
}
}
@@ -1,102 +0,0 @@
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
// ignore: unused_import
import 'package:Tripz/controller/functions/launch.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/contact_us_controller.dart';
class ContactUsPage extends StatelessWidget {
ContactUsPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(ContactUsController());
return GetBuilder<ContactUsController>(builder: (controller) {
return MyScafolld(
title: "Contact Us".tr,
body: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image.asset('assets/images/logo.gif')),
IconButton(
onPressed: () async {
Get.put(TextToSpeechController()).speakText(
'Tripz is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Tripz, you enjoy quality, safety, and peace of mind—every time you ride.'
.tr);
},
icon: const Icon(Icons.headphones),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Tripz is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Tripz, you enjoy quality, safety, and peace of mind—every time you ride.'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
],
),
),
const SizedBox(
height: 30,
),
Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"You can contact us during working hours from 10:00 - 16:00."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
),
InkWell(
onTap: () => controller.showContactDialog(context),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
Icons.phone,
color: AppColor.blueColor,
),
Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
Icon(
Icons.email,
color: AppColor.redColor,
),
],
),
),
const SizedBox(
height: 30,
)
],
),
)
],
isleading: true);
});
}
}
@@ -1,228 +0,0 @@
import 'package:Tripz/views/home/HomePage/contact_us.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class FrequentlyQuestionsPage extends StatelessWidget {
const FrequentlyQuestionsPage({super.key});
void _showAnswerDialog(BuildContext context, String question, String answer) {
showCupertinoDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text(question,
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.indigo)),
content: Text(answer),
actions: <CupertinoDialogAction>[
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: Colors.indigo,
middle: Text(
'Frequently Asked Questions'.tr,
style: const TextStyle(color: Colors.white),
),
),
child: SafeArea(
child: ListView(
children: <Widget>[
CupertinoListSection.insetGrouped(
header: Text(
'Getting Started'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.car_detailed,
color: Colors.indigo,
),
title: Text('How do I request a ride?'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'How do I request a ride?'.tr,
'Simply open the Tripz app, enter your destination, and tap "Request Ride". The app will connect you with a nearby driver.'
.tr,
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'Vehicle Options'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.car_fill,
color: Colors.blue,
),
title: Text('What types of vehicles are available?'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'What types of vehicles are available?'.tr,
'Tripz offers a variety of options including Economy, Comfort, and Luxury to suit your needs and budget.'
.tr,
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'Payments'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.creditcard,
color: Colors.green,
),
title: Text('How can I pay for my ride?'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'How can I pay for my ride?'.tr,
'You can pay for your ride using cash or credit/debit card. You can select your preferred payment method before confirming your ride.'
.tr,
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'Ride Management'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.xmark_circle_fill,
color: Colors.red,
),
title: Text('Can I cancel my ride?'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'Can I cancel my ride?'.tr,
'Yes, you can cancel your ride, but please note that cancellation fees may apply depending on how far in advance you cancel.'
.tr,
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'For Drivers'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.person_crop_circle_fill,
color: Colors.orange,
),
title: Text('Driver Registration & Requirements'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => showCupertinoDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text('Driver Registration'.tr,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.indigo)),
content: Text(
'To register as a driver or learn about the requirements, please visit our website or contact Tripz support directly.'
.tr),
actions: <CupertinoDialogAction>[
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Get.to(() => ContactUsPage());
// Optionally, you can open a URL here
},
child: Text('Visit Website/Contact Support'.tr),
),
CupertinoDialogAction(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Close'.tr),
),
],
),
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'Communication'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.chat_bubble_2_fill,
color: Colors.purple,
),
title: Text(
'How do I communicate with the other party (passenger/driver)?'
.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'How do I communicate with the other party (passenger/driver)?'
.tr,
'You can communicate with your driver or passenger through the in-app chat feature once a ride is confirmed.'
.tr,
),
),
],
),
CupertinoListSection.insetGrouped(
header: Text(
'Safety & Security'.tr,
style: const TextStyle(fontWeight: FontWeight.bold),
),
children: <CupertinoListTile>[
CupertinoListTile(
leading: const Icon(
CupertinoIcons.shield_fill,
color: Colors.teal,
),
title: Text('What safety measures does Tripz offer?'.tr),
trailing: const CupertinoListTileChevron(),
onTap: () => _showAnswerDialog(
context,
'What safety measures does Tripz offer?'.tr,
'Tripz offers various safety features including driver verification, in-app trip tracking, emergency contact options, and the ability to share your trip status with trusted contacts.'
.tr,
),
),
],
),
const SizedBox(height: 20), // Add some bottom padding
],
),
),
);
}
}
@@ -1,568 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/profile/invit_controller.dart';
import '../../../print.dart';
class ShareAppPage extends StatelessWidget {
final InviteController controller = Get.put(InviteController());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.systemBackground,
appBar: AppBar(
backgroundColor: CupertinoColors.systemBackground,
elevation: 0,
title: Text(
'Invite'.tr,
style: const TextStyle(color: CupertinoColors.label),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColor.blueColor),
onPressed: () => Get.back(),
),
),
body: SafeArea(
child: GetBuilder<InviteController>(
builder: (controller) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: _buildPassengerTab(context),
),
),
],
);
},
),
),
);
}
Widget _buildPassengerTab(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
"Share this code with your friends and earn rewards when they use it!"
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 13,
),
),
],
),
),
const SizedBox(height: 20),
_buildPhoneInput(),
const SizedBox(height: 20),
_buildActionButtonsPassengers(),
const SizedBox(height: 20),
const SizedBox(height: 20),
_buildInvitationsListPassengers(context),
],
);
}
// Widget _buildPhoneInput() {
// return Container(
// decoration: BoxDecoration(
// color: CupertinoColors.systemGrey6,
// borderRadius: BorderRadius.circular(8),
// ),
// child: Row(
// children: [
// Expanded(
// child: CupertinoTextField.borderless(
// controller: controller.invitePhoneController,
// placeholder: 'Enter phone'.tr,
// padding: const EdgeInsets.all(12),
// keyboardType: TextInputType.phone,
// ),
// ),
// CupertinoButton(
// child: const Icon(CupertinoIcons.person_badge_plus,
// color: AppColor.blueColor),
// onPressed: () async {
// await controller.pickContacts();
// if (controller.contacts.isNotEmpty) {
// if (box.read(BoxName.isSavedPhones) == null) {
// controller.savePhoneToServer();
// box.write(BoxName.isSavedPhones, true);
// }
// _showContactsDialog(Get.context!);
// }
// },
// ),
// ],
// ),
// );
// }
Widget _buildPhoneInput() {
return Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
child: CupertinoTextField.borderless(
controller: controller.invitePhoneController,
placeholder: 'Enter phone'.tr,
padding: const EdgeInsets.all(12),
keyboardType: TextInputType.phone,
),
),
CupertinoButton(
child: const Icon(CupertinoIcons.person_badge_plus,
color: AppColor.blueColor),
onPressed: () async {
await controller.pickContacts();
Log.print('contacts: ${controller.contacts}');
if (controller.contacts.isNotEmpty) {
_showContactsDialog(Get
.context!); // Show contacts dialog after loading contacts
} else {
Get.snackbar(
'No contacts available'.tr,
'Please add contacts to your phone.'.tr,
);
}
},
),
],
),
);
}
Widget _buildActionButtonsPassengers() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInviteToPassenger,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
onPressed: () async {
controller.fetchDriverStatsPassengers();
},
),
),
),
],
),
);
}
Widget _buildInvitationsListPassengers(BuildContext context) {
return SizedBox(
height: Get.height * .4,
child: controller.driverInvitationDataToPassengers.isEmpty
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 17,
),
),
)
: ListView.builder(
itemCount: controller.driverInvitationDataToPassengers.length,
itemBuilder: (context, index) {
return _buildInvitationItemPassengers(context, index);
},
),
);
}
Widget _buildInvitationItemPassengers(BuildContext context, int index) {
// Extracting the data from the sample JSON-like structure
var invitation = controller.driverInvitationDataToPassengers[index];
int countOfInvitDriver =
int.tryParse(invitation['countOfInvitDriver']?.toString() ?? '0') ?? 0;
double progressValue = (countOfInvitDriver / 10.0).clamp(0.0, 1.0);
return GestureDetector(
onTap: () {
controller.onSelectPassengerInvitation(index);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
invitation['passengerName']
.toString(), // Handle null or missing data
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 2 ${'Trip'.tr}', // Show trips completed
style: const TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
],
),
),
);
}
Widget _buildPassengerStats(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Your Rewards".tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 16),
_buildStatItem(
context,
"Total Invites".tr,
controller.driverInvitationDataToPassengers[0]['countOfInvitDriver']
.toString(),
),
_buildStatItem(
context,
"Active Users".tr,
controller.driverInvitationDataToPassengers[0]['passengerName']
.toString(),
),
],
),
);
}
Widget _buildStatItem(BuildContext context, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 15,
),
),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: AppColor.blueColor,
fontSize: 15,
),
),
],
),
);
}
void _showContactsDialog(BuildContext context) {
Get.defaultDialog(
title: 'Choose from contact'.tr,
content: SizedBox(
height: 400,
width: 400,
child: Column(
children: [
// Header with cancel and title
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
// color: CupertinoColors.systemGrey6,
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// CupertinoButton(
// padding: EdgeInsets.zero,
// child: Text(
// 'Cancel'.tr,
// style: const TextStyle(color: CupertinoColors.systemBlue),
// ),
// onPressed: () => Navigator.pop(context),
// ),
// Container(
// child: Text('Choose from contact'.tr,
// style: AppStyle.title)),
// const SizedBox(width: 60), // Balance for Cancel button
// ],
// ),
// ),
// Contact list
Expanded(
child: ListView.builder(
itemCount: controller.contactMaps.length,
itemBuilder: (context, index) {
final contact = controller.contactMaps[index];
return CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () {
controller.selectPhone(contact['phones'].toString());
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5),
),
),
),
child: Row(
children: [
// Display contact name and phone number
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact['name'],
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
Text(
(contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 15,
),
),
],
),
),
// Chevron icon for selection
const Icon(
CupertinoIcons.chevron_forward,
color: CupertinoColors.systemGrey2,
),
],
),
),
);
},
),
),
],
),
),
);
// showCupertinoModalPopup(
// context: context,
// builder: (BuildContext context) => Container(
// height: 400,
// decoration: BoxDecoration(
// color: CupertinoColors.systemBackground,
// borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
// boxShadow: [
// BoxShadow(
// color: CupertinoColors.black.withOpacity(0.2),
// offset: const Offset(0, -4),
// blurRadius: 10,
// ),
// ],
// ),
// child: Column(
// children: [
// // Header with cancel and title
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
// color: CupertinoColors.systemGrey6,
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// CupertinoButton(
// padding: EdgeInsets.zero,
// child: Text(
// 'Cancel'.tr,
// style: const TextStyle(color: CupertinoColors.systemBlue),
// ),
// onPressed: () => Navigator.pop(context),
// ),
// Container(
// child: Text('Choose from contact'.tr,
// style: AppStyle.title)),
// const SizedBox(width: 60), // Balance for Cancel button
// ],
// ),
// ),
// // Contact list
// Expanded(
// child: ListView.builder(
// itemCount: controller.contactMaps.length,
// itemBuilder: (context, index) {
// final contact = controller.contactMaps[index];
// return CupertinoButton(
// padding: EdgeInsets.zero,
// onPressed: () {
// controller.selectPhone(contact['phones'].toString());
// },
// child: Container(
// padding: const EdgeInsets.symmetric(
// horizontal: 16, vertical: 12),
// decoration: BoxDecoration(
// color: CupertinoColors.systemBackground,
// border: Border(
// bottom: BorderSide(
// color: CupertinoColors.separator.withOpacity(0.5),
// ),
// ),
// ),
// child: Row(
// children: [
// // Display contact name and phone number
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// contact['name'],
// style: const TextStyle(
// color: CupertinoColors.label,
// fontSize: 17,
// fontWeight: FontWeight.w500,
// ),
// ),
// Text(
// controller.formatPhoneNumber(
// contact['phones'][0].toString()),
// style: const TextStyle(
// color: CupertinoColors.secondaryLabel,
// fontSize: 15,
// ),
// ),
// ],
// ),
// ),
// // Chevron icon for selection
// const Icon(
// CupertinoIcons.chevron_forward,
// color: CupertinoColors.systemGrey2,
// ),
// ],
// ),
// ),
// );
// },
// ),
// ),
// ],
// ),
// ),
// );
}
}
@@ -1,105 +0,0 @@
import 'dart:io';
import 'package:Tripz/controller/home/trip_monitor_controller.dart';
import 'package:Tripz/views/widgets/my_scafold.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:vibration/vibration.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../widgets/elevated_btn.dart';
class TripMonitor extends StatelessWidget {
const TripMonitor({super.key});
@override
Widget build(BuildContext context) {
final params = Get.parameters;
// Use params to initialize your controller or pass data
Get.put(TripMonitorController()).init();
return GetBuilder<TripMonitorController>(builder: (tripMonitorController) {
return MyScafolld(
title: 'Trip Monitor'.tr,
body: [
GoogleMap(
onMapCreated: tripMonitorController.onMapCreated,
initialCameraPosition: CameraPosition(
// bearing: 45,
target: tripMonitorController.parentLocation,
zoom: 16,
tilt: 40,
),
// onCameraMove: (position) {},
markers: {
Marker(
markerId: MarkerId('start'.tr),
position: tripMonitorController.parentLocation,
draggable: true,
icon: tripMonitorController.tripData['message'][0]['model']
.contains('دراجة')
? tripMonitorController.motoIcon
: tripMonitorController.tripData['message'][0]['model']
['gender'] ==
'Male'
? tripMonitorController.carIcon
: tripMonitorController.ladyIcon,
rotation: tripMonitorController.rotation,
),
},
),
speedCircle()
],
isleading: true,
);
});
}
}
GetBuilder<TripMonitorController> speedCircle() {
if (Get.find<TripMonitorController>().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<TripMonitorController>(
builder: (tripMonitorController) {
return Positioned(
bottom: 25,
right: 100,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: tripMonitorController.speed > 100
? Colors.red
: AppColor.secondaryColor,
border: Border.all(width: 3, color: AppColor.redColor),
),
height: 60,
width: 60,
child: Center(
child: Text(
tripMonitorController.speed.toStringAsFixed(0),
style: AppStyle.number,
),
),
),
);
},
);
}
@@ -1,111 +0,0 @@
import 'dart:io';
import 'package:Tripz/controller/home/trip_monitor_controller.dart';
import 'package:Tripz/views/widgets/my_scafold.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:vibration/vibration.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../widgets/elevated_btn.dart';
class TripMonitor extends StatelessWidget {
const TripMonitor({super.key});
@override
Widget build(BuildContext context) {
final params = Get.parameters;
final arguments = Get.arguments as Map<String, dynamic>?;
// Use params or arguments to initialize your controller
final controller = Get.put(TripMonitorController());
controller.init(
rideId: params['rideId'] ?? arguments?['rideId'],
driverId: params['driverId'] ?? arguments?['driverId'],
);
return GetBuilder<TripMonitorController>(builder: (tripMonitorController) {
return MyScafolld(
title: 'Trip Monitor'.tr,
body: [
GoogleMap(
onMapCreated: tripMonitorController.onMapCreated,
initialCameraPosition: CameraPosition(
// bearing: 45,
target: tripMonitorController.parentLocation,
zoom: 16,
tilt: 40,
),
// onCameraMove: (position) {},
markers: {
Marker(
markerId: MarkerId('start'.tr),
position: tripMonitorController.parentLocation,
draggable: true,
icon: tripMonitorController.tripData['message'][0]['model']
.contains('دراجة')
? tripMonitorController.motoIcon
: tripMonitorController.tripData['message'][0]['model']
['gender'] ==
'Male'
? tripMonitorController.carIcon
: tripMonitorController.ladyIcon,
rotation: tripMonitorController.rotation,
),
},
),
speedCircle()
],
isleading: true,
);
});
}
}
GetBuilder<TripMonitorController> speedCircle() {
if (Get.find<TripMonitorController>().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<TripMonitorController>(
builder: (tripMonitorController) {
return Positioned(
bottom: 25,
right: 100,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: tripMonitorController.speed > 100
? Colors.red
: AppColor.secondaryColor,
border: Border.all(width: 3, color: AppColor.redColor),
),
height: 60,
width: 60,
child: Center(
child: Text(
tripMonitorController.speed.toStringAsFixed(0),
style: AppStyle.number,
),
),
),
);
},
);
}
@@ -1,263 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as path;
import 'package:share_plus/share_plus.dart';
import '../../../controller/functions/audio_record1.dart';
class TripsRecordedPage extends StatelessWidget {
const TripsRecordedPage({super.key});
@override
Widget build(BuildContext context) {
// Ensure the controller is available.
// If it's not initialized elsewhere, you can use Get.put() or Get.lazyPut() here.
// Get.lazyPut(() => AudioRecorderController());
return Scaffold(
appBar: AppBar(
title: Text('Trips recorded'.tr),
backgroundColor: Colors.white,
elevation: 1,
actions: [
GetBuilder<AudioRecorderController>(
builder: (controller) => IconButton(
tooltip: 'Delete All'.tr,
icon: const Icon(Icons.delete_sweep_outlined),
onPressed: () {
_showDeleteConfirmation(context, controller, isDeleteAll: true);
},
),
)
],
),
body: GetBuilder<AudioRecorderController>(
builder: (controller) {
return Column(
children: [
Expanded(
child: _buildRecordingsList(controller),
),
// Show player controls only when a file is selected
if (controller.selectedFilePath != null)
_buildPlayerControls(context, controller),
],
);
},
),
);
}
/// Builds the list of recorded audio files.
Widget _buildRecordingsList(AudioRecorderController controller) {
return FutureBuilder<List<String>>(
future: controller.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'.tr));
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.mic_off_outlined, size: 80, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No Recordings Found'.tr,
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Text(
'Record your trips to see them here.'.tr,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey[500]),
),
),
],
),
);
}
final recordedFiles = snapshot.data!;
return ListView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 8),
itemCount: recordedFiles.length,
itemBuilder: (context, index) {
final file = recordedFiles[index];
final fileName = path.basename(file);
final isSelected = controller.selectedFilePath == file;
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
elevation: isSelected ? 4 : 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Icon(
isSelected && controller.isPlaying
? Icons.pause_circle_filled
: Icons.play_circle_fill,
color:
isSelected ? Theme.of(context).primaryColor : Colors.grey,
size: 40,
),
title: Text(fileName,
style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text("Audio Recording".tr),
onTap: () {
if (isSelected) {
controller.isPlaying
? controller.pausePlayback()
: controller.resumePlayback();
} else {
controller.playRecordedFile(file);
}
},
selected: isSelected,
selectedTileColor:
Theme.of(context).primaryColor.withOpacity(0.1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
);
},
);
},
);
}
/// Builds the player UI at the bottom of the screen.
Widget _buildPlayerControls(
BuildContext context, AudioRecorderController controller) {
final fileName = path.basename(controller.selectedFilePath!);
final positionText = Duration(seconds: controller.currentPosition.toInt())
.toString()
.split('.')
.first
.padLeft(8, '0')
.substring(3);
final durationText = Duration(seconds: controller.totalDuration.toInt())
.toString()
.split('.')
.first
.padLeft(8, '0')
.substring(3);
return Material(
elevation: 10,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(fileName,
style:
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
textAlign: TextAlign.center),
const SizedBox(height: 8),
Row(
children: [
Text(positionText),
Expanded(
child: Slider(
value: (controller.totalDuration > 0)
? controller.currentPosition / controller.totalDuration
: 0.0,
onChanged: (value) {
final newPosition = value * controller.totalDuration;
controller.audioPlayer
.seek(Duration(seconds: newPosition.toInt()));
},
),
),
Text(durationText),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.share_outlined),
tooltip: 'Share'.tr,
onPressed: () {
Share.shareXFiles([XFile(controller.selectedFilePath!)]);
},
iconSize: 28,
),
IconButton(
icon: Icon(controller.isPlaying
? Icons.pause_circle_filled
: Icons.play_circle_filled),
onPressed: () {
controller.isPlaying
? controller.pausePlayback()
: controller.resumePlayback();
},
iconSize: 56,
color: Theme.of(context).primaryColor,
),
IconButton(
icon:
const Icon(Icons.delete_outline, color: Colors.redAccent),
tooltip: 'Delete'.tr,
onPressed: () {
_showDeleteConfirmation(context, controller,
isDeleteAll: false);
},
iconSize: 28,
),
],
)
],
),
),
);
}
/// Shows a confirmation dialog for deleting one or all files.
void _showDeleteConfirmation(
BuildContext context,
AudioRecorderController controller, {
required bool isDeleteAll,
}) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(isDeleteAll
? 'Delete All Recordings?'.tr
: 'Delete Recording?'.tr),
content: Text(isDeleteAll
? 'This action cannot be undone.'.tr
: 'Are you sure you want to delete this file?'.tr),
actions: [
TextButton(
child: Text('Cancel'.tr),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child:
Text('Delete'.tr, style: const TextStyle(color: Colors.red)),
onPressed: () async {
if (isDeleteAll) {
await controller.deleteAllRecordedFiles();
} else {
// NOTE: You may need to add this method to your controller
// if it doesn't exist.
// await controller.deleteFile(controller.selectedFilePath!);
}
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
@@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
import 'package:Tripz/constant/colors.dart';
class DrawerMenuPage extends StatelessWidget {
const DrawerMenuPage({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 500,
color: AppColor.secondaryColor.withOpacity(.5),
child: Column(
children: [
Container(
height: 100,
color: AppColor.secondaryColor,
),
Container(
color: Colors.transparent,
)
],
),
);
}
}
@@ -1,164 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../controller/functions/crud.dart';
import '../../controller/functions/package_info.dart';
import '../../controller/home/map_passenger_controller.dart';
import '../../main.dart';
import '../../views/home/map_widget.dart/ride_begin_passenger.dart';
import '../../controller/home/menu_controller.dart';
import 'map_widget.dart/apply_order_widget.dart';
import 'map_widget.dart/buttom_sheet_map_show.dart';
import 'map_widget.dart/car_details_widget_to_go.dart';
import 'map_widget.dart/cash_confirm_bottom_page.dart';
import 'map_widget.dart/google_map_passenger_widget.dart';
import 'map_widget.dart/left_main_menu_icons.dart';
import 'map_widget.dart/main_bottom_menu_map.dart';
import 'map_widget.dart/map_menu_widget.dart';
import 'map_widget.dart/menu_map_page.dart';
import 'map_widget.dart/passengerRideLoctionWidget.dart';
import 'map_widget.dart/payment_method.page.dart';
import 'map_widget.dart/points_page_for_rider.dart';
import 'map_widget.dart/ride_from_start_app.dart';
import 'map_widget.dart/searching_captain_window.dart';
import 'map_widget.dart/vip_begin.dart';
class MapPagePassenger extends StatelessWidget {
const MapPagePassenger({super.key});
@override
Widget build(BuildContext context) {
Get.put(MapPassengerController());
Get.put(MyMenuController());
Get.put(CRUD());
WidgetsBinding.instance.addPostFrameCallback((_) {
checkForUpdate(context);
});
return Scaffold(
body: SafeArea(
bottom: true,
child: Stack(
children: [
GoogleMapPassengerWidget(),
// OsmMapPassengerWidget(),
leftMainMenuIcons(),
// PaymobPackage(),
const PickerIconOnMap(),
// PickerAnimtionContainerFormPlaces(),
const MainBottomMenuMap(),
// NewMainBottomSheet(),
buttomSheetMapPage(),
CarDetailsTypeToChoose(),
// const HeaderDestination(),
const BurcMoney(),
// const PromoCode(),
ApplyOrderWidget(),
const MapMenuWidget(),
// hexagonClipper(),
const CancelRidePageShow(),
CashConfirmPageShown(),
const PaymentMethodPage(),
const SearchingCaptainWindow(),
// timerForCancelTripFromPassenger(),
// const DriverTimeArrivePassengerPage(),
// const TimerToPassengerFromDriver(),
const PassengerRideLocationWidget(),
const RideBeginPassenger(),
const VipRideBeginPassenger(),
const RideFromStartApp(),
// cancelRidePage(),
// const MenuIconMapPageWidget(),
PointsPageForRider()
],
),
),
);
}
}
class CancelRidePageShow extends StatelessWidget {
const CancelRidePageShow({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) {
// نستخدم RideState Enum لأنه أدق، أو نصلح المنطق النصي
// الشرط:
// 1. يوجد خط مسار
// 2. الحالة ليست "بدأت"
// 3. الحالة ليست "انتهت"
// 4. الحالة ليست "قيد التنفيذ" (لزيادة التأكيد)
// bool showCancelButton = controller.polyLines.isNotEmpty &&
// controller.statusRide != 'Begin' && // استخدمنا &&
// controller.statusRide != 'inProgress' &&
// controller.statusRide != 'Finished';
// يمكنك أيضاً استخدام RideState ليكون أدق:
bool showCancelButton = controller.polyLines.isNotEmpty &&
controller.currentRideState.value != RideState.inProgress &&
controller.currentRideState.value != RideState.finished;
return showCancelButton
? Positioned(
right: box.read(BoxName.lang) != 'ar' ? 10 : null,
left: box.read(BoxName.lang) == 'ar' ? 10 : null,
top: Get.height * .013,
child: GestureDetector(
onTap: () {
// استدعاء دالة الإلغاء
controller.changeCancelRidePageShow();
// ملاحظة: تأكد أن الدالة تظهر ديالوج للتأكيد أولاً ولا تلغي فوراً
},
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,
),
),
),
))
: const SizedBox();
},
);
}
}
class PickerIconOnMap extends StatelessWidget {
const PickerIconOnMap({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) => controller.isPickerShown
? Positioned(
bottom: Get.height * .2,
top: 0,
left: 0,
right: 0,
child: controller.isPickerShown
? const Icon(
Icons.add_location,
color: Colors.purple,
)
: const SizedBox(),
)
: const SizedBox());
}
}
@@ -1,641 +0,0 @@
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/links.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/map_passenger_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../../constant/box_name.dart';
import '../../../controller/firebase/notification_service.dart';
import '../../../controller/functions/launch.dart';
import '../../../main.dart';
class ApplyOrderWidget extends StatelessWidget {
const ApplyOrderWidget({super.key});
@override
Widget build(BuildContext context) {
Color parseColor(String colorHex) {
if (colorHex.isEmpty) return Colors.grey;
try {
String processedHex = colorHex.replaceFirst('#', '').trim();
if (processedHex.length == 6) processedHex = 'FF$processedHex';
return Color(int.parse('0x$processedHex'));
} catch (e) {
return Colors.grey;
}
}
return Obx(() {
final controller = Get.find<MapPassengerController>();
final bool isVisible =
controller.currentRideState.value == RideState.driverApplied ||
controller.currentRideState.value == RideState.driverArrived;
return AnimatedPositioned(
duration: const Duration(milliseconds: 500),
curve: Curves.elasticOut,
// تغيير: جعلنا الإخفاء للأسفل أقل حدة ليكون التحريك أسرع
bottom: isVisible ? 0 : -400,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(25)),
boxShadow: [
BoxShadow(
blurRadius: 20,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
offset: const Offset(0, -3),
)
],
),
// تغيير: تقليل الحواف الخارجية بشكل كبير
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: GetBuilder<MapPassengerController>(
builder: (c) {
return Column(
mainAxisSize:
MainAxisSize.min, // مهم جداً: يأخذ أقل مساحة ممكنة
children: [
// مقبض صغير
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
),
const SizedBox(height: 10), // تقليل المسافة
// 1. [تغيير جوهري] دمج السعر مع الحالة في صف واحد لتوفير المساحة
_buildCompactHeaderRow(context, c),
const SizedBox(height: 10), // مسافة مضغوطة
// 2. كرت المعلومات المضغوط
_buildCompactInfoCard(context, c, parseColor),
const SizedBox(height: 10), // مسافة مضغوطة
// 3. أزرار الاتصال (Slim)
_buildCompactButtonsRow(context, c),
const SizedBox(height: 10), // مسافة مضغوطة
// 4. شريط الوقت
c.currentRideState.value == RideState.driverArrived
? const DriverArrivePassengerAndWaitMinute()
: const TimeDriverToPassenger(),
],
);
},
),
),
);
});
}
// ---------------------------------------------------------------------------
// [NEW] 1. صف الرأس المضغوط (يحتوي الحالة + الإحصائيات + السعر)
// ---------------------------------------------------------------------------
Widget _buildCompactHeaderRow(
BuildContext context, MapPassengerController controller) {
// تنسيق السعر
final formatter = NumberFormat("#,###");
String formattedPrice = formatter.format(controller.totalPassenger);
// حساب الدقائق
int minutes =
(controller.timeToPassengerFromDriverAfterApplied / 60).ceil();
if (minutes < 1) minutes = 1;
// تنسيق المسافة
String distanceDisplay = "";
try {
double distMeters = double.parse(controller.distanceByPassenger);
if (distMeters >= 1000) {
distanceDisplay = "${(distMeters / 1000).toStringAsFixed(1)} km";
} else {
distanceDisplay = "${distMeters.toInt()} m";
}
} catch (e) {
distanceDisplay = controller.distanceByPassenger;
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// القسم الأيسر: الحالة + Chips
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Driver is on the way'.tr,
style: AppStyle.subtitle.copyWith(
color: Colors.grey[600],
fontWeight: FontWeight.w600,
fontSize: 13, // تصغير الخط
),
),
const SizedBox(height: 6),
Row(
children: [
_buildMiniStatChip(
icon: Icons.access_time_filled_rounded,
text: "$minutes ${'min'.tr}",
color: AppColor.primaryColor,
bgColor: AppColor.primaryColor.withOpacity(0.1),
),
const SizedBox(width: 8),
_buildMiniStatChip(
icon: Icons.near_me_rounded,
text: distanceDisplay,
color: Colors.orange[800]!,
bgColor: Colors.orange.withOpacity(0.1),
),
],
),
],
),
),
// القسم الأيمن: السعر (كبير وواضح في الزاوية)
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
formattedPrice,
style: AppStyle.title.copyWith(
fontSize: 24, // تصغير من 32 إلى 24
fontWeight: FontWeight.w900,
color: AppColor.primaryColor,
height: 1.0,
),
),
Text(
'SYP'.tr,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
],
),
],
);
}
Widget _buildMiniStatChip({
required IconData icon,
required String text,
required Color color,
required Color bgColor,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 12, color: color), // تصغير الأيقونة
const SizedBox(width: 4),
Text(
text,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
fontSize: 12, // تصغير الخط
),
),
],
),
);
}
// ---------------------------------------------------------------------------
// [MODIFIED] 2. كرت المعلومات المضغوط جداً
// ---------------------------------------------------------------------------
Widget _buildCompactInfoCard(BuildContext context,
MapPassengerController controller, Color Function(String) parseColor) {
return Container(
// تقليل الحواف الداخلية للكرت
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.withOpacity(0.1)),
),
child: Column(
children: [
// الصف العلوي: سائق + سيارة
Row(
children: [
// صورة السائق (أصغر)
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.2), width: 2),
),
child: CircleAvatar(
radius: 22, // تصغير من 28 إلى 22
backgroundColor: Colors.grey[200],
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
onBackgroundImageError: (_, __) =>
const Icon(Icons.person, color: Colors.grey, size: 20),
),
),
const SizedBox(width: 10),
// معلومات نصية
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.driverName,
style: const TextStyle(
fontSize: 15, // تصغير الخط
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Row(
children: [
const Icon(Icons.star_rounded,
color: Colors.amber, size: 14),
Text(
" ${controller.driverRate} • ${controller.model}",
style: TextStyle(
fontSize: 12,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
],
),
),
// أيقونة السيارة (أصغر)
_buildMicroCarIcon(controller, parseColor),
],
),
const SizedBox(height: 8),
// لوحة السيارة (شريط نحيف جداً)
_buildSlimLicensePlate(controller.licensePlate),
],
),
);
}
Widget _buildMicroCarIcon(
MapPassengerController controller, Color Function(String) parseColor) {
Color carColor = parseColor(controller.colorHex);
return Container(
height: 40, // تصغير من 50
width: 40,
decoration: BoxDecoration(
color: carColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(4),
child: ColorFiltered(
colorFilter: ColorFilter.mode(carColor, BlendMode.srcIn),
child: Image.asset(
box.read(BoxName.carType) == 'Scooter' ||
box.read(BoxName.carType) == 'Pink Bike'
? 'assets/images/moto.png'
: 'assets/images/car3.png',
fit: BoxFit.contain,
),
),
);
}
Widget _buildSlimLicensePlate(String plateNumber) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.grey.withOpacity(0.3)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
plateNumber,
style: const TextStyle(
fontFamily: 'RobotoMono',
fontSize: 18, // تصغير الرقم
fontWeight: FontWeight.w900,
color: Colors.black87,
letterSpacing: 1.5,
),
),
const Text("SYR",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.black54)),
],
),
);
}
// ---------------------------------------------------------------------------
// [MODIFIED] 3. أزرار الاتصال (Slim Buttons)
// ---------------------------------------------------------------------------
Widget _buildCompactButtonsRow(
BuildContext context, MapPassengerController controller) {
return SizedBox(
height: 40, // تحديد ارتفاع ثابت وصغير للأزرار
child: Row(
children: [
Expanded(
child: _buildSlimButton(
label: 'Message'.tr, // اختصار الكلمة
icon: Icons.chat_bubble_outline_rounded,
color: AppColor.blueColor,
bgColor: AppColor.blueColor.withOpacity(0.08),
onTap: () => _showContactOptionsDialog(context, controller),
),
),
const SizedBox(width: 10), // تقليل المسافة
Expanded(
child: _buildSlimButton(
label: 'Call'.tr, // اختصار الكلمة
icon: Icons.phone_rounded,
color: Colors.white,
bgColor: AppColor.greenColor,
onTap: () {
HapticFeedback.heavyImpact();
makePhoneCall(controller.driverPhone);
},
isPrimary: true,
),
),
],
),
);
}
Widget _buildSlimButton({
required String label,
required IconData icon,
required Color color,
required Color bgColor,
required VoidCallback onTap,
bool isPrimary = false,
}) {
return ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
backgroundColor: bgColor,
foregroundColor: color,
elevation: isPrimary ? 2 : 0,
padding: EdgeInsets.zero, // إزالة الحواشي الداخلية
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 18, color: color), // تصغير الأيقونة
const SizedBox(width: 6),
Text(
label,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14, // تصغير الخط
color: color,
),
),
],
),
);
}
// --- النوافذ المنبثقة للرسائل (نفس الكود السابق مع تحسين بسيط) ---
void _showContactOptionsDialog(
BuildContext context, MapPassengerController controller) {
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Quick Message'.tr,
style: AppStyle.title.copyWith(fontSize: 16)),
const SizedBox(height: 15),
..._buildPredefinedMessages(controller),
const Divider(height: 20),
_buildCustomMessageInput(controller, context),
SizedBox(height: MediaQuery.of(context).viewInsets.bottom),
],
),
),
isScrollControlled: true,
);
}
List<Widget> _buildPredefinedMessages(MapPassengerController controller) {
const messages = [
'Hello, I\'m at the agreed-upon location',
'I\'m waiting for you',
"How much longer will you be?",
];
return messages
.map((message) => Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: InkWell(
onTap: () {
_sendMessage(controller, message.tr);
Get.back();
},
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.08),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.withOpacity(0.1)),
),
child: Row(
children: [
Icon(Icons.chat_bubble_outline,
size: 16, color: AppColor.primaryColor),
const SizedBox(width: 10),
Expanded(
child: Text(message.tr,
style: AppStyle.subtitle.copyWith(fontSize: 13))),
],
),
),
),
))
.toList();
}
Widget _buildCustomMessageInput(
MapPassengerController controller, BuildContext context) {
return Row(
children: [
Expanded(
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.08),
borderRadius: BorderRadius.circular(20),
),
child: Form(
key: controller.messagesFormKey,
child: TextFormField(
controller: controller.messageToDriver,
decoration: InputDecoration(
hintText: 'Type your message...'.tr,
hintStyle: TextStyle(color: Colors.grey[500], fontSize: 13),
border: InputBorder.none,
contentPadding: const EdgeInsets.only(bottom: 10),
),
),
),
),
),
const SizedBox(width: 8),
InkWell(
onTap: () {
if (controller.messagesFormKey.currentState!.validate()) {
_sendMessage(controller, controller.messageToDriver.text);
controller.messageToDriver.clear();
Get.back();
}
},
child: CircleAvatar(
backgroundColor: AppColor.primaryColor,
radius: 20,
child:
const Icon(Icons.send_rounded, color: Colors.white, size: 16),
),
),
],
);
}
void _sendMessage(MapPassengerController controller, String text) {
NotificationService.sendNotification(
category: 'MSG_FROM_PASSENGER',
target: controller.driverToken.toString(),
title: text.tr,
body: text.tr,
isTopic: false,
tone: 'ding',
driverList: [],
);
}
}
// -----------------------------------------------------------------------------
// مؤشرات الانتظار والوقت (مضغوطة)
// -----------------------------------------------------------------------------
class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
const DriverArrivePassengerAndWaitMinute({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Waiting...'.tr,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.orange)),
Text(
controller.stringRemainingTimeDriverWaitPassenger5Minute,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.orange,
fontSize: 12),
),
],
),
const SizedBox(height: 4),
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: LinearProgressIndicator(
backgroundColor: Colors.orange.withOpacity(0.2),
color: Colors.orange,
minHeight: 4,
value:
controller.progressTimerDriverWaitPassenger5Minute.toDouble(),
),
),
],
);
});
}
}
class TimeDriverToPassenger extends StatelessWidget {
const TimeDriverToPassenger({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
if (controller.timeToPassengerFromDriverAfterApplied <= 0) {
return const SizedBox();
}
return Column(
children: [
// شريط التقدم فقط لأن الوقت والمسافة موجودان بالأعلى
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: LinearProgressIndicator(
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
color: AppColor.primaryColor,
minHeight: 4,
value: controller.progressTimerToPassengerFromDriverAfterApplied
.toDouble()
.clamp(0.0, 1.0),
),
),
],
);
});
}
}
@@ -1,560 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/controller/payment/payment_controller.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
GetBuilder<MapPassengerController> buttomSheetMapPage() {
Get.put(PaymentController());
return GetBuilder<MapPassengerController>(
builder: (controller) =>
controller.isBottomSheetShown && controller.rideConfirm == false
? const Positioned(
left: 5,
bottom: 0,
right: 5,
child: Column(
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// double.parse(box.read(BoxName.passengerWalletTotal)) <
// 0 &&
// controller.data.isNotEmpty
// ? Container(
// decoration: AppStyle.boxDecoration
// .copyWith(color: AppColor.redColor),
// height: 50,
// width: Get.width * .94,
// child: Padding(
// padding:
// const EdgeInsets.symmetric(horizontal: 8),
// child: Text(
// 'Your trip cost is'.tr +
// ' ${controller.totalCostPassenger.toStringAsFixed(2)} '
// 'But you have a negative salary of'
// .tr +
// '${double.parse(box.read(BoxName.passengerWalletTotal)).toStringAsFixed(2)}'
// ' in your'
// .tr +
// ' ${AppInformation.appName}'
// ' wallet due to a previous trip.'
// .tr,
// style: AppStyle.subtitle,
// ),
// ))
// : const SizedBox(),
// ],
// ),
// const SizedBox(
// height: 5,
// ),
// AnimatedContainer(
// // clipBehavior: Clip.antiAliasWithSaveLayer,
// curve: Curves.easeInCirc,
// onEnd: () {
// controller.height = 250;
// },
// height: controller.heightBottomSheetShown,
// duration: const Duration(seconds: 2),
// child: Column(
// children: [
// controller.data.isEmpty
// ? const SizedBox()
// : Container(
// // width: Get.width * .9,
// height: 100,
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// boxShadow: [
// const BoxShadow(
// color: AppColor.accentColor,
// offset: Offset(2, 2)),
// BoxShadow(
// color: AppColor.accentColor
// .withOpacity(.4),
// offset: const Offset(-2, -2))
// ],
// borderRadius: const BorderRadius.all(
// Radius.circular(15))),
// child: ListView.builder(
// scrollDirection: Axis.horizontal,
// itemCount: controller
// .dataCarsLocationByPassenger.length -
// 1,
// itemBuilder:
// (BuildContext context, int index) {
// return Container(
// color: controller.gender == 'Female'
// ? const Color.fromARGB(
// 255, 246, 52, 181)
// : AppColor.secondaryColor,
// width: Get.width,
// child: Row(
// mainAxisAlignment:
// MainAxisAlignment.spaceBetween,
// children: [
// SizedBox(
// width: Get.width * .15,
// child: Padding(
// padding:
// const EdgeInsets.all(8.0),
// child: Image.asset(
// 'assets/images/jeep.png',
// width: 50,
// fit: BoxFit.fill,
// repeat: ImageRepeat.repeatX,
// ),
// ),
// ),
// SizedBox(
// width: Get.width * .55,
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// mainAxisAlignment:
// MainAxisAlignment.spaceEvenly,
// children: [
// Text(
// controller.hours > 0
// ? '${'Your Ride Duration is '.tr}${controller.hours} ${'H and'.tr} ${controller.minutes} ${'m'.tr}'
// : '${'Your Ride Duration is '.tr} ${controller.minutes} m',
// style: AppStyle.subtitle,
// ),
// // Text(
// // '${'You will be thier in'.tr} ${DateFormat('h:mm a').format(controller.newTime)}',
// // style: AppStyle.subtitle,
// // ),
// Text(
// '${'Your trip distance is'.tr} ${controller.distance.toStringAsFixed(2)} ${'KM'.tr}',
// style: AppStyle.subtitle,
// )
// ],
// ),
// ),
// SizedBox(
// width: Get.width * .2,
// child: Padding(
// padding: const EdgeInsets.only(
// right: 5, left: 5),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.center,
// children: [
// Container(
// width: Get.width * .14,
// height: Get.height * .06,
// decoration: BoxDecoration(
// color: AppColor
// .secondaryColor,
// shape:
// BoxShape.rectangle,
// border: Border.all(
// width: 2,
// color: AppColor
// .greenColor)),
// child: Center(
// child: Text(
// '${'Fee is'.tr} \n${controller.totalPassenger.toStringAsFixed(2)}',
// style:
// AppStyle.subtitle,
// ),
// ),
// ),
// controller.promoTaken
// ? const Icon(
// Icons
// .filter_vintage_rounded,
// color:
// AppColor.redColor,
// )
// : const SizedBox(
// height: 0,
// )
// ],
// ),
// ),
// ),
// ],
// ),
// );
// },
// ),
// ),
// const SizedBox(
// height: 5,
// ),
// Container(
// // height: 130,
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// boxShadow: [
// const BoxShadow(
// color: AppColor.accentColor,
// offset: Offset(2, 2)),
// BoxShadow(
// color: AppColor.accentColor.withOpacity(.4),
// offset: const Offset(-2, -2))
// ],
// borderRadius:
// const BorderRadius.all(Radius.circular(15))),
// child: controller.data.isEmpty
// ? const SizedBox()
// : Center(
// child: Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 5),
// child: Column(
// children: [
// Row(
// children: [
// const Icon(
// Icons.location_on,
// color: AppColor.redColor,
// ),
// const SizedBox(
// width: 10,
// ),
// Text(
// 'From : '.tr,
// style: AppStyle.subtitle,
// ),
// Text(
// controller.data[0]
// ['start_address']
// .toString(),
// style: AppStyle.subtitle,
// )
// ],
// ),
// Row(
// children: [
// const Icon(Icons
// .location_searching_rounded),
// const SizedBox(
// width: 10,
// ),
// Text(
// 'To : '.tr,
// style: AppStyle.subtitle,
// ),
// Text(
// controller.data[0]['end_address'],
// style: AppStyle.subtitle,
// ),
// ],
// ),
// const Divider(
// color: AppColor.accentColor,
// thickness: 1,
// height: 2,
// indent: 1,
// ),
// SizedBox(
// height: 40,
// child: Row(
// mainAxisAlignment:
// MainAxisAlignment.center,
// children: [
// Container(
// decoration: BoxDecoration(
// color:
// AppColor.secondaryColor,
// borderRadius:
// BorderRadius.circular(12),
// // border: Border.all(),
// ),
// child: Row(
// children: [
// Icon(
// Icons.monetization_on,
// color: Colors.green[400],
// ),
// InkWell(
// onTap: () async {
// controller
// .changeCashConfirmPageShown();
// Get.find<
// PaymentController>()
// .getPassengerWallet();
// },
// child: GetBuilder<
// PaymentController>(
// builder: (paymentController) =>
// paymentController
// .isCashChecked
// ? Text(
// 'CASH',
// style: AppStyle
// .title,
// )
// : Text(
// '${AppInformation.appName} Wallet',
// style: AppStyle
// .title,
// ),
// ),
// ),
// ],
// ),
// ),
// const SizedBox(
// width: 40,
// ),
// GetBuilder<PaymentController>(
// builder:
// (paymentController) =>
// Container(
// decoration:
// BoxDecoration(
// color: AppColor
// .secondaryColor,
// borderRadius:
// BorderRadius
// .circular(
// 12),
// ),
// child: Row(
// children: [
// Icon(
// Icons
// .qr_code_2_rounded,
// color: Colors
// .green[
// 400],
// ),
// InkWell(
// onTap: () {
// if (controller
// .promoTaken ==
// false) {
// Get.defaultDialog(
// title: 'Add Promo'.tr,
// titleStyle: AppStyle.title,
// content: Column(
// children: [
// SizedBox(
// width: Get.width * .7,
// child: TextFormField(
// controller: controller.promo,
// decoration: InputDecoration(
// labelText: 'Promo Code'.tr,
// hintText: 'Enter promo code'.tr,
// labelStyle: AppStyle.subtitle,
// hintStyle: AppStyle.subtitle,
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// filled: true,
// fillColor: Colors.grey[200],
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: AppColor.primaryColor,
// width: 2.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// errorBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.red,
// width: 2.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.grey,
// width: 1.0,
// ),
// borderRadius: BorderRadius.circular(10),
// ),
// ),
// ),
// ),
// MyElevatedButton(
// title: 'Add Promo'.tr,
// onPressed: () async {
// controller.applyPromoCodeToPassenger();
// },
// )
// ],
// ));
// } else {
// Get.snackbar(
// 'You have promo!'
// .tr,
// '',
// backgroundColor:
// AppColor.redColor);
// }
// },
// child: Text(
// 'Add Promo'
// .tr,
// style: AppStyle
// .title,
// ),
// ),
// ],
// ),
// )),
// ],
// ),
// ),
// SizedBox(
// width: Get.width * .95,
// child: Row(
// mainAxisAlignment:
// MainAxisAlignment.center,
// children: [
// controller.isCashSelectedBeforeConfirmRide ==
// false
// ? MyElevatedButton(
// title: 'Next'.tr,
// onPressed: () {
// controller
// .changeCashConfirmPageShown();
// },
// )
// :
// // controller.isPassengerChosen ==
// // false
// // ? MyElevatedButton(
// // title: 'Next'.tr,
// // onPressed: () {
// // controller
// // .onChangedPassengersChoose();
// // Get.defaultDialog(
// // barrierDismissible:
// // false,
// // title:
// // 'How Many Passengers?'
// // .tr,
// // titleStyle:
// // AppStyle
// // .title,
// // content:
// // Column(
// // children: [
// // Text(
// // 'Allowed up to 4 Passengers.'
// // .tr,
// // style: AppStyle
// // .title,
// // ),
// // SizedBox(
// // height:
// // 200, // Set the desired height here
// // child:
// // CupertinoPicker(
// // itemExtent:
// // 32,
// // onSelectedItemChanged:
// // (index) {
// // controller.onChangedPassengerCount(index +
// // 1);
// // },
// // children: [
// // Text('1 Passenger'.tr),
// // Text('2 Passengers'.tr),
// // Text('3 Passengers'.tr),
// // Text('4 Passengers'.tr),
// // ],
// // ),
// // ),
// // MyElevatedButton(
// // title:
// // 'Back',
// // onPressed:
// // () =>
// // Get.back(),
// // )
// // ],
// // ),
// // );
// // },
// // )
// // :
// MyElevatedButton(
// title: 'Confirm Selection'
// .tr,
// onPressed: () {
// controller
// .confirmRideForFirstDriver();
// },
// ),
// ],
// ),
// )
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ],
),
)
: const SizedBox());
}
class Details extends StatelessWidget {
const Details({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) => Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'${'Distance is'.tr} ${controller.distance.toStringAsFixed(2)} KM',
style: AppStyle.title,
),
Text(
'${'Duration is'.tr} ${controller.data[0]['duration']['text']}',
style: AppStyle.title,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Cost for .21/km ${controller.costDistance.toStringAsFixed(2)} ',
style: AppStyle.title,
),
Text(
'${'Cost Duration'.tr} ${controller.averageDuration.toStringAsFixed(2)} is ${controller.costDuration.toStringAsFixed(2)} ',
style: AppStyle.title,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Total Driver ${controller.totalDriver.toStringAsFixed(2)}',
style: AppStyle.title,
),
Text(
'totaME ${controller.totalME.toStringAsFixed(2)} ',
style: AppStyle.title,
),
],
),
Text(
'Cost for passenger ${controller.totalPassenger.toStringAsFixed(2)} ',
style: AppStyle.title,
),
],
));
}
}
@@ -1,258 +0,0 @@
// import 'dart:async';
// import 'package:SEFER/constant/box_name.dart';
// import 'package:SEFER/controller/home/map_passenger_controller.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 '../../../constant/colors.dart';
// import '../../../constant/style.dart';
// import '../../../controller/firebase/firbase_messge.dart';
// String appId = AK.agoraAppId;
// class PassengerCallPage extends StatefulWidget {
// const PassengerCallPage({
// super.key,
// required this.channelName,
// required this.token,
// required this.remoteID,
// });
// final String channelName, token, remoteID;
// @override
// State<PassengerCallPage> createState() => _PassengerCallPageState();
// }
// class _PassengerCallPageState extends State<PassengerCallPage> {
// int uid = 0;
// int? _remoteUid = 0; // uid of the remote user
// bool _isJoined = false; // Indicates if the local user has joined the channel
// late RtcEngine agoraEngine; // Agora engine instance
// String status = '';
// 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 setupVoiceSDKEngine();
// }
// @override
// void initState() {
// super.initState();
// _remoteUid = int.parse(widget.remoteID);
// uid = 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;
// status = 'joined'.tr;
// });
// },
// onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
// showMessage("Driver joined the channel".tr);
// setState(() {
// status = "Driver joined the channel".tr;
// _remoteUid = remoteUid;
// });
// },
// onUserOffline: (RtcConnection connection, int? remoteUid,
// UserOfflineReasonType reason) {
// showMessage("Driver left the channel".tr);
// setState(() {
// status = "Driver left the channel".tr;
// _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: widget.token,
// channelId: widget.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: 'Call Page'.tr,
// isleading: true,
// body: [
// Positioned(
// top: Get.height * .2,
// child: Container(
// height: 100, width: Get.width,
// decoration: AppStyle.boxDecoration,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// GestureDetector(
// onTap: () async {
// // await callController.initAgoraFull();
// // callController.join();
// // FirebaseMessagesController().sendNotificationToPassengerToken(
// // 'Call Income',
// // '${'You have call from driver'.tr} ${box.read(BoxName.nameDriver)}',
// // Get.find<MapDriverController>().tokenPassenger,
// // [
// // callController.token,
// // callController.channelName,
// // callController.uid.toString(),
// // callController.remoteUid.toString(),
// // ],
// // );
// join();
// // callController.fetchToken();
// },
// 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(
// status,
// style: AppStyle.title,
// ),
// Text('Driver Name'),
// ],
// ),
// GestureDetector(
// onTap: () async {
// FirebaseMessagesController()
// .sendNotificationToPassengerToken(
// 'Call End'.tr,
// 'Call End',
// Get.find<MapPassengerController>().driverToken,
// [],
// 'iphone_ringtone.wav',
// );
// 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
// ),
// ),
// // 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: Text("Join".tr),
// // onPressed: () => {join()},
// // ),
// // ),
// // const SizedBox(width: 10),
// // Expanded(
// // child: ElevatedButton(
// // child: Text("Leave".tr),
// // onPressed: () => {leave()},
// // ),
// // ),
// // ],
// // ),
// // ],
// // ),
// ]),
// );
// }
// // Widget _status() {
// // String statusText;
// //
// // if (!_isJoined) {
// // statusText = 'Join a channel'.tr;
// // } else if (_remoteUid == null)
// // statusText = 'Waiting for a remote user to join...';
// // else
// // statusText = 'Connected to remote user, uid:$_remoteUid';
// //
// // return Text(
// // statusText,
// // );
// // }
// }
@@ -1,160 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/map_passenger_controller.dart';
import '../../widgets/elevated_btn.dart';
// دالة لإظهار الشيت
void showCancelRideBottomSheet() {
Get.bottomSheet(
const CancelRidePageWidget(),
backgroundColor: Colors.transparent,
isScrollControlled: true,
);
}
// الويدجت مفصولة لترتيب الكود
class CancelRidePageWidget extends StatelessWidget {
const CancelRidePageWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// تأكد من وجود الكنترولر
final controller = Get.find<MapPassengerController>();
final List<String> reasons = [
"Changed my mind".tr,
"Found another transport".tr,
"Driver is taking too long".tr,
"Driver asked me to cancel".tr,
"Wrong pickup location".tr,
"Other".tr,
];
return Container(
height: Get.height * 0.7, // ارتفاع مناسب
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
),
child: GetBuilder<MapPassengerController>(
builder: (controller) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// مؤشر السحب
Center(
child: Container(
width: 50,
height: 5,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
),
const SizedBox(height: 20),
Text(
'Why do you want to cancel?'.tr,
style: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Expanded(
child: ListView.separated(
itemCount: reasons.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
bool isSelected = controller.selectedReasonIndex == index;
return Column(
children: [
ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
reasons[index],
style: TextStyle(
fontWeight: isSelected
? FontWeight.bold
: FontWeight.normal,
color: isSelected
? AppColor.primaryColor
: Colors.black87,
fontSize: 15),
),
trailing: isSelected
? Icon(Icons.radio_button_checked,
color: AppColor.primaryColor)
: Icon(Icons.radio_button_off, color: Colors.grey),
onTap: () {
controller.selectReason(index, reasons[index]);
},
),
// إظهار حقل النص فقط عند اختيار "أخرى"
if (isSelected && reasons[index] == "Other".tr)
Padding(
padding: const EdgeInsets.only(
bottom: 10, left: 10, right: 10),
child: TextField(
controller: controller.otherReasonController,
decoration: InputDecoration(
hintText: "Please write the reason...".tr,
filled: true,
fillColor: Colors.grey[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 12),
),
maxLines: 2,
),
)
],
);
},
),
),
const SizedBox(height: 20),
// زر التأكيد
SizedBox(
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.redColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
elevation: 0,
),
onPressed: () => controller.cancelRide(),
child: Text(
'Confirm Cancellation'.tr,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16),
),
),
),
const SizedBox(height: 10),
// زر التراجع
Center(
child: TextButton(
onPressed: () => Get.back(),
child: Text("Don't Cancel".tr,
style: TextStyle(color: Colors.grey[600])),
),
),
],
),
),
);
}
}
@@ -1,872 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/main.dart';
import 'package:Tripz/views/home/profile/passenger_profile_page.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import 'package:Tripz/views/widgets/my_textField.dart';
import 'package:intl/intl.dart';
import 'dart:ui';
import '../../../constant/info.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../print.dart';
import '../../widgets/mydialoug.dart';
// --- CarType class (Unchanged) ---
class CarType {
final String carType;
final String carDetail;
final String image;
bool isSelected = false;
CarType(
{required this.carType, required this.carDetail, required this.image});
}
// --- List of Car Types (Unchanged) ---
List<CarType> carTypes = [
CarType(
carType: 'Fixed Price',
carDetail: 'Closest & Cheapest'.tr,
image: 'assets/images/carspeed.png'),
CarType(
carType: 'Comfort',
carDetail: 'Comfort choice'.tr,
image: 'assets/images/blob.png'),
CarType(
carType: 'Electric',
carDetail: 'Quiet & Eco-Friendly'.tr,
image: 'assets/images/electric.png'),
CarType(
carType: 'Lady',
carDetail: 'Lady Captain for girls'.tr,
image: 'assets/images/lady.png'),
CarType(
carType: 'Van',
carDetail: 'Van for familly'.tr,
image: 'assets/images/bus.png'),
CarType(
carType: 'Rayeh Gai',
carDetail: "Best choice for cities".tr,
image: 'assets/images/roundtrip.png'),
];
// --- Main Widget ---
class CarDetailsTypeToChoose extends StatelessWidget {
CarDetailsTypeToChoose({super.key});
final textToSpeechController = Get.put(TextToSpeechController());
void _prepareCarTypes(MapPassengerController controller) {
if (controller.distance > 23) {
if (!carTypes.any((car) => car.carType == 'Rayeh Gai')) {
carTypes.add(CarType(
carType: 'Rayeh Gai',
carDetail: "Best choice for cities".tr,
image: 'assets/images/roundtrip.png'));
}
} else {
carTypes.removeWhere((car) => car.carType == 'Rayeh Gai');
}
}
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
_prepareCarTypes(controller);
if (!(controller.isBottomSheetShown) && controller.rideConfirm == false) {
return const SizedBox.shrink();
}
// Main Bottom Sheet Design
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: AppColor
.secondaryColor, // Solid background for better performance
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 20,
spreadRadius: 5,
offset: const Offset(0, -5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Drag Handle
Center(
child: Container(
width: 50,
height: 5,
margin: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
),
),
// Header (Title + Trip Info)
_buildModernHeader(controller),
// Warning Message (if any)
_buildNegativeBalanceWarning(controller),
// Car List
SizedBox(
height: 165, // Fixed height for consistency
child: ListView.separated(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
itemCount: carTypes.length,
separatorBuilder: (context, index) =>
const SizedBox(width: 12),
itemBuilder: (context, index) {
final carType = carTypes[index];
final isSelected = controller.selectedIndex == index;
return _buildVerticalCarCard(
context, controller, carType, isSelected, index);
},
),
),
// Promo Code Button
_buildPromoButton(context, controller),
// Safe Area spacing
SizedBox(height: MediaQuery.of(context).padding.bottom + 10),
],
),
),
);
});
}
// --- UI Components ---
Widget _buildModernHeader(MapPassengerController controller) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'Choose your ride'.tr,
style: AppStyle.headTitle.copyWith(
fontSize: 20,
fontWeight: FontWeight.w800,
letterSpacing: 0.5),
),
),
// Trip Info Pill
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.08),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColor.primaryColor.withOpacity(0.2)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.directions_car_filled_outlined,
size: 16, color: AppColor.primaryColor),
const SizedBox(width: 6),
Text(
'${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
style: AppStyle.subtitle.copyWith(
fontSize: 13,
fontWeight: FontWeight.bold,
color: AppColor.primaryColor),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
height: 12,
width: 1,
color: Colors.grey.shade400),
Icon(Icons.access_time_filled_rounded,
size: 16, color: AppColor.primaryColor),
const SizedBox(width: 6),
Text(
controller.hours > 0
? '${controller.hours}h ${controller.minutes}m'
: '${controller.minutes} min',
style: AppStyle.subtitle.copyWith(
fontSize: 13,
fontWeight: FontWeight.bold,
color: AppColor.primaryColor),
),
],
),
)
],
),
);
}
Widget _buildVerticalCarCard(
BuildContext context,
MapPassengerController controller,
CarType carType,
bool isSelected,
int index) {
return GestureDetector(
onTap: () {
controller.selectCarFromList(index);
_showCarDetailsDialog(
context, controller, carType, textToSpeechController);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
width: 110,
decoration: BoxDecoration(
color: isSelected
? AppColor.primaryColor.withOpacity(0.05)
: Colors.white,
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: isSelected
? AppColor.primaryColor
: Colors.grey.withOpacity(0.2),
width: isSelected ? 2.0 : 1.0,
),
boxShadow: isSelected
? [
BoxShadow(
color: AppColor.primaryColor.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
)
]
: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
child: Stack(
alignment: Alignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Image with subtle scaling if selected
AnimatedScale(
scale: isSelected ? 1.1 : 1.0,
duration: const Duration(milliseconds: 250),
child: Image.asset(
carType.image,
height: 50,
fit: BoxFit.contain,
),
),
const SizedBox(height: 12),
// Car Type Text
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
carType.carType.tr,
style: AppStyle.subtitle.copyWith(
fontWeight:
isSelected ? FontWeight.w800 : FontWeight.w600,
fontSize: 14,
color:
isSelected ? AppColor.primaryColor : Colors.black87,
),
maxLines: 1,
),
),
const SizedBox(height: 6),
// Price Tag
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isSelected
? AppColor.primaryColor
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: FittedBox(
child: Text(
'${_getPassengerPriceText(carType, controller)} ${'SYP'.tr}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: isSelected ? Colors.white : Colors.black87,
),
),
),
),
],
),
),
// Checkmark Badge for Selected Item
if (isSelected)
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: AppColor.primaryColor,
shape: BoxShape.circle,
),
child: const Icon(Icons.check, size: 12, color: Colors.white),
),
),
],
),
),
);
}
Widget _buildPromoButton(
BuildContext context, MapPassengerController controller) {
if (controller.promoTaken) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 5),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => _showPromoCodeDialog(context, controller),
borderRadius: BorderRadius.circular(14),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.05),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: Colors.grey.withOpacity(0.2)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.1),
shape: BoxShape.circle),
child: Icon(Icons.confirmation_number_outlined,
color: AppColor.primaryColor, size: 20),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Promo Code'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 14, fontWeight: FontWeight.bold),
),
Text(
'Have a promo code?'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 12, color: Colors.grey.shade600),
),
],
),
),
Icon(Icons.arrow_forward_ios_rounded,
size: 16, color: Colors.grey.shade400)
],
),
),
),
),
);
}
Widget _buildNegativeBalanceWarning(MapPassengerController controller) {
final passengerWallet =
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ?? 0.0;
if (passengerWallet < 0.0) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.redColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.redColor.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.info_outline_rounded,
color: AppColor.redColor, size: 24),
const SizedBox(width: 12),
Expanded(
child: Text(
'${'You have a negative balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${'SYP'.tr}.',
style: AppStyle.subtitle.copyWith(
color: AppColor.redColor,
fontWeight: FontWeight.w600,
fontSize: 13),
),
),
],
),
);
}
return const SizedBox.shrink();
}
// --- Logic Helpers (Copied from your previous code to ensure functionality) ---
String _getPassengerPriceText(
CarType carType, MapPassengerController mapPassengerController) {
double rawPrice;
switch (carType.carType) {
case 'Comfort':
rawPrice = mapPassengerController.totalPassengerComfort;
break;
case 'Fixed Price':
rawPrice = mapPassengerController.totalPassengerSpeed;
break;
case 'Electric':
rawPrice = mapPassengerController.totalPassengerElectric;
break;
case 'Awfar Car':
rawPrice = mapPassengerController.totalPassengerBalash;
break;
case 'Scooter':
case 'Pink Bike':
rawPrice = mapPassengerController.totalPassengerScooter;
break;
case 'Van':
rawPrice = mapPassengerController.totalPassengerVan;
break;
case 'Lady':
rawPrice = mapPassengerController.totalPassengerLady;
break;
case 'Rayeh Gai':
rawPrice = mapPassengerController.totalPassengerRayehGai;
break;
default:
return '...';
}
final int roundedPrice = rawPrice.round();
final formatter = NumberFormat.decimalPattern();
return formatter.format(roundedPrice);
}
// --- Dialogs (Styled consistently) ---
void _showPromoCodeDialog(
BuildContext context, MapPassengerController controller) {
Get.dialog(
Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
backgroundColor: AppColor.secondaryColor,
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Form(
key: controller.promoFormKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Icon(Icons.local_activity_outlined,
size: 40, color: AppColor.primaryColor),
const SizedBox(height: 16),
Text(
'Apply Promo Code'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle.copyWith(fontSize: 20),
),
const SizedBox(height: 8),
Text(
'Enter your code below to apply the discount.'.tr,
textAlign: TextAlign.center,
style: AppStyle.subtitle
.copyWith(color: Colors.grey.shade600, fontSize: 14),
),
const SizedBox(height: 24),
MyTextForm(
controller: controller.promo,
label: 'Promo Code'.tr,
hint: 'Enter your promo code'.tr,
type: TextInputType.text,
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => Get.back(),
style: TextButton.styleFrom(
foregroundColor: Colors.grey,
),
child: Text('Cancel'.tr),
),
),
const SizedBox(width: 16),
Expanded(
child: MyElevatedButton(
title: 'Apply'.tr,
onPressed: () {
if (controller.promoFormKey.currentState!
.validate()) {
controller.applyPromoCodeToPassenger(context);
Get.back();
}
},
),
),
],
),
],
),
),
),
),
barrierColor: Colors.black.withOpacity(0.5),
);
}
void _showCarDetailsDialog(
BuildContext context,
MapPassengerController mapPassengerController,
CarType carType,
TextToSpeechController textToSpeechController) {
Get.dialog(
Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(28.0)),
backgroundColor: Colors.transparent,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
children: [
Container(
margin: const EdgeInsets.only(top: 60),
padding: const EdgeInsets.fromLTRB(24, 70, 24, 24),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(28.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
carType.carType.tr,
style: AppStyle.headTitle.copyWith(fontSize: 22),
),
const SizedBox(width: 8),
InkWell(
onTap: () => textToSpeechController.speakText(
_getCarDescription(
mapPassengerController, carType)),
borderRadius: BorderRadius.circular(20),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Icon(Icons.volume_up_rounded,
color: AppColor.primaryColor, size: 24),
),
)
],
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.05),
borderRadius: BorderRadius.circular(16)),
child: Text(
_getCarDescription(mapPassengerController, carType),
textAlign: TextAlign.center,
style: AppStyle.subtitle.copyWith(
color: Colors.black87,
fontSize: 15,
height: 1.4,
),
),
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => Get.back(),
child: Text('Back'.tr,
style: TextStyle(color: Colors.grey.shade600)),
),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Select This Ride'.tr,
onPressed: () {
Get.back();
_handleCarSelection(
context, mapPassengerController, carType);
},
),
),
],
),
],
),
),
Positioned(
top: 0,
child: Hero(
tag: 'car_${carType.carType}',
child: Image.asset(carType.image, height: 130),
),
),
],
),
),
barrierColor: Colors.black.withOpacity(0.6),
);
}
// --- Logic Helpers (Keep unchanged) ---
String _getCarDescription(
MapPassengerController mapPassengerController, CarType carType) {
switch (carType.carType) {
case 'Comfort':
return mapPassengerController.endNameAddress
.toLowerCase()
.contains("airport".tr)
? "Best choice for a comfortable car with a flexible route and stop points. This airport offers visa entry at this price."
.tr
: 'Best choice for comfort car and flexible route and stops point'
.tr;
case 'Fixed Price':
return 'This trip goes directly from your starting point to your destination for a fixed price. The driver must follow the planned route'
.tr;
case 'Electric':
return 'Travel in a modern, silent electric car. A premium, eco-friendly choice for a smooth ride.'
.tr;
case 'Scooter':
case 'Van':
return "Spacious van service ideal for families and groups. Comfortable, safe, and cost-effective travel together."
.tr;
case 'Pink Bike':
return 'This is for delivery or a motorcycle.'.tr;
case 'Mishwar Vip':
return "Perfect for passengers seeking the latest car models with the freedom to choose any route they desire"
.tr;
case 'Awfar Car':
return "Old and affordable, perfect for budget rides.".tr;
case 'Lady':
return "This trip is for women only".tr;
case 'Rayeh Gai':
return "Rayeh Gai: Round trip service for convenient travel between cities, easy and reliable."
.tr;
default:
return '';
}
}
void _handleCarSelection(BuildContext context,
MapPassengerController mapPassengerController, CarType carType) {
box.write(BoxName.carType, carType.carType);
mapPassengerController.totalPassenger =
_getOriginalPrice(carType, mapPassengerController);
if (carType.carType == 'Mishwar Vip') {
Get.back();
mapPassengerController.mishwariOption();
} else if (carType.carType == 'Lady') {
if (box.read(BoxName.gender).toString() != '') {
mapPassengerController.isBottomSheetShown = false;
mapPassengerController.update();
mapPassengerController.changeCashConfirmPageShown();
} else {
MyDialog().getDialog('Idintify gender',
'You should ideintify your gender for this type of trip!'.tr, () {
Get.to(() => PassengerProfilePage());
});
}
} else if (carType.carType == 'Rayeh Gai') {
Get.defaultDialog(
barrierDismissible: false,
title: "Select betweeen types".tr,
content:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
_buildRayehGaiOption(mapPassengerController, 'Awfar Car',
mapPassengerController.totalPassengerRayehGaiBalash),
_buildRayehGaiOption(mapPassengerController, 'Fixed Price',
mapPassengerController.totalPassengerRayehGai),
_buildRayehGaiOption(mapPassengerController, 'Comfort',
mapPassengerController.totalPassengerRayehGaiComfort)
]),
cancel: MyElevatedButton(
kolor: AppColor.redColor,
title: 'Cancel'.tr,
onPressed: () => Get.back()),
confirm: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Next'.tr,
onPressed: () {
mapPassengerController.isBottomSheetShown = false;
mapPassengerController.update();
mapPassengerController.changeCashConfirmPageShown();
}));
} else {
mapPassengerController.isBottomSheetShown = false;
mapPassengerController.update();
mapPassengerController.changeCashConfirmPageShown();
}
}
double _getOriginalPrice(
CarType carType, MapPassengerController mapPassengerController) {
switch (carType.carType) {
case 'Comfort':
return mapPassengerController.totalPassengerComfort;
case 'Fixed Price':
return mapPassengerController.totalPassengerSpeed;
case 'Electric':
return mapPassengerController.totalPassengerElectric;
case 'Awfar Car':
return mapPassengerController.totalPassengerBalash;
case 'Van':
return mapPassengerController.totalPassengerVan;
case 'Lady':
return mapPassengerController.totalPassengerLady;
default:
return 0.0;
}
}
Widget _buildRayehGaiOption(MapPassengerController mapPassengerController,
String carTypeName, double price) {
return GestureDetector(
onTap: () {
Get.back();
mapPassengerController.totalPassenger = price;
mapPassengerController.isBottomSheetShown = false;
mapPassengerController.update();
mapPassengerController.changeCashConfirmPageShown();
},
child: Container(
decoration: AppStyle.boxDecoration1,
padding: const EdgeInsets.all(8.0),
child: Column(children: [
Text(carTypeName.tr),
Text(price.toStringAsFixed(0))
])),
);
}
}
// --- BurcMoney Widget (Unchanged) ---
class BurcMoney extends StatelessWidget {
const BurcMoney({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (mapPassengerController) {
final passengerWallet =
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ??
0.0;
return mapPassengerController.data.isNotEmpty &&
mapPassengerController.isBottomSheetShown &&
!mapPassengerController.rideConfirm &&
passengerWallet < 0.0
? Positioned(
bottom: Get.height * .41,
left: 16,
right: 16,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.redColor.withOpacity(0.8),
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
const Icon(
Icons.warning_amber_rounded,
color: Colors.white,
size: 24,
),
const SizedBox(width: 8),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Negative Balance:'.tr} ',
style: AppStyle.subtitle.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
TextSpan(
text:
'${'You have a balance of'.tr} ${passengerWallet.toStringAsFixed(2)} ${box.read(BoxName.countryCode) == 'Egypt' ? 'LE'.tr : 'SYP'.tr} ${'due to a previous trip.'.tr}',
style: AppStyle.subtitle.copyWith(
color: Colors.white,
),
),
],
),
textAlign: TextAlign.start,
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: () async =>
await Get.find<TextToSpeechController>().speakText(
'${'you have a negative balance of'.tr}${passengerWallet.toStringAsFixed(2)}${' in your'.tr} ${AppInformation.appName}${' wallet due to a previous trip.'.tr}'),
child: const Icon(
Icons.headphones,
color: Colors.white,
),
),
],
),
),
)
: const SizedBox();
},
);
}
}
@@ -1,207 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/views/home/my_wallet/passenger_wallet.dart';
import '../../../constant/colors.dart';
import '../../../constant/info.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/payment/payment_controller.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
class CashConfirmPageShown extends StatelessWidget {
CashConfirmPageShown({super.key});
final PaymentController paymentController = Get.put(PaymentController());
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
// شرط الإظهار الرئيسي لم يتغير
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
// التحكم في ظهور اللوحة لم يتغير
transform: Matrix4.translationValues(
0, controller.isCashConfirmPageShown ? 0 : Get.height, 0),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
),
],
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 1. رأس الصفحة ---
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Payment Method'.tr,
style: AppStyle.headTitle.copyWith(fontSize: 24),
),
// زر الإغلاق (كان معلقاً في الكود القديم، تم تفعيله هنا)
IconButton(
onPressed: () => controller.changeCashConfirmPageShown(),
icon: const Icon(Icons.close, color: AppColor.writeColor),
),
],
),
const SizedBox(height: 16),
// --- 2. بطاقات اختيار الدفع ---
GetBuilder<PaymentController>(builder: (paymentCtrl) {
// نفس منطق تغيير اللون للسيارات النسائية
final bool isLadyRide = box.read(BoxName.carType) == 'Lady' ||
box.read(BoxName.carType) == 'Pink Bike';
final Color selectedColor =
isLadyRide ? Colors.pink.shade300 : AppColor.primaryColor;
return Column(
children: [
// بطاقة المحفظة
_buildPaymentOptionCard(
icon: Icons.account_balance_wallet_outlined,
title: '${AppInformation.appName} Balance'.tr,
subtitle:
'${'Balance:'.tr} ${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'SYP'.tr}',
isSelected: paymentCtrl.isWalletChecked,
selectedColor: selectedColor,
onTap: () =>
paymentCtrl.onChangedPaymentMethodWallet(true),
),
const SizedBox(height: 12),
// بطاقة الكاش
_buildPaymentOptionCard(
icon: Icons.money_rounded,
title: 'Cash'.tr,
subtitle: 'Pay directly to the captain'.tr,
isSelected: paymentCtrl.isCashChecked,
selectedColor: selectedColor,
onTap: () =>
paymentCtrl.onChangedPaymentMethodCash(true),
),
],
);
}),
const SizedBox(height: 24),
// --- 3. أزرار التأكيد (بنفس منطقك القديم تماماً) ---
GetBuilder<PaymentController>(builder: (paymentCtrl) {
final bool isWalletSufficient = (double.tryParse(
box.read(BoxName.passengerWalletTotal) ?? '0') ??
0) >=
controller.totalPassenger;
// إذا تم اختيار المحفظة والرصيد غير كافٍ
if (paymentCtrl.isWalletChecked && !isWalletSufficient) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MyElevatedButton(
title: 'Top up Balance to continue'.tr,
onPressed: () =>
Get.to(() => const PassengerWallet()),
kolor: AppColor.redColor,
),
const SizedBox(height: 8),
TextButton(
onPressed: () =>
paymentCtrl.onChangedPaymentMethodCash(true),
child: Text("Or pay with Cash instead".tr,
style: TextStyle(color: AppColor.primaryColor)),
)
],
);
}
// في كل الحالات الأخرى (كاش، أو محفظة برصيد كافٍ)
else {
return MyElevatedButton(
title: 'Confirm & Find a Ride'.tr,
onPressed: () {
// --- نفس منطقك القديم بالضبط ---
controller.changeCashConfirmPageShown();
// controller.isSearchingWindow = true;
controller.startSearchingForDriver();
// controller.update();
},
);
}
}),
],
),
),
),
);
});
}
// --- ويدجت مساعدة لبناء بطاقة الدفع ---
Widget _buildPaymentOptionCard({
required IconData icon,
required String title,
required String subtitle,
required bool isSelected,
required VoidCallback onTap,
required Color selectedColor,
}) {
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isSelected
? selectedColor.withOpacity(0.1)
: AppColor.writeColor.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
? selectedColor
: AppColor.writeColor.withOpacity(0.2),
width: isSelected ? 2.0 : 1.0,
),
),
child: Row(
children: [
Icon(icon,
color: isSelected ? selectedColor : AppColor.writeColor,
size: 28),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style:
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Text(subtitle,
style: AppStyle.subtitle.copyWith(
color: AppColor.writeColor.withOpacity(0.7))),
],
),
),
if (isSelected)
Icon(Icons.check_circle_rounded, color: selectedColor, size: 24),
],
),
),
);
}
}
@@ -1,106 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
import 'hexegone_clipper.dart';
GetBuilder<MapPassengerController> hexagonClipper() {
return GetBuilder<MapPassengerController>(
builder: ((controller) => controller.rideConfirm
? Positioned(
top: Get.height * .1,
left: Get.width * .1,
right: Get.width * .1,
child: ClipPath(
clipper:
HexagonClipper(), // CustomClipper to create a hexagon shape
child: AnimatedContainer(
duration: const Duration(microseconds: 300),
height: 250,
width: 250,
// decoration: AppStyle.boxDecoration,
// gradient: const LinearGradient(
// colors: [AppColor.greenColor, AppColor.secondaryColor],
// begin: Alignment.topLeft,
// end: Alignment.bottomCenter,
// ),
// border: Border.all(),
// color: AppColor.secondaryColor,
// borderRadius: BorderRadius.circular(15)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Waiting for Driver ...'.tr,
style: AppStyle.title,
),
// IconButton(
// onPressed: () {
// },
// icon: const Icon(Icons.add),
// ),
// Text(
// controller.dataCarsLocationByPassenger['message']
// [controller.carsOrder]['phone']
// .toString(),
// style: AppStyle.title,
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'${controller.dataCarsLocationByPassenger['message'][controller.carsOrder]['first_name']} ${controller.dataCarsLocationByPassenger['message'][controller.carsOrder]['last_name']}',
style: AppStyle.title,
),
Text(
'Age is '.tr +
controller
.dataCarsLocationByPassenger['message']
[controller.carsOrder]['age']
.toString(),
style: AppStyle.title,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
controller.dataCarsLocationByPassenger['message']
[controller.carsOrder]['make']
.toString(),
style: AppStyle.title,
),
Text(
controller.dataCarsLocationByPassenger['message']
[controller.carsOrder]['model']
.toString(),
style: AppStyle.title,
),
],
),
Text(
'Rating is '.tr +
controller.dataCarsLocationByPassenger['message']
[controller.carsOrder]['ratingDriver']
.toString(),
style: AppStyle.title,
),
Container(
decoration: BoxDecoration(border: Border.all(width: 2)),
child: Text(
controller.dataCarsLocationByPassenger['message']
[controller.carsOrder]['car_plate']
.toString(),
style: AppStyle.title,
),
),
],
),
),
),
)
: const SizedBox()));
}
@@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
// import 'package:intl/intl.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
class DriverTimeArrivePassengerPage extends StatelessWidget {
const DriverTimeArrivePassengerPage({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) {
return controller.remainingTime == 0
? Positioned(
bottom: Get.height * .35,
right: Get.width * .05,
child: Stack(
alignment: Alignment.center,
children: [
Container(
decoration: AppStyle.boxDecoration,
// width: 50,
// height: 50,
child: Padding(
padding: const EdgeInsetsDirectional.only(
start: 5, end: 5),
child: Column(
children: [
Text(
controller.durationByPassenger.toString() +
' to arrive you.'.tr,
style: AppStyle.title,
),
Text(
" ${DateFormat('h:mm a').format(controller.newTime)}",
style: AppStyle.title,
),
],
),
))
],
),
)
: const SizedBox();
},
);
}
}
@@ -1,457 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/table_names.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../main.dart';
// ---------------------------------------------------
// -- Widget for Destination Point Search (Optimized) --
// ---------------------------------------------------
/// A more optimized and cleaner implementation of the destination search form.
///
/// Improvements:
/// 1. **Widget Refactoring**: The UI is broken down into smaller, focused widgets
/// (_SearchField, _QuickActions, _SearchResults) to prevent unnecessary rebuilds.
/// 2. **State Management Scoping**: `GetBuilder` is used only on widgets that
/// actually need to update, not the entire form.
/// 3. **Reduced Build Logic**: Logic like reading from `box` is done once.
/// 4. **Readability**: Code is cleaner and easier to follow.
GetBuilder<MapPassengerController> formSearchPlacesDestenation() {
// --- [تحسين] قراءة القيم مرة واحدة في بداية البناء ---
// Store box values in local variables to avoid repeated calls inside the build method.
final String addWorkValue =
box.read(BoxName.addWork)?.toString() ?? 'addWork';
final String addHomeValue =
box.read(BoxName.addHome)?.toString() ?? 'addHome';
// --- [ملاحظة] تأكد من أن القيم الأولية موجودة ---
// This initialization can be moved to your app's startup logic or a splash screen controller.
if (addWorkValue.isEmpty || addHomeValue.isEmpty) {
box.write(BoxName.addWork, 'addWork');
box.write(BoxName.addHome, 'addHome');
}
return GetBuilder<MapPassengerController>(
id: 'destination_form', // Use an ID to allow targeted updates
builder: (controller) {
return Column(
children: [
// --- Widget for the search text field ---
_SearchField(controller: controller),
// --- Widget for "Add Work" and "Add Home" buttons ---
_QuickActions(
controller: controller,
addWorkValue: addWorkValue,
addHomeValue: addHomeValue,
),
// --- Widget for displaying search results, wrapped in its own GetBuilder ---
_SearchResults(),
],
);
},
);
}
// ---------------------------------------------------
// -- Private Helper Widgets for Cleaner Code --
// ---------------------------------------------------
/// A dedicated widget for the search input field.
class _SearchField extends StatefulWidget {
final MapPassengerController controller;
const _SearchField({required this.controller});
@override
State<_SearchField> createState() => _SearchFieldState();
}
class _SearchFieldState extends State<_SearchField> {
Timer? _debounce;
// --- [إصلاح] Listener لتحديث الواجهة عند تغيير النص لإظهار/إخفاء زر المسح ---
void _onTextChanged() {
if (mounted) {
setState(() {});
}
}
@override
void initState() {
super.initState();
// Add listener to update the suffix icon when text changes
widget.controller.placeDestinationController.addListener(_onTextChanged);
}
// --- [تحسين] إضافة Debouncer لتأخير البحث أثناء الكتابة ---
void _onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
if (query.length > 2) {
widget.controller.getPlaces();
widget.controller.changeHeightPlaces();
} else if (query.isEmpty) {
widget.controller.clearPlacesDestination();
widget.controller.changeHeightPlaces();
}
});
}
@override
void dispose() {
_debounce?.cancel();
// Remove the listener to prevent memory leaks
widget.controller.placeDestinationController.removeListener(_onTextChanged);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: [
Expanded(
child: TextFormField(
controller: widget.controller.placeDestinationController,
onChanged: _onSearchChanged,
decoration: InputDecoration(
hintText: widget.controller.hintTextDestinationPoint,
hintStyle: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
prefixIcon:
const Icon(Icons.search, color: AppColor.primaryColor),
// --- [إصلاح] تم استبدال Obx بشرط بسيط لأن `setState` يعيد بناء الواجهة الآن ---
suffixIcon: widget
.controller.placeDestinationController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear, color: Colors.grey[400]),
onPressed: () {
widget.controller.placeDestinationController.clear();
// The listener will automatically handle the UI update
// And _onSearchChanged will handle clearing the results
},
)
: null, // Use null instead of SizedBox for better practice
contentPadding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.primaryColor),
),
filled: true,
fillColor: Colors.grey[50],
),
),
),
const SizedBox(width: 8.0),
IconButton(
onPressed: () {
widget.controller.changeMainBottomMenuMap();
widget.controller.changePickerShown();
},
icon: Icon(Icons.location_on_outlined,
color: AppColor.accentColor, size: 30),
tooltip: widget.controller.isAnotherOreder
? 'Pick destination on map'.tr
: 'Pick on map'.tr,
),
],
),
);
}
}
/// A dedicated widget for the quick action buttons (Work/Home).
class _QuickActions extends StatelessWidget {
final MapPassengerController controller;
final String addWorkValue;
final String addHomeValue;
const _QuickActions({
required this.controller,
required this.addWorkValue,
required this.addHomeValue,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildQuickActionButton(
icon: Icons.work_outline,
text: addWorkValue == 'addWork' ? 'Add Work'.tr : 'To Work'.tr,
onTap: () {
if (addWorkValue == 'addWork') {
controller.workLocationFromMap = true;
controller.changeMainBottomMenuMap();
controller.changePickerShown();
} else {
_handleQuickAction(controller, BoxName.addWork, 'To Work');
}
},
onLongPress: () => _showChangeLocationDialog(controller, 'Work'),
),
_buildQuickActionButton(
icon: Icons.home_outlined,
text: addHomeValue == 'addHome' ? 'Add Home'.tr : 'To Home'.tr,
onTap: () {
if (addHomeValue == 'addHome') {
controller.homeLocationFromMap = true;
controller.changeMainBottomMenuMap();
controller.changePickerShown();
} else {
_handleQuickAction(controller, BoxName.addHome, 'To Home');
}
},
onLongPress: () => _showChangeLocationDialog(controller, 'Home'),
),
],
),
);
}
}
/// A dedicated widget for the search results list.
/// It uses its own `GetBuilder` to only rebuild when the list of places changes.
class _SearchResults extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
id: 'places_list', // Use a specific ID for targeted updates
builder: (controller) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: controller.placesDestination.isNotEmpty ? 300 : 0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
),
margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: controller.placesDestination.length,
separatorBuilder: (context, index) =>
const Divider(height: 1, color: Colors.grey),
itemBuilder: (BuildContext context, int index) {
final res = controller.placesDestination[index];
final title = res['name_ar'] ?? res['name'] ?? 'Unknown Place';
final address = res['address'] ?? 'Details not available';
final latitude = res['latitude'];
final longitude = res['longitude'];
return ListTile(
leading: const Icon(Icons.place, size: 30, color: Colors.grey),
title: Text(
title,
style:
AppStyle.subtitle.copyWith(fontWeight: FontWeight.w500),
),
subtitle: Text(
address,
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
trailing: IconButton(
icon: const Icon(Icons.favorite_border, color: Colors.grey),
onPressed: () => _handleAddToFavorites(
context, latitude, longitude, title),
),
onTap: () => _handlePlaceSelection(
controller, latitude, longitude, title, index),
);
},
),
);
},
);
}
// --- [تحسين] استخراج المنطق المعقد إلى دوال مساعدة ---
Future<void> _handleAddToFavorites(BuildContext context, dynamic latitude,
dynamic longitude, String title) async {
if (latitude != null && longitude != null) {
await sql.insertMapLocation({
'latitude': latitude,
'longitude': longitude,
'name': title,
'rate': 'N/A',
}, TableName.placesFavorite);
Toast.show(
context,
'$title ${'Saved Successfully'.tr}',
AppColor.primaryColor,
);
} else {
Toast.show(
context,
'Invalid location data',
AppColor.redColor,
);
}
}
Future<void> _handlePlaceSelection(MapPassengerController controller,
dynamic latitude, dynamic longitude, String title, int index) async {
if (latitude == null || longitude == null) {
Toast.show(Get.context!, 'Invalid location data', AppColor.redColor);
return;
}
// Save to recent locations
await sql.insertMapLocation({
'latitude': latitude,
'longitude': longitude,
'name': title,
'rate': 'N/A',
'createdAt': DateTime.now().toIso8601String(),
}, TableName.recentLocations);
final destLatLng = LatLng(
double.parse(latitude.toString()), double.parse(longitude.toString()));
if (controller.isAnotherOreder) {
// **Another Order Flow**
await _handleAnotherOrderSelection(controller, destLatLng);
} else {
// **Regular Order Flow**
_handleRegularOrderSelection(controller, destLatLng, index);
}
}
Future<void> _handleAnotherOrderSelection(
MapPassengerController controller, LatLng destination) async {
controller.myDestination = destination;
controller.clearPlacesDestination(); // Helper method in controller
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${controller.myDestination.latitude},${controller.myDestination.longitude}');
controller.isPickerShown = false;
controller.passengerStartLocationFromMap = false;
controller.changeMainBottomMenuMap();
controller.showBottomSheet1();
}
void _handleRegularOrderSelection(
MapPassengerController controller, LatLng destination, int index) {
controller.passengerLocation = controller.newMyLocation;
controller.myDestination = destination;
controller.convertHintTextDestinationNewPlaces(index);
controller.clearPlacesDestination(); // Helper method in controller
controller.changeMainBottomMenuMap();
controller.passengerStartLocationFromMap = true;
controller.isPickerShown = true;
}
}
// ---------------------------------------------------
// -- Helper Functions (kept from original code) --
// ---------------------------------------------------
Widget _buildQuickActionButton({
required IconData icon,
required String text,
VoidCallback? onTap,
VoidCallback? onLongPress,
}) {
return InkWell(
onTap: onTap,
onLongPress: onLongPress,
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: AppColor.blueColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: AppColor.blueColor.withOpacity(0.3)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.blueColor),
const SizedBox(height: 4.0),
Text(
text,
textAlign: TextAlign.center,
style: AppStyle.title.copyWith(
color: AppColor.blueColor, fontWeight: FontWeight.w500),
),
],
),
),
);
}
void _showChangeLocationDialog(
MapPassengerController controller, String locationType) {
Get.defaultDialog(
title: 'Change $locationType location?'.tr,
middleText: '',
confirm: MyElevatedButton(
title: 'Yes'.tr,
onPressed: () {
Get.back();
if (locationType == 'Work') {
controller.workLocationFromMap = true;
} else {
controller.homeLocationFromMap = true;
}
controller.changeMainBottomMenuMap();
controller.changePickerShown();
},
),
);
}
void _handleQuickAction(
MapPassengerController controller, String boxName, String hintText) async {
// --- [تحسين] قراءة وتحويل الإحداثيات بأمان أكبر ---
try {
final locationString = box.read(boxName).toString();
final parts = locationString.split(',');
final latLng = LatLng(
double.parse(parts[0]),
double.parse(parts[1]),
);
controller.hintTextDestinationPoint = hintText;
controller.changeMainBottomMenuMap();
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${latLng.latitude},${latLng.longitude}',
);
controller.currentLocationToFormPlaces = false;
controller.clearPlacesDestination(); // Helper method in controller
controller.passengerStartLocationFromMap = false;
controller.isPickerShown = false;
controller.showBottomSheet1();
} catch (e) {
// Handle error if parsing fails
print("Error handling quick action: $e");
Toast.show(Get.context!, "Failed to get location".tr, AppColor.redColor);
}
}
@@ -1,138 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
// ---------------------------------------------------
// -- Widget for Start Point Search (Updated) --
// ---------------------------------------------------
GetBuilder<MapPassengerController> formSearchPlacesStart() {
return GetBuilder<MapPassengerController>(
id: 'start_point_form', // إضافة معرف لتحديث هذا الجزء فقط عند الحاجة
builder: (controller) => Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: [
// --- حقل البحث النصي ---
Expanded(
child: TextFormField(
controller: controller.placeStartController,
onChanged: (value) {
if (controller.placeStartController.text.length > 2) {
controller.getPlacesStart();
} else if (controller.placeStartController.text.isEmpty) {
controller.clearPlacesStart();
}
},
decoration: InputDecoration(
hintText: 'Search for a starting point'.tr,
hintStyle:
AppStyle.subtitle.copyWith(color: Colors.grey[600]),
prefixIcon:
const Icon(Icons.search, color: AppColor.primaryColor),
suffixIcon: controller.placeStartController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear, color: Colors.grey[400]),
onPressed: () {
controller.placeStartController.clear();
controller.clearPlacesStart();
},
)
: null,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: AppColor.primaryColor),
),
filled: true,
fillColor: Colors.grey[50],
),
),
),
const SizedBox(width: 8.0),
// --- أيقونة اختيار الموقع من الخريطة (الجزء المضاف) ---
IconButton(
onPressed: () {
// هذا السطر مهم جداً: نخبر الكونترولر أننا نحدد نقطة البداية الآن
controller.passengerStartLocationFromMap = true;
// إخفاء القائمة السفلية وفتح مؤشر الخريطة (Picker)
controller.changeMainBottomMenuMap();
controller.changePickerShown();
},
icon: Icon(Icons.location_on_outlined,
color: AppColor.accentColor, size: 30),
tooltip: 'Pick start point on map'.tr,
),
],
),
),
// --- قائمة نتائج البحث ---
AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: controller.placesStart.isNotEmpty ? 300 : 0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
),
margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: controller.placesStart.length,
separatorBuilder: (context, index) =>
const Divider(height: 1, color: Colors.grey),
itemBuilder: (BuildContext context, int index) {
var res = controller.placesStart[index];
var title = res['name_ar'] ?? res['name'] ?? 'Unknown Place';
var address = res['address'] ?? 'Details not available';
return ListTile(
leading: const Icon(Icons.place, size: 30, color: Colors.grey),
title: Text(title,
style: AppStyle.subtitle
.copyWith(fontWeight: FontWeight.w500)),
subtitle: Text(address,
style: TextStyle(color: Colors.grey[600], fontSize: 12)),
onTap: () {
var latitude = res['latitude'];
var longitude = res['longitude'];
if (latitude != null && longitude != null) {
// تحديث موقع الراكب (نقطة الانطلاق) بناءً على الاختيار
controller.passengerLocation =
LatLng(double.parse(latitude), double.parse(longitude));
// تحديث النص في الحقل
controller.placeStartController.text = title;
// مسح النتائج
controller.clearPlacesStart();
// إغلاق القائمة والعودة للخريطة لرؤية النتيجة (اختياري حسب منطق تطبيقك)
controller.changeMainBottomMenuMap();
controller.update();
}
},
);
},
),
),
],
),
);
}
@@ -1,167 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/table_names.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../main.dart';
GetBuilder<MapPassengerController> formSearchPlaces(int index) {
// DbSql sql = DbSql.instance;
return GetBuilder<MapPassengerController>(
builder: (controller) => Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Container(
decoration:
const BoxDecoration(color: AppColor.secondaryColor),
child: TextField(
decoration: InputDecoration(
border: const OutlineInputBorder(
borderRadius: BorderRadius.only(),
gapPadding: 4,
borderSide: BorderSide(
color: AppColor.redColor,
width: 2,
)),
suffixIcon: const Icon(Icons.search),
hintText: controller.hintTextwayPoint0.tr,
hintStyle: AppStyle.title,
hintMaxLines: 1,
prefixIcon: IconButton(
onPressed: () {
controller.allTextEditingPlaces[index].clear();
controller.clearPlaces(index);
},
icon: Icon(
Icons.clear,
color: Colors.red[300],
),
),
),
controller: controller.allTextEditingPlaces[index],
onChanged: (value) {
if (controller.allTextEditingPlaces[index].text.length >
5) {
controller.getPlacesListsWayPoint(index);
controller.changeHeightPlacesAll(index);
}
},
// onEditingComplete: () => controller.changeHeight(),
),
),
),
controller.placeListResponseAll[index].isEmpty
? InkWell(
onTap: () {
controller.startLocationFromMapAll[index] = true;
controller.wayPointIndex = index;
Get.back();
// controller.changeMainBottomMenuMap();
controller.changeWayPointStopsSheet();
controller.changePickerShown();
},
child: Text(
'Choose from Map'.tr + ' $index'.tr,
style:
AppStyle.title.copyWith(color: AppColor.blueColor),
),
)
: const SizedBox(),
Container(
height: controller.placeListResponseAll[index].isNotEmpty
? controller.height
: 0,
color: AppColor.secondaryColor,
child: ListView.builder(
itemCount: controller.placeListResponseAll[index].length,
itemBuilder: (BuildContext context, int i) {
var res = controller.placeListResponseAll[index][i];
return InkWell(
onTap: () async {
controller.changeHeightPlaces();
if (controller.currentLocationToFormPlacesAll[index] ==
true) {
controller.newStartPointLocation =
controller.passengerLocation;
} else {
controller.passengerLocation =
controller.newStartPointLocation;
}
controller.convertHintTextPlaces(index, res);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Image.network(
res['icon'],
width: 20,
),
IconButton(
onPressed: () async {
await sql.insertMapLocation({
'latitude': res['geometry']
['location']['lat'],
'longitude': res['geometry']
['location']['lng'],
'name': res['name'].toString(),
'rate': res['rating'].toString(),
}, TableName.placesFavorite);
Toast.show(
context,
'${res['name']} ${'Saved Sucssefully'.tr}',
AppColor.primaryColor);
},
icon: const Icon(Icons.favorite_border),
),
],
),
Column(
children: [
Text(
res['name'].toString(),
style: AppStyle.title,
),
Text(
res['vicinity'].toString(),
style: AppStyle.subtitle,
),
],
),
Column(
children: [
Text(
'rate',
style: AppStyle.subtitle,
),
Text(
res['rating'].toString(),
style: AppStyle.subtitle,
),
],
),
],
),
const Divider(
thickness: 1,
)
],
),
),
);
},
),
)
],
));
}
@@ -1,157 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:Tripz/controller/home/points_for_rider_controller.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
// import '../../../controller/functions/location_controller.dart'; // Un-comment if needed
// import '../../../controller/home/device_tier.dart'; // Removed to rely on Controller logic
import '../../../controller/home/map_passenger_controller.dart';
import '../../widgets/mycircular.dart';
import '../../widgets/mydialoug.dart';
class GoogleMapPassengerWidget extends StatelessWidget {
GoogleMapPassengerWidget({super.key});
final WayPointController wayPointController = Get.put(WayPointController());
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: Positioned(
bottom: Get.height * .2,
top: 0,
left: 0,
right: 0,
child: GoogleMap(
onMapCreated: controller.onMapCreated,
// ✅ Camera Bounds
cameraTargetBounds: CameraTargetBounds(controller.boundsdata),
// ✅ Performance: Smoother zoom limits for low-end devices
minMaxZoomPreference: controller.lowPerf
? const MinMaxZoomPreference(6, 17)
: const MinMaxZoomPreference(6, 18),
// ✅ Destination Selection on Long Press
onLongPress: (LatLng argument) {
MyDialog().getDialog('Are you want to go to this site'.tr, '',
() async {
controller.clearPolyline();
// Ensure we have car data available before routing
// if (controller.dataCarsLocationByPassenger != null) {
// i use this check if needed
if (controller.carsLocationByPassenger.isNotEmpty) {
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${argument.latitude},${argument.longitude}',
);
Get.back(); // Close Dialog
await controller.bottomSheet();
controller.showBottomSheet1();
} else {
Get.back();
Get.snackbar(
'We Are Sorry That we dont have cars in your Location!'
.tr,
'',
colorText: AppColor.redColor,
duration: const Duration(seconds: 5),
backgroundColor: AppColor.secondaryColor,
icon: const Icon(Icons.error, color: AppColor.redColor),
titleText: Text('Error'.tr,
style: const TextStyle(color: AppColor.redColor)),
messageText: Text(
'We Are Sorry That we dont have cars in your Location!'
.tr,
style: AppStyle.title),
);
}
});
},
// ✅ Hide UI elements on tap
onTap: (argument) {
controller.hidePlaces();
},
initialCameraPosition: CameraPosition(
target: controller.passengerLocation,
zoom: controller.lowPerf ? 14.5 : 15,
),
// ✅ Markers
markers: controller.markers.toSet(),
// ✅ Polygons (e.g., University/Country borders)
polygons: controller.polygons,
// ✅ Polylines: Switch to lighter version if lowPerf is detected
polylines: controller.lowPerf
? controller.polyLinesLight.toSet()
: controller.polyLines.toSet(),
// ✅ Map Type: Switch to Normal map on low-end devices to save RAM
mapType: controller.lowPerf
? MapType.normal
: (controller.mapType
? MapType.satellite
: MapType
.normal), // Changed terrain default to normal for better performance
// ✅ UI Settings for Performance
myLocationButtonEnabled: false,
mapToolbarEnabled: false,
tiltGesturesEnabled:
false, // Disable tilt to save GPU resources
// Lite Mode (Static image) only on very low-end Androids if needed,
// but usually handled by lowPerf logic in mapType/Traffic
liteModeEnabled: Platform.isAndroid && controller.lowPerf,
trafficEnabled: controller.mapTrafficON && !controller.lowPerf,
buildingsEnabled: !controller.lowPerf,
rotateGesturesEnabled:
!controller.lowPerf, // Disable rotation on low-end
// ✅ Camera Movement Logic
onCameraMove: (CameraPosition position) {
// 1. Always update current view target (for pickers)
controller.newMyLocation = position.target;
// 2. Handle Drag-to-Select for specific states
if (controller.startLocationFromMap == true) {
controller.newStartPointLocation = position.target;
} else if (controller.passengerStartLocationFromMap == true) {
controller.newStartPointLocation = position.target;
}
// 3. Handle Waypoints Dragging
int waypointsLength =
Get.find<WayPointController>().wayPoints.length;
if (waypointsLength > 0 &&
controller.wayPointIndex >= 0 &&
controller.wayPointIndex <
controller.placesCoordinate.length) {
controller.placesCoordinate[controller.wayPointIndex] =
'${position.target.latitude},${position.target.longitude}';
}
// 4. Throttle heavy calculations (Reverse Geocoding / API calls)
if (controller.lowPerf) {
controller.onCameraMoveThrottled(position);
}
},
myLocationEnabled: false,
),
),
);
}
}
@@ -1,52 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
class HexagonClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
final height = size.height;
final width = size.width;
final centerX = width / 2;
final centerY = height / 2;
final radius = width / 2;
const angle = 2 * pi / 10; // Angle between each side of the hexagon
// Start at the top right vertex of the hexagon
final startX = centerX + radius * cos(0);
final startY = centerY + radius * sin(0);
path.moveTo(startX, startY);
// Draw the remaining sides of the hexagon
for (int i = 1; i < 10; i++) {
final x = centerX + radius * cos(angle * i);
final y = centerY + radius * sin(angle * i);
path.lineTo(x, y);
}
path.close();
return path;
}
@override
bool shouldReclip(HexagonClipper oldClipper) => false;
}
class ArrowClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
path.moveTo(0, size.height / 2);
path.lineTo(size.width / 2, 0);
path.lineTo(size.width, size.height / 2);
path.lineTo(size.width / 2, size.height);
path.close();
return path;
}
@override
bool shouldReclip(ArrowClipper oldClipper) => false;
}
@@ -1,213 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import '../../../constant/colors.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/ios_live_activity_service.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/home/vip_waitting_page.dart';
import '../../../print.dart';
// --- الدالة الرئيسية بالتصميم الجديد ---
GetBuilder<MapPassengerController> leftMainMenuIcons() {
Get.put(TextToSpeechController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
// تم تعديل الموضع ليتناسب مع التصميم الجديد
top: Get.height * .01,
left: 0,
right: 0,
child: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(50.0), // لإنشاء شكل الكبسولة
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 8.0, sigmaY: 8.0), // تأثير الزجاج المصنفر
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.4), // لون شبه شفاف
borderRadius: BorderRadius.circular(50.0),
border: Border.all(color: AppColor.secondaryColor),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min, // ليأخذ الشريط حجم الأزرار فقط
children: [
// --- تم استخدام دالة مساعدة جديدة للزر ---
_buildMapActionButton(
icon: Icons.satellite_alt_outlined,
tooltip: 'Toggle Map Type',
onPressed: () => controller.changeMapType(),
),
// _buildVerticalDivider(),
// _buildMapActionButton(
// icon: Icons.traffic_outlined,
// tooltip: 'Toggle Traffic',
// onPressed: () => controller.changeMapTraffic(),
// ),
_buildVerticalDivider(),
_buildMapActionButton(
icon: Icons.my_location_rounded,
tooltip: 'Go to My Location',
onPressed: () {
controller.mapController?.animateCamera(
CameraUpdate.newLatLng(
LatLng(
controller.passengerLocation.latitude,
controller.passengerLocation.longitude,
),
),
);
},
),
_buildVerticalDivider(),
_buildMapActionButton(
icon: Octicons.watch,
tooltip: 'VIP Waiting Page',
onPressed: () => Get.to(() => VipWaittingPage()),
),
// _buildMapActionButton(
// icon: Octicons.ellipsis,
// tooltip: 'test',
// onPressed: () => Get.to(() => TestPage()),
// ),
],
),
),
),
),
),
),
);
}
// --- دالة مساعدة جديدة لإنشاء الأزرار بشكل أنيق ---
Widget _buildMapActionButton({
required IconData icon,
required String tooltip,
required VoidCallback onPressed,
}) {
return IconButton(
onPressed: onPressed,
icon: Icon(icon, color: AppColor.writeColor, size: 22),
tooltip: tooltip,
splashRadius: 22,
padding: const EdgeInsets.all(12),
constraints: const BoxConstraints(), // لإزالة المساحات الافتراضية
);
}
// --- ويدجت للفاصل الرأسي بين الأزرار ---
Widget _buildVerticalDivider() {
return Container(
height: 20,
width: 1,
color: AppColor.writeColor.withOpacity(0.2),
);
}
// --- باقي الكود الخاص بك يبقى كما هو بدون تغيير ---
class TestPage extends StatelessWidget {
const TestPage({super.key});
@override
Widget build(BuildContext context) {
final random = Random();
return Scaffold(
appBar: AppBar(
title: const Text('iOS Live Activity Test'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// زر البدء
ElevatedButton(
onPressed: () async {
print("🍎 محاولة تشغيل Live Activity (Start)...");
try {
await IosLiveActivityService.startRideActivity(
rideId: "123",
driverName: "تجربة مبدئية",
carDetails: "تويوتا • أسود",
etaText: "5 دقائق",
progress: 0.2,
);
Log.print(
"✅ تم تشغيل Live Activity بنجاح! أغلق الشاشة لترى النتيجة.");
} catch (e) {
Log.print("❌ خطأ في Start Live Activity: $e");
}
},
child: const Text('Start Activity'),
),
const SizedBox(height: 16),
// زر التحديث العشوائي
ElevatedButton(
onPressed: () async {
Log.print("🔄 محاولة تحديث Live Activity (Update)...");
// توليد بيانات عشوائية للاختبار
final statuses = ['waiting', 'ongoing'];
final status = statuses[random.nextInt(statuses.length)];
final int minutes = random.nextInt(15) + 1; // 1–15
final String eta = "$minutes دقائق";
final double progress = (random.nextDouble() * 0.9) + 0.05;
// بين 0.05 و 0.95 تقريبًا
try {
await IosLiveActivityService.updateRideActivity(
status: status,
driverName:
status == 'waiting' ? 'السائق في الطريق' : 'السائق معك',
carDetails: "تويوتا • أسود",
etaText: eta,
progress: progress,
);
Log.print(
"✅ تم تحديث Live Activity: status=$status, eta=$eta, progress=$progress");
} catch (e) {
Log.print("❌ خطأ في Update Live Activity: $e");
}
},
child: const Text('Update (Random)'),
),
const SizedBox(height: 16),
// زر الإنهاء
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
onPressed: () async {
Log.print("🛑 محاولة إنهاء Live Activity (End)...");
try {
await IosLiveActivityService.endRideActivity();
Log.print("✅ تم إنهاء Live Activity.");
} catch (e) {
Log.print("❌ خطأ في End Live Activity: $e");
}
},
child: const Text('End Activity'),
),
],
),
),
);
}
}
@@ -1,642 +0,0 @@
import 'package:Tripz/views/widgets/my_textField.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/map_passenger_controller.dart';
import 'package:Tripz/main.dart';
import 'package:Tripz/views/home/map_widget.dart/form_search_places_destenation.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../constant/colors.dart';
import '../../../constant/table_names.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/functions/tts.dart';
import '../../widgets/error_snakbar.dart';
import '../../widgets/mydialoug.dart';
import 'form_search_start.dart';
class MainBottomMenuMap extends StatelessWidget {
const MainBottomMenuMap({super.key});
@override
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
bottom: Get.height * .04, // Increased bottom padding
left: 16,
right: 16,
child: GestureDetector(
onTap: controller
.changeMainBottomMenuMap, // Make the whole area tappable
child: AnimatedContainer(
duration: const Duration(
milliseconds: 300), // Reduced duration for smoother animation
curve: Curves.easeInOut, // Added animation curve
height: controller.mainBottomMenuMapHeight,
decoration: BoxDecoration(
color: AppColor.secondaryColor, // Use a solid background color
borderRadius: BorderRadius.circular(16), // More rounded corners
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), // Add bouncing effect
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment
.stretch, // Stretch children to full width
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
controller.isMainBottomMenuMap
? 'Where are you going?'.tr
: 'Quick Actions'.tr,
style: AppStyle.title
.copyWith(fontWeight: FontWeight.bold),
),
IconButton(
onPressed: controller.changeMainBottomMenuMap,
icon: Icon(
controller.isMainBottomMenuMap
? Icons.keyboard_arrow_down_rounded
: Icons.keyboard_arrow_up_rounded,
size: 28,
color: AppColor.primaryColor,
),
),
],
),
),
if (controller.isMainBottomMenuMap) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: InkWell(
onTap: () => controller.changeMainBottomMenuMap(),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.primaryColor
.withOpacity(0.05), // Subtle background
borderRadius: BorderRadius.circular(12),
),
child: DefaultTextStyle(
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor),
child: Center(
child: controller.isPickerShown
? clickPointPosition(controller, context)
: whereWidgetSmall(controller),
),
),
),
),
),
const SizedBox(height: 8),
if (controller.recentPlaces.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text('Recent Places'.tr, style: AppStyle.subtitle),
SizedBox(
height: 30,
child: Center(
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: controller.recentPlaces.length,
separatorBuilder: (context, index) =>
const SizedBox(width: 8),
itemBuilder: (context, index) =>
_buildRecentPlaceButton(
controller, context, index),
),
),
),
],
),
),
] else ...[
if (!controller.isAnotherOreder)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'${'From:'.tr} ${controller.currentLocationString}'
.tr,
style: AppStyle.subtitle,
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: !controller.isAnotherOreder
? const SizedBox()
: formSearchPlacesStart(),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: formSearchPlacesDestenation(),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: GestureDetector(
onTap: () {
Get.dialog(
AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
title: Text('WhatsApp Location Extractor'.tr),
content: Form(
key: controller.sosFormKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyTextForm(
controller:
controller.whatsAppLocationText,
label: 'Location Link'.tr,
hint: 'Paste location link here'.tr,
type: TextInputType.url,
),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Go to this location'.tr,
onPressed: () async {
controller.goToWhatappLocation();
},
),
],
),
),
),
);
},
child: Container(
decoration: BoxDecoration(
color: Colors.blue.shade100, // Lighter background
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.blue.shade400), // Add a border
),
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.link, color: Colors.blue.shade700),
const SizedBox(width: 8),
Expanded(
child: Text(
'Paste WhatsApp location link'.tr,
style: TextStyle(color: Colors.blue.shade700),
),
),
const Icon(Icons.arrow_forward_ios_rounded,
size: 16, color: Colors.blueGrey),
],
),
),
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: OutlinedButton(
onPressed: () {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) =>
CupertinoActionSheet(
title: Text('Select Order Type'.tr),
message: Text('Choose who this order is for'.tr),
actions: <Widget>[
CupertinoActionSheetAction(
child: Text('I want to order for myself'.tr),
onPressed: () {
controller.changeisAnotherOreder(false);
Navigator.pop(context);
},
),
CupertinoActionSheetAction(
child: Text(
'I want to order for someone else'.tr),
onPressed: () {
controller.changeisAnotherOreder(true);
Navigator.pop(context);
},
),
],
cancelButton: CupertinoActionSheetAction(
isDefaultAction: true,
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel'.tr),
),
),
);
},
child: Text(
!controller.isAnotherOreder
? 'Order for someone else'.tr
: 'Order for myself'.tr,
),
),
),
],
const SizedBox(height: 8),
],
),
),
),
),
),
);
}
Widget _buildRecentPlaceButton(
MapPassengerController controller, BuildContext context, int index) {
final textToSpeechController = Get.find<TextToSpeechController>();
return InkWell(
onTap: () {
MyDialog().getDialog('Are you want to go this site'.tr, ' ', () async {
Get.back();
await controller.getLocation();
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${controller.recentPlaces[index]['latitude']},${controller.recentPlaces[index]['longitude']}',
);
controller.showBottomSheet1();
});
},
onLongPress: () {
MyDialog().getDialog(
"Are you sure to delete this location?".tr,
'',
() {
sql.deleteData(TableName.recentLocations,
controller.recentPlaces[index]['id']);
controller.getFavioratePlaces();
controller.update();
Get.back();
mySnackbarSuccess('deleted'.tr);
},
);
},
child: Container(
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.05), // Subtle background
borderRadius: BorderRadius.circular(12),
border: Border(
bottom: BorderSide(
color: AppColor.primaryColor.withOpacity(0.1), width: 1),
),
),
child: Text(controller.recentPlaces[index]['name'],
style: const TextStyle(fontSize: 14)),
),
);
}
Widget clickPointPosition(
MapPassengerController controller, BuildContext context) {
return TextButton(
onPressed: () async {
controller.clearPolyline();
controller.data = [];
if (controller.passengerStartLocationFromMap == true) {
controller.newMyLocation = controller.newStartPointLocation;
controller.changeMainBottomMenuMap();
await controller.getDirectionMap(
'${controller.newStartPointLocation.latitude},${controller.newStartPointLocation.longitude}',
'${controller.myDestination.latitude},${controller.myDestination.longitude}',
);
controller.currentLocationToFormPlaces = false;
controller.placesDestination = [];
controller.clearPlacesStart();
controller.clearPlacesDestination();
controller.passengerStartLocationFromMap = false;
controller.isPickerShown = false;
controller.showBottomSheet1();
} else if (controller.startLocationFromMap == true) {
controller.newMyLocation = controller.newStartPointLocation;
controller.hintTextStartPoint =
'${controller.newStartPointLocation.latitude.toStringAsFixed(4)} , ${controller.newStartPointLocation.longitude.toStringAsFixed(4)}';
controller.startLocationFromMap = false;
controller.isPickerShown = false;
} else if (controller.workLocationFromMap == true) {
controller.hintTextDestinationPoint = 'To Work'.tr;
box.write(BoxName.addWork,
'${controller.newMyLocation.latitude.toStringAsFixed(4)} , ${controller.newMyLocation.longitude.toStringAsFixed(4)}');
controller.newMyLocation = controller.newMyLocation;
controller.isPickerShown = false;
controller.workLocationFromMap = false;
Get.snackbar('Work Saved'.tr, '',
backgroundColor: AppColor.greenColor);
} else if (controller.homeLocationFromMap == true) {
controller.hintTextDestinationPoint = 'To Home'.tr;
box.write(BoxName.addHome,
'${controller.newMyLocation.latitude.toStringAsFixed(4)} , ${controller.newMyLocation.longitude.toStringAsFixed(4)}');
controller.newMyLocation = controller.newMyLocation;
controller.isPickerShown = false;
controller.homeLocationFromMap = false;
controller.update();
Get.snackbar('Home Saved'.tr, '',
backgroundColor: AppColor.greenColor);
} else {
controller.hintTextDestinationPoint =
'${controller.newMyLocation.latitude.toStringAsFixed(4)} , ${controller.newMyLocation.longitude.toStringAsFixed(4)}';
controller.myDestination = controller.newMyLocation;
controller.isPickerShown = false;
controller.changeMainBottomMenuMap();
controller.passengerStartLocationFromMap = true;
controller.isPickerShown = true;
if (controller.isAnotherOreder == false) {
await controller.mapController?.animateCamera(
CameraUpdate.newLatLng(LatLng(
controller.passengerLocation.latitude,
controller.passengerLocation.longitude)));
Get.defaultDialog(
title: 'Destination selected'.tr,
titleStyle: AppStyle.title,
content: Text(
'Now select start pick'.tr,
style: AppStyle.title,
),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
}));
}
if (controller.isWhatsAppOrder == true) {
Get.defaultDialog(
title: 'Destination selected'.tr,
titleStyle: AppStyle.title,
content: Text(
'Now select start pick'.tr,
style: AppStyle.title,
),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
}));
}
}
controller.placesDestination = [];
controller.placeDestinationController.clear();
// controller.showBottomSheet1();
controller.changeMainBottomMenuMap();
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
controller.passengerStartLocationFromMap
? Icons.location_on
: Icons.location_searching,
size: 20,
color: AppColor.primaryColor,
),
const SizedBox(width: 8),
Text(
controller.passengerStartLocationFromMap
? 'Confirm Pick-up Location'.tr
: "Set Location on Map".tr,
style: AppStyle.subtitle.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.primaryColor,
),
),
],
),
),
);
}
Widget whereWidgetSmall(MapPassengerController controller) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.location_searching, color: AppColor.primaryColor),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'Where to'.tr} ${(box.read(BoxName.name).toString().split(' ')[0])} ',
style: AppStyle.subtitle),
// if (controller.noCarString)
// Text('Nearest Car: ~'.tr,
// style: TextStyle(color: Colors.grey.shade600))
// else
// Text(
// controller.nearestCar != null
// ? 'Nearest Car: ${controller.nearestDistance.toStringAsFixed(0)} m'
// : 'No cars nearby'.tr,
// style: TextStyle(color: Colors.grey.shade600),
// ),
],
),
],
);
}
}
class FaviouratePlacesDialog extends StatelessWidget {
const FaviouratePlacesDialog({super.key});
@override
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Center(
child: InkWell(
onTap: () async {
List favoritePlaces =
await sql.getAllData(TableName.placesFavorite);
Get.defaultDialog(
title: 'Favorite Places'.tr,
content: SizedBox(
width: Get.width * .8,
height: 300,
child: favoritePlaces.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.star_border_rounded,
size: 99,
color: AppColor.accentColor,
),
Text(
'No favorite places yet!'.tr,
style: AppStyle.title,
),
],
),
)
: ListView.separated(
itemCount: favoritePlaces.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading:
const Icon(Icons.star, color: Colors.amber),
title: Text(favoritePlaces[index]['name'],
style: AppStyle.title),
trailing: IconButton(
icon: const Icon(Icons.delete_outline,
color: Colors.redAccent),
onPressed: () async {
await sql.deleteData(TableName.placesFavorite,
favoritePlaces[index]['id']);
Get.back();
Toast.show(
context,
'${'Deleted'.tr} ${favoritePlaces[index]['name']} from your favorites',
AppColor.redColor);
},
),
onTap: () async {
Get.back();
await controller.getLocation();
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}',
);
controller.showBottomSheet1();
},
);
},
),
),
confirm: MyElevatedButton(
title: 'Back'.tr, onPressed: () => Get.back()),
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.star_border_rounded,
color: AppColor.accentColor),
const SizedBox(width: 8),
Text('Favorite Places'.tr, style: AppStyle.title),
],
),
),
),
);
}
}
@@ -1,343 +0,0 @@
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/main.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/views/home/my_wallet/passenger_wallet.dart';
import 'package:Tripz/views/home/profile/complaint_page.dart';
import 'package:Tripz/views/home/profile/order_history.dart';
import 'package:Tripz/views/home/profile/promos_passenger_page.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../notification/notification_page.dart';
import '../HomePage/contact_us.dart';
import '../HomePage/share_app_page.dart';
import '../setting_page.dart';
import '../profile/passenger_profile_page.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class MapMenuWidget extends StatelessWidget {
const MapMenuWidget({super.key});
@override
Widget build(BuildContext context) {
Get.lazyPut(() => MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Stack(
children: [
// --- خلفية معتمة عند فتح القائمة ---
if (controller.widthMenu > 0)
GestureDetector(
onTap: controller.getDrawerMenu,
child: Container(
color: Colors.black.withOpacity(0.4),
),
),
// --- القائمة الجانبية المنزلقة ---
_buildSideMenu(controller),
// --- زر القائمة العائم ---
_buildMenuButton(controller),
],
),
);
}
// --- ويدجت لبناء زر القائمة ---
Widget _buildMenuButton(MapPassengerController controller) {
return Positioned(
top: 45,
left: 16,
child: SafeArea(
child: InkWell(
onTap: controller.getDrawerMenu,
borderRadius: BorderRadius.circular(50),
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.8),
shape: BoxShape.circle,
border:
Border.all(color: AppColor.writeColor.withOpacity(0.2)),
),
child: Icon(
controller.widthMenu > 0 ? Icons.close : Icons.menu,
color: AppColor.writeColor,
size: 26,
),
),
),
),
),
),
);
}
// --- ويدجت لبناء القائمة الجانبية ---
Widget _buildSideMenu(MapPassengerController controller) {
return AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.fastOutSlowIn,
top: 0,
bottom: 0,
left: controller.widthMenu > 0 ? 0 : -Get.width,
child: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
width: Get.width * 0.8,
constraints: const BoxConstraints(maxWidth: 320),
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.95),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
)
],
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildMenuHeader(),
_buildQuickActionButtons(),
const Divider(
color: Colors.white24,
indent: 16,
endIndent: 16,
height: 24),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8),
children: [
MenuListItem(
title: 'My Balance'.tr,
icon: Icons.account_balance_wallet_outlined,
onTap: () => Get.to(() => const PassengerWallet())),
MenuListItem(
title: 'Order History'.tr,
icon: Icons.history_rounded,
onTap: () => Get.to(() => const OrderHistory())),
MenuListItem(
title: 'Promos'.tr,
icon: Icons.local_offer_outlined,
onTap: () =>
Get.to(() => const PromosPassengerPage())),
MenuListItem(
title: 'Contact Us'.tr,
icon: Icons.contact_support_outlined,
onTap: () => Get.to(() => ContactUsPage())),
MenuListItem(
title: 'Complaint'.tr,
icon: Icons.flag_outlined,
onTap: () => Get.to(() => ComplaintPage())),
MenuListItem(
title: 'Driver'.tr,
icon: Ionicons.car_sport_outline,
onTap: () => _launchDriverAppUrl()),
MenuListItem(
title: 'Share App'.tr,
icon: Icons.share_outlined,
onTap: () => Get.to(() => ShareAppPage())),
MenuListItem(
title: 'Privacy Policy'.tr,
icon: Icons.shield_outlined,
onTap: () => launchUrl(Uri.parse(
'${AppLink.server}/privacy_policy.php')),
),
],
),
),
const Divider(
color: Colors.white24,
indent: 16,
endIndent: 16,
height: 1),
Padding(
padding: const EdgeInsets.all(8.0),
child: MenuListItem(
title: 'Logout'.tr,
icon: Icons.logout_rounded,
onTap: () {
Get.defaultDialog(
title: "Logout".tr,
middleText: "Are you sure you want to logout?".tr,
textConfirm: "Logout".tr,
textCancel: "Cancel".tr,
onConfirm: () {
// controller.logout();
Get.back();
},
);
},
color: Colors.red.shade300,
),
),
],
),
),
),
),
),
);
}
// --- ويدجت رأس القائمة ---
Widget _buildMenuHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 16),
child: Row(
children: [
const CircleAvatar(
radius: 30,
backgroundColor: AppColor.primaryColor,
child: Icon(Icons.person, color: AppColor.writeColor, size: 35),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
box.read(BoxName.name) ?? 'Guest',
style: AppStyle.headTitle.copyWith(fontSize: 20),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
"Tripz Passenger".tr,
style: AppStyle.title.copyWith(
color: AppColor.writeColor.withOpacity(0.7),
fontSize: 14),
),
],
),
),
],
),
);
}
// --- ويدجت الأزرار السريعة ---
Widget _buildQuickActionButtons() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildSmallActionButton(
icon: Icons.person_outline_rounded,
label: 'Profile'.tr,
onTap: () => Get.to(() => PassengerProfilePage())),
_buildSmallActionButton(
icon: Icons.notifications_none_rounded,
label: 'Alerts'.tr,
onTap: () => Get.to(() => const NotificationPage())),
_buildSmallActionButton(
icon: Icons.settings_outlined,
label: 'Settings'.tr,
onTap: () => Get.to(() => const SettingPage())),
],
),
);
}
Widget _buildSmallActionButton(
{required IconData icon,
required String label,
required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.writeColor.withOpacity(0.9), size: 24),
const SizedBox(height: 6),
Text(label,
style: AppStyle.subtitle.copyWith(
fontSize: 12, color: AppColor.writeColor.withOpacity(0.9))),
],
),
),
);
}
void _launchDriverAppUrl() async {
final String driverAppUrl;
if (defaultTargetPlatform == TargetPlatform.android) {
driverAppUrl =
'https://play.google.com/store/apps/details?id=com.Tripz_driver';
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
driverAppUrl = 'https://apps.apple.com/st/app/Tripz-driver/id6482995159';
} else {
return;
}
try {
final Uri url = Uri.parse(driverAppUrl);
if (await canLaunchUrl(url)) {
await launchUrl(url);
} else {
Get.snackbar('Error', 'Could not launch driver app store.');
}
} catch (e) {
Get.snackbar('Error', 'Could not open the link.');
}
}
}
// --- ويدجت عناصر القائمة بتصميم محسن ---
class MenuListItem extends StatelessWidget {
const MenuListItem({
super.key,
required this.title,
required this.onTap,
required this.icon,
this.color,
});
final String title;
final IconData icon;
final VoidCallback onTap;
final Color? color;
@override
Widget build(BuildContext context) {
return ListTile(
onTap: onTap,
leading: Icon(
icon,
size: 26,
color: color ?? AppColor.writeColor.withOpacity(0.8),
),
title: Text(
title.tr,
style: AppStyle.title.copyWith(
fontSize: 16,
color: color ?? AppColor.writeColor,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
splashColor: AppColor.primaryColor.withOpacity(0.2),
);
}
}
@@ -1,55 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../main.dart';
class MenuIconMapPageWidget extends StatelessWidget {
const MenuIconMapPageWidget({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
top: Get.height * .008,
left: box.read(BoxName.lang) != 'ar' ? 5 : null,
right: box.read(BoxName.lang) == 'ar' ? 5 : null,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.secondaryColor,
border: Border.all(color: AppColor.accentColor)),
child: AnimatedCrossFade(
sizeCurve: Curves.bounceOut,
duration: const Duration(
milliseconds: 300), // Adjust the duration as needed
crossFadeState: controller.heightMenuBool
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: IconButton(
onPressed: () {
controller.getDrawerMenu();
},
icon: const Icon(
Icons.close,
color: AppColor.primaryColor,
),
),
secondChild: IconButton(
onPressed: () {
controller.getDrawerMenu();
},
icon: const Icon(
Icons.menu,
color: AppColor.accentColor,
),
),
),
),
));
}
}
@@ -1,86 +0,0 @@
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/main.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NewMainBottomSheet extends StatelessWidget {
const NewMainBottomSheet({super.key});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 0,
left: 5,
right: 5,
child: Container(
decoration: AppStyle.boxDecoration,
width: Get.width,
height: Get.height * .15,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text('Home'.tr),
const Icon(Icons.home),
],
),
),
),
Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text('Work'.tr),
const Icon(Icons.work_outline),
],
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(15),
color: AppColor.blueColor.withOpacity(.5),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(Icons.search),
Text(
"${"Where you want go ".tr}${(box.read(BoxName.name).toString().split(' ')[0]).toString()} ?",
),
],
),
),
)
],
)
],
),
),
);
}
}
@@ -1,105 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class PassengerRideLocationWidget extends StatefulWidget {
const PassengerRideLocationWidget({super.key});
@override
State<PassengerRideLocationWidget> createState() =>
_PassengerRideLocationWidgetState();
}
class _PassengerRideLocationWidgetState
extends State<PassengerRideLocationWidget>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
@override
void initState() {
super.initState();
// --- إعداد الأنيميشن للأيقونة ---
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
)..repeat(reverse: true); // التكرار بشكل عكسي لإنشاء تأثير النبض
_animation = Tween<double>(begin: 0.9, end: 1.1).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
// --- نفس شرط الإظهار الخاص بك ---
return AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
bottom: controller.isPassengerRideLocationWidget
? 20
: -100, // حركة دخول وخروج ناعمة
left: 20,
right: 20,
child: ClipRRect(
borderRadius: BorderRadius.circular(50.0), // حواف دائرية بالكامل
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0), // تأثير زجاجي
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.85),
borderRadius: BorderRadius.circular(50.0),
border: Border.all(color: AppColor.writeColor.withOpacity(0.2)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// --- أيقونة متحركة لجذب الانتباه ---
ScaleTransition(
scale: _animation,
child: Icon(
Icons.location_on,
color: AppColor.primaryColor,
size: 28,
),
),
const SizedBox(width: 12),
// --- نص إرشادي واضح ---
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Set pickup location".tr,
style: AppStyle.title
.copyWith(fontWeight: FontWeight.bold),
),
Text(
"Move the map to adjust the pin".tr,
style: AppStyle.subtitle.copyWith(
color: AppColor.writeColor.withOpacity(0.7),
),
),
],
),
],
),
),
),
),
);
});
}
}
@@ -1,418 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/controller/functions/secure_storage.dart';
import 'package:Tripz/controller/home/payment/credit_card_controller.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/digit_obsecur_formate.dart';
import '../../../controller/home/map_passenger_controller.dart';
class PaymentMethodPage extends StatelessWidget {
const PaymentMethodPage({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
right: 5,
bottom: 5,
left: 5,
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
height: controller.isPaymentMethodPageShown
? controller.paymentPageShown
: 0,
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'My Card'.tr,
style: AppStyle.title.copyWith(fontSize: 22),
),
IconButton(
onPressed: () =>
controller.changePaymentMethodPageShown(),
icon: const Icon(Icons.close),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Add Card'.tr,
style: AppStyle.title,
),
// GetBuilder<CreditCardController>(
// builder: (controller) => IconButton(
// onPressed: () {
// // controller.scanCard();
// // Get.defaultDialog(content: OptionConfigureWidget(
// // initialOptions: scanOptions,
// // onScanOptionChanged: (newOptions) =>
// // scanOptions = newOptions,
// // ),
// // );
// },
// icon: const Icon(Icons.contact_emergency_sharp),
// ),
// )
],
),
const SizedBox(
height: 10,
),
const MyCreditCardWidget(),
const Spacer(),
GetBuilder<CreditCardController>(
builder: (controller) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyElevatedButton(
title: 'Add Credit Card'.tr,
onPressed: () async {
if (controller.formKey.currentState!
.validate()) {
SecureStorage().saveData(
BoxName.cardNumber,
controller
.cardNumberController.text);
SecureStorage().saveData(
BoxName.cardHolderName,
controller
.cardHolderNameController.text);
SecureStorage().saveData(
BoxName.cvvCode,
controller.cvvCodeController.text);
SecureStorage().saveData(
BoxName.expiryDate,
controller
.expiryDateController.text);
}
},
),
],
))
],
),
),
),
));
}
}
class MyCreditCardWidget extends StatelessWidget {
const MyCreditCardWidget({
super.key,
});
@override
Widget build(BuildContext context) {
Get.put(CreditCardController());
return GetBuilder<CreditCardController>(
builder: (controller) => Container(
height: Get.height * .4,
width: Get.width * .9,
decoration: const BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: BorderRadius.all(Radius.circular(15)),
gradient: LinearGradient(colors: [
AppColor.secondaryColor,
// AppColor.blueColor,
// AppColor.greenColor,
AppColor.accentColor,
// AppColor.primaryColor,
// AppColor.redColor,
// AppColor.yellowColor
]),
boxShadow: [
BoxShadow(
spreadRadius: 3,
offset: Offset(3, 3),
blurRadius: 3,
color: AppColor.redColor),
BoxShadow(
offset: Offset(-3, -3),
blurRadius: 3,
spreadRadius: 3,
color: AppColor.redColor),
],
),
child: Form(
key: controller.formKey,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
child: Row(
children: [
getCardIcon(controller.cardNumberController
.text), // Dynamic credit card icon
SizedBox(
width: Get.width * .03,
),
SizedBox(
width: Get.width * .25,
child: Text(
'Card Number'.tr,
style: AppStyle.title,
),
),
SizedBox(
width: Get.width * .03,
),
SizedBox(
width: Get.width * .4,
height: 70,
child: TextFormField(
maxLength: 16,
keyboardType: TextInputType.number,
controller: controller.cardNumberController,
style: const TextStyle(
color: AppColor.blueColor,
fontFamily: 'digital-counter-7',
fontWeight: FontWeight.bold),
decoration: const InputDecoration(
helperStyle: TextStyle(
fontFamily: 'digital-counter-7'),
// labelText: 'Card Number',
),
// inputFormatters: [DigitObscuringFormatter()],
validator: (value) {
if (value!.isEmpty || value.length != 16) {
return 'Please enter a valid 16-digit card number'
.tr;
}
return null;
},
),
),
],
),
),
Row(
children: [
const Icon(Icons.person),
SizedBox(
width: Get.width * .03,
),
SizedBox(
width: Get.width * .25,
child: Text(
'Holder Name',
style: AppStyle.title,
),
),
SizedBox(
width: Get.width * .03,
),
SizedBox(
width: Get.width * .3,
child: SizedBox(
// height: 50,
child: TextFormField(
style: AppStyle.title,
keyboardType: TextInputType.text,
// maxLength: 16,
controller: controller.cardHolderNameController,
decoration: const InputDecoration(
// labelText: 'Cardholder Name',
),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the cardholder name'
.tr;
}
return null;
},
),
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: Row(
children: [
const Icon(Icons.date_range_outlined),
SizedBox(
width: Get.width * .03,
),
Column(
children: [
SizedBox(
width: Get.width * .2,
child: Text(
'Expiry Date',
style: AppStyle.subtitle,
),
),
SizedBox(
width: Get.width * .1,
child: SizedBox(
height: 60,
child: TextFormField(
maxLength: 4,
keyboardType: TextInputType.datetime,
controller:
controller.expiryDateController,
style: AppStyle.title,
decoration: const InputDecoration(),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the expiry date'
.tr;
}
return null;
},
),
),
)
],
),
],
),
),
SizedBox(
width: Get.width * .4,
child: Row(
children: [
const Icon(Icons.security),
SizedBox(
width: Get.width * .021,
),
Column(
children: [
SizedBox(
width: Get.width * .2,
child: Text(
'CVV Code',
style: AppStyle.subtitle,
),
),
SizedBox(
width: Get.width * .2,
child: SizedBox(
height: 60,
child: TextFormField(
obscureText: true,
keyboardType: TextInputType.number,
style: const TextStyle(
color: AppColor.primaryColor,
fontFamily: 'digital-counter-7'),
maxLength: 3,
controller:
controller.cvvCodeController,
decoration: const InputDecoration(
// labelText: 'CVV Code',
),
validator: (value) {
if (value!.isEmpty &&
value.length != 3) {
return 'Please enter the CVV code'
.tr;
}
return null;
},
),
),
),
],
)
],
),
),
],
),
// const SizedBox(
// height: 20,
// ),
MyElevatedButton(
title: 'Save'.tr,
onPressed: () {
if (controller.formKey.currentState!.validate()) {
// final creditCard = CreditCardModel(
// cardNumber: controller.cardNumberController.text,
// cardHolderName:
// controller.cardHolderNameController.text,
// expiryDate: controller.expiryDateController.text,
// cvvCode: controller.cvvCodeController.text,
// );
// Process the credit card details
// You can use GetX to handle the logic here
if (controller.formKey.currentState!.validate()) {
SecureStorage().saveData(BoxName.cardNumber,
controller.cardNumberController.text);
SecureStorage().saveData(BoxName.cardHolderName,
controller.cardHolderNameController.text);
SecureStorage().saveData(BoxName.cvvCode,
controller.cvvCodeController.text);
SecureStorage().saveData(BoxName.expiryDate,
controller.expiryDateController.text);
}
}
},
),
],
),
))));
}
Widget getCardIcon(String cardNumber) {
String cardType = detectCardType(
cardNumber); // Function to detect card type based on the first digit
IconData iconData;
Color iconColor;
switch (cardType) {
case 'Visa':
iconData = Icons.credit_card_rounded;
iconColor = Colors.blue; // Change color for Visa cards
break;
case 'Mastercard':
iconData = Icons.credit_card_rounded;
iconColor = Colors.red; // Change color for Mastercard cards
break;
default:
iconData = Icons.credit_card_rounded;
iconColor = Colors.black; // Default color for other card types
break;
}
return Icon(
iconData,
color: iconColor,
);
}
String detectCardType(String cardNumber) {
if (cardNumber.startsWith('4')) {
return 'Visa';
} else if (cardNumber.startsWith('5')) {
return 'Mastercard';
} else {
return 'Other';
}
}
}
@@ -1,218 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/table_names.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import 'form_search_places_destenation.dart';
class PickerAnimtionContainerFormPlaces extends StatelessWidget {
PickerAnimtionContainerFormPlaces({
super.key,
});
final controller = MapPassengerController();
@override
Widget build(BuildContext context) {
// DbSql sql = DbSql.instance;
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
bottom: 0,
left: 0,
right: 5,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
height: controller.heightPickerContainer,
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColor.accentColor, offset: Offset(2, 2)),
BoxShadow(
color: AppColor.accentColor, offset: Offset(-2, -2))
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
)),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
controller.isPickerShown
? const SizedBox()
: Text(
'Hi, Where to '.tr,
style: AppStyle.title,
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 5,
),
controller.isPickerShown
? InkWell(
onTapDown: (details) {
controller.changePickerShown();
controller.changeHeightPlaces();
},
child: Container(
height: 7,
width: Get.width * .3,
decoration: BoxDecoration(
color: AppColor.accentColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: AppColor.accentColor,
)),
),
)
: const SizedBox(),
controller.isPickerShown
? InkWell(
onTap: () {},
child: formSearchPlacesDestenation(),
)
: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
controller.changePickerShown();
},
child: Text(
"Pick your destination from Map".tr,
style: AppStyle.subtitle,
),
),
TextButton(
onPressed: () async {
List favoritePlaces = await sql
.getAllData(TableName.placesFavorite);
Get.defaultDialog(
title: 'Favorite Places'.tr,
content: SizedBox(
width: Get.width * .8,
height: 300,
child: favoritePlaces.isEmpty
? Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
const Icon(
Icons
.hourglass_empty_rounded,
size: 99,
color: AppColor
.primaryColor,
),
Text(
'You Dont Have Any places yet !'
.tr,
style: AppStyle.title,
),
],
),
)
: ListView.builder(
itemCount:
favoritePlaces.length,
itemBuilder:
(BuildContext context,
int index) {
return Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
TextButton(
onPressed: () async {
await controller
.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}',
);
controller
.changePickerShown();
controller
.changeBottomSheetShown(
forceValue:
true);
controller
.bottomSheet();
Get.back();
},
child: Text(
favoritePlaces[
index]['name'],
style:
AppStyle.title,
),
),
IconButton(
onPressed: () async {
await sql.deleteData(
TableName
.placesFavorite,
favoritePlaces[
index]
['id']);
Get.back();
Get.snackbar(
'Deleted ',
'${'You are Delete'.tr} ${favoritePlaces[index]['name']} from your list',
backgroundColor:
AppColor
.accentColor);
},
icon: const Icon(Icons
.favorite_outlined),
),
],
);
},
),
),
onCancel: () {},
);
},
child: Text(
"Go To Favorite Places".tr,
style: AppStyle.subtitle,
),
),
],
),
if (controller.isPickerShown &&
controller.placesDestination.isEmpty)
MyElevatedButton(
title: 'Go to this Target'.tr,
onPressed: () async {
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${controller.newMyLocation.latitude},${controller.newMyLocation.longitude}',
);
controller.changePickerShown();
controller.changeBottomSheetShown(
forceValue: true);
controller.bottomSheet();
// await sql
// .getAllData(TableName.placesFavorite)
},
),
if (controller.isPickerShown &&
controller.placesDestination.isEmpty)
const SizedBox(),
],
),
],
),
),
));
}
}
@@ -1,329 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/style.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/home/points_for_rider_controller.dart';
class PointsPageForRider extends StatelessWidget {
PointsPageForRider({
super.key,
});
MapPassengerController mapPassengerController =
Get.put(MapPassengerController());
@override
Widget build(BuildContext context) {
Get.put(WayPointController());
return GetBuilder<MapPassengerController>(builder: (controller) {
return Positioned(
bottom: 2,
left: 2,
right: 2,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
height: controller.wayPointSheetHeight,
decoration: AppStyle.boxDecoration,
child: ListView(
children: [
// const AppBarPointsPageForRider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
mapPassengerController.downPoints();
},
icon: const Icon(Icons.arrow_drop_down_circle_outlined),
),
GetBuilder<WayPointController>(builder: (wayPointController) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () {
wayPointController.addWayPoints();
controller.isWayPointStopsSheetUtilGetMap = true;
},
child: const Text('Add Stops'),
),
wayPointController.wayPoints.length > 1
? ElevatedButton(
onPressed: () async {
mapPassengerController
.getMapPointsForAllMethods();
},
child: const Text('Get Direction'),
)
: const SizedBox()
],
);
}),
],
),
SizedBox(
height: Get.height * .36,
child: GetBuilder<WayPointController>(
builder: (wayPointController) {
return ReorderableListView(
// The children of the list are the text fields
children: wayPointController.wayPoints
.asMap()
.entries
.map((entry) {
final index = entry.key;
final wayPoint = entry.value;
return Padding(
key: ValueKey(index),
padding: const EdgeInsets.all(1),
child: ListTile(
leading: Container(
decoration: BoxDecoration(
color: AppColor.deepPurpleAccent,
border: Border.all(),
shape: BoxShape.rectangle),
child: Padding(
padding: const EdgeInsets.all(2),
child: Text(
index.toString(),
style: AppStyle.title,
),
)),
title: InkWell(
onTap: () {
// showAddLocationDialog(context);
Get.defaultDialog(
content: SizedBox(
width: Get.width,
height: 400,
child: mapPassengerController
.placeListResponse[index]),
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(),
color:
AppColor.accentColor.withOpacity(.5)),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(index > 0
? mapPassengerController
.currentLocationStringAll[index]
.toString()
: ''),
const Icon(
Icons.reorder,
size: 20,
),
],
),
),
),
trailing: index > 0
? IconButton(
icon: const Icon(Icons.close),
onPressed: () {
wayPointController.removeTextField(index);
},
)
: IconButton(
icon: const Icon(
Icons.close,
color: AppColor.secondaryColor,
),
onPressed: () {},
)),
);
}).toList(),
// The callback when the user reorders the text fields
onReorder: (int oldIndex, int newIndex) {
wayPointController.reorderTextFields(oldIndex, newIndex);
},
);
}),
),
],
),
),
);
});
}
// GetBuilder<PointsForRiderController>(
// builder: (controller) => Container(
// decoration: AppStyle.boxDecoration,
// height: Get.height *
// .5, // height: controller.heightPointsPageForRider,
// width: Get.width,
// child: Column(
// children: [
// SizedBox(
// height: 300,
// child: ReorderableListView(
// onReorder: (oldIndex, newIndex) {
// if (oldIndex < newIndex) {
// newIndex -= 1;
// }
// pointsForRiderController.locations.insert(
// newIndex,
// pointsForRiderController.locations
// .removeAt(oldIndex));
// },
// children: [
// for (int i = 0;
// i < pointsForRiderController.locations.length;
// i++)
// ListTile(
// key: Key('$i'),
// title: DragTarget<int>(
// onAccept: (int data) {
// pointsForRiderController.locations
// .insert(i, 'New Text Field');
// },
// builder: (context, candidateData, rejectedData) {
// return Row(
// children: [
// SizedBox(
// width: 300,
// child: TextField(
// controller: TextEditingController(
// text: pointsForRiderController
// .locations[i]),
// onChanged: (value) {
// pointsForRiderController
// .locations[i] = value;
// },
// decoration: InputDecoration(
// prefixIcon: IconButton(
// onPressed: () {
// pointsForRiderController
// .removeLocation(i);
// },
// icon: const Icon(Icons.delete),
// ),
// labelText: 'Text Field ${i + 1}',
// border: const OutlineInputBorder(),
// ),
// ),
// ),
// IconButton(
// onPressed: () {
// // pointsForRiderController.onReorder(
// // index, newIndex);
// },
// icon: const Icon(Icons.reorder),
// ),
// ],
// );
// },
// ),
// ),
// ],
// ),
// ),
// ElevatedButton(
// onPressed: () {
// pointsForRiderController.addLocation('location');
// },
// child: const Text('Add Text Field'),
// ),
// ],
// ),
// ));
}
void showAddLocationDialog(BuildContext context, int index) {
final TextEditingController locationController = TextEditingController();
// Get.put(WayPointController());
showDialog(
context: context,
builder: (context) {
return Dialog.fullscreen(
// title: const Text('Add Location'),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(
Icons.close,
size: 40,
),
),
Text(
'Add Location'.tr,
style: AppStyle.title,
),
const Icon(
Icons.clear,
color: AppColor.secondaryColor,
)
],
),
// SizedBox(
// width: Get.width,
// child: formSearchCaptain(),
// ),
],
),
);
},
);
}
class AppBarPointsPageForRider extends StatelessWidget {
const AppBarPointsPageForRider({
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {},
icon: const Icon(
Icons.arrow_back_ios_new_rounded,
color: AppColor.primaryColor,
),
),
Container(
child: Row(
children: [
const CircleAvatar(
backgroundColor: AppColor.primaryColor,
maxRadius: 15,
child: Icon(
Icons.person,
color: AppColor.secondaryColor,
),
),
TextButton(
onPressed: () {},
child: Text(
"Switch Rider".tr,
style: AppStyle.title,
),
),
],
),
),
const Icon(
Icons.clear,
color: AppColor.secondaryColor,
)
],
);
}
}
@@ -1,332 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
// تأكد من المسارات
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/profile/profile_controller.dart';
import '../../../main.dart';
import '../../../views/home/profile/complaint_page.dart';
class RideBeginPassenger extends StatelessWidget {
const RideBeginPassenger({super.key});
@override
Widget build(BuildContext context) {
final ProfileController profileController = Get.put(ProfileController());
final AudioRecorderController audioController =
Get.put(AudioRecorderController());
return Obx(() {
final controller = Get.find<MapPassengerController>();
// شرط الإظهار
final bool isVisible =
controller.currentRideState.value == RideState.inProgress &&
controller.isStartAppHasRide == false;
return AnimatedPositioned(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOutCubic,
// تم تقليل قيمة الإخفاء لأن الارتفاع الكلي للنافذة أصبح أصغر
bottom: isVisible ? 0 : -300,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
spreadRadius: 2,
offset: const Offset(0, -3),
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 1. مقبض السحب
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
),
const SizedBox(height: 12),
// 2. هيدر المعلومات (سائق + سيارة + سعر)
_buildCompactHeader(controller),
const SizedBox(height: 12),
// خط فاصل خفيف
const Divider(
height: 1, thickness: 0.5, color: Color(0xFFEEEEEE)),
const SizedBox(height: 12),
// 3. الأزرار (إجراءات)
_buildCompactActionButtons(
context, controller, profileController, audioController),
// إضافة هامش سفلي بسيط لرفع الأزرار عن حافة الشاشة
const SizedBox(height: 5),
],
),
),
),
);
});
}
// --- الهيدر (بدون تغيير، ممتاز) ---
Widget _buildCompactHeader(MapPassengerController controller) {
return Row(
children: [
// صورة السائق
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.5), width: 1.5),
),
child: CircleAvatar(
radius: 24,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
onBackgroundImageError: (_, __) => const Icon(Icons.person),
),
),
const SizedBox(width: 10),
// الاسم ومعلومات السيارة
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
controller.driverName,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 4),
const Icon(Icons.star, color: Colors.amber, size: 14),
Text(
controller.driverRate,
style: const TextStyle(
fontSize: 12, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 2),
Row(
children: [
Flexible(
child: Text(
'${controller.model} • ',
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: Colors.grey[100],
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.circular(4),
),
child: Text(
controller.licensePlate,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 11,
fontWeight: FontWeight.w900,
),
),
),
],
),
],
),
),
// السعر
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.08),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
NumberFormat('#,###').format(controller.totalPassenger),
style: const TextStyle(
fontWeight: FontWeight.w900,
fontSize: 16,
color: AppColor.primaryColor,
),
),
Text('SYP',
style: TextStyle(fontSize: 9, color: Colors.grey[600])),
],
),
),
],
);
}
// --- الأزرار (بدون تغيير) ---
Widget _buildCompactActionButtons(
BuildContext context,
MapPassengerController controller,
ProfileController profileController,
AudioRecorderController audioController) {
return SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_compactBtn(
icon: Icons.sos_rounded,
label: 'SOS'.tr,
color: AppColor.redColor,
bgColor: AppColor.redColor.withOpacity(0.1),
onTap: () async {
if (box.read(BoxName.sosPhonePassenger) == null) {
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
} else {
makePhoneCall('112');
}
},
),
_compactBtn(
icon: FontAwesome.whatsapp,
label: 'WhatsApp'.tr,
color: const Color(0xFF25D366),
bgColor: const Color(0xFF25D366).withOpacity(0.1),
onTap: () async {
if (box.read(BoxName.sosPhonePassenger) == null) {
await profileController.updatField(
'sosPhone', TextInputType.phone);
} else {
final phone = controller.formatSyrianPhoneNumber(
box.read(BoxName.sosPhonePassenger).toString());
controller.sendWhatsapp(phone);
}
},
),
_compactBtn(
icon: Icons.share,
label: 'Share'.tr,
color: AppColor.primaryColor,
bgColor: AppColor.primaryColor.withOpacity(0.1),
onTap: () async => await controller.shareTripWithFamily(),
),
GetBuilder<AudioRecorderController>(
init: audioController,
builder: (audioCtx) {
return _compactBtn(
icon: audioCtx.isRecording
? Icons.stop_circle_outlined
: Icons.mic_none_outlined,
label: audioCtx.isRecording ? 'Stop'.tr : 'Record'.tr,
color: audioCtx.isRecording
? AppColor.redColor
: AppColor.primaryColor,
bgColor: audioCtx.isRecording
? AppColor.redColor.withOpacity(0.1)
: AppColor.primaryColor.withOpacity(0.1),
onTap: () async {
if (!audioCtx.isRecording) {
await audioCtx.startRecording();
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
} else {
await audioCtx.stopRecording();
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
}
},
);
},
),
_compactBtn(
icon: Icons.info_outline_rounded,
label: 'Report'.tr,
color: Colors.grey[700]!,
bgColor: Colors.grey[200]!,
onTap: () => Get.to(() => ComplaintPage()),
),
],
),
);
}
Widget _compactBtn({
required IconData icon,
required String label,
required Color color,
required Color bgColor,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: bgColor,
shape: BoxShape.circle,
),
child: Icon(icon, size: 20, color: color),
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey[700],
fontWeight: FontWeight.w500),
),
],
),
);
}
}
@@ -1,315 +0,0 @@
import 'package:Tripz/controller/functions/launch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
// ... استيراد ملفاتك الأخرى ...
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/profile/profile_controller.dart';
import '../../../main.dart';
class RideFromStartApp extends StatelessWidget {
const RideFromStartApp({super.key});
@override
Widget build(BuildContext context) {
final profileController = Get.put(ProfileController());
final MapPassengerController controller =
Get.find<MapPassengerController>();
return Obx(() {
final bool isRideActive =
controller.currentRideState.value == RideState.inProgress &&
controller.isStartAppHasRide == true;
if (!isRideActive) return const SizedBox();
// قراءة البيانات
final rideData = controller.rideStatusFromStartApp['data'] ?? {};
final driverId = rideData['driver_id'];
final driverName = rideData['driverName'] ?? 'Captain'.tr;
final driverRate = controller.driverRate;
final carType = rideData['carType'] ?? 'Car'.tr;
final carModel = controller.model ?? '';
final arrivalTime = box.read(BoxName.arrivalTime) ?? '--:--';
// تحديد البيانات للعرض
final displayTime = controller.stringRemainingTimeRideBegin.isNotEmpty
? controller.stringRemainingTimeRideBegin
: arrivalTime;
final displayDistance = rideData['distance']?.toStringAsFixed(1) ?? 'N/A';
final displayPrice = rideData['price']?.toString() ?? 'N/A';
return Positioned(
left: 0,
right: 0,
bottom: 0, // ملتصق بالأسفل تماماً
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: const BoxDecoration(
color: Colors.white, // خلفية بيضاء نظيفة
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 15.0,
spreadRadius: 5.0,
offset: Offset(0, -5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// 1. مقبض صغير للدلالة على السحب (تصميم جمالي)
Center(
child: Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 15),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
),
// 2. حالة الرحلة + معلومات السائق
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// صورة السائق
Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
shape: BoxShape.circle,
border:
Border.all(color: AppColor.primaryColor, width: 2),
),
child: CircleAvatar(
radius: 28,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/$driverId.jpg'),
),
),
const SizedBox(width: 12),
// الاسم والسيارة
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
driverName,
style: AppStyle.title.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Row(
children: [
Icon(Icons.star,
color: AppColor.yellowColor, size: 16),
const SizedBox(width: 4),
Text(
driverRate,
style: AppStyle.title.copyWith(
fontSize: 13, fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
Container(width: 1, height: 12, color: Colors.grey),
const SizedBox(width: 8),
Text(
"$carType - $carModel",
style: AppStyle.title.copyWith(
fontSize: 13, color: Colors.grey[600]),
),
],
),
],
),
),
// حالة الرحلة (نص ملون)
_buildStatusBadge(controller.currentRideState.value),
],
),
const SizedBox(height: 20),
// 3. شريط المعلومات (وقت، مسافة، سعر)
Container(
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 10),
decoration: BoxDecoration(
color: AppColor.grayColor
.withOpacity(0.1), // خلفية رمادية خفيفة جداً
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoColumn(
Icons.access_time_filled, displayTime, "Time".tr),
_buildVerticalDivider(),
_buildInfoColumn(Icons.location_on, "$displayDistance KM",
"Distance".tr),
_buildVerticalDivider(),
_buildInfoColumn(
Icons.attach_money, "$displayPrice SYP", "Price".tr),
],
),
),
const SizedBox(height: 20),
// 4. أزرار التحكم (SOS & Share)
Row(
children: [
// زر المشاركة (يأخذ مساحة أكبر)
Expanded(
flex: 2,
child: ElevatedButton.icon(
onPressed: () => _checkAndCall(
controller.sendWhatsapp, profileController),
icon:
const Icon(FontAwesome.whatsapp, color: Colors.white),
label: Text("Share Trip".tr,
style: const TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.greenColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
),
),
const SizedBox(width: 10),
// زر الاستغاثة SOS
Expanded(
flex: 1,
child: ElevatedButton.icon(
onPressed: () =>
makePhoneCall(box.read(BoxName.sosPhonePassenger)),
icon: const Icon(Icons.sos, color: Colors.white),
label: Text("SOS".tr,
style: const TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.redColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
),
),
],
),
],
),
),
);
});
}
// ------------------------- Widgets Helper Methods -------------------------
Widget _buildStatusBadge(RideState status) {
String text;
Color color;
if (status == RideState.inProgress) {
text = 'On Trip'.tr;
color = AppColor.primaryColor;
} else if (status == RideState.driverArrived) {
text = 'Arrived'.tr;
color = AppColor.greenColor;
} else {
text = 'Coming'.tr;
color = AppColor.yellowColor;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.5)),
),
child: Text(
text,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
);
}
Widget _buildInfoColumn(IconData icon, String value, String label) {
return Column(
children: [
Icon(icon,
color: AppColor.secondaryColor,
size: 22), // افترضت أن السكندري لون داكن، أو استخدم Primary
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w800,
fontSize: 15,
color: Colors.black87,
),
),
Text(
label,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
),
),
],
);
}
Widget _buildVerticalDivider() {
return Container(
height: 30,
width: 1,
color: Colors.grey[300],
);
}
// دالة المساعدة للمنطق (بقيت كما هي ولكن تم تمرير البروفايل كونترولر)
Future<void> _checkAndCall(
Function(String) action, ProfileController profileController) async {
String? sosPhone = box.read(BoxName.sosPhonePassenger);
if (sosPhone == null || sosPhone == 'sos') {
await profileController.updatField('sosPhone', TextInputType.phone);
sosPhone = profileController.prfoileData['sosPhone'];
}
if (sosPhone != null && sosPhone != 'sos') {
action(sosPhone);
} else {
Get.snackbar('Warning'.tr, 'Please set a valid SOS phone number.'.tr,
backgroundColor: AppColor.redColor, colorText: Colors.white);
}
}
}
@@ -1,469 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/map_passenger_controller.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class SearchingCaptainWindow extends StatefulWidget {
const SearchingCaptainWindow({super.key});
@override
State<SearchingCaptainWindow> createState() => _SearchingCaptainWindowState();
}
class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// [تعديل 1] نستخدم Obx للاستماع إلى التغييرات في حالة الرحلة
return Obx(() {
// ابحث عن الكنترولر مرة واحدة
final controller = Get.find<MapPassengerController>();
// [تعديل 2] شرط الإظهار يعتمد الآن على حالة الرحلة مباشرة
final bool isVisible =
controller.currentRideState.value == RideState.searching;
return AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: isVisible ? 0 : -Get.height * 0.45, // زيادة الارتفاع قليلاً
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
offset: const Offset(0, -5),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// --- 1. أنيميشن الرادار ---
_buildRadarAnimation(controller),
const SizedBox(height: 20),
// --- 2. زر الإلغاء ---
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: () {
// [تعديل 3] استدعاء دالة الإلغاء الموحدة
controller.changeCancelRidePageShow();
// ();
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColor.writeColor,
side:
BorderSide(color: AppColor.writeColor.withOpacity(0.3)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text('Cancel Search'.tr),
),
),
],
),
),
);
});
}
// --- ويدجت بناء أنيميشن الرادار ---
Widget _buildRadarAnimation(MapPassengerController controller) {
return SizedBox(
height: 180, // ارتفاع ثابت لمنطقة الأنيميشن
child: Stack(
alignment: Alignment.center,
children: [
// --- دوائر الرادار المتحركة (تبقى كما هي) ---
...List.generate(3, (index) {
return FadeTransition(
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
),
),
child: ScaleTransition(
scale: Tween<double>(begin: 0.3, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
),
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.7),
width: 2,
),
),
),
),
);
}),
// --- المحتوى في المنتصف ---
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
// [تعديل 4] النص يأتي مباشرة من الكنترولر
controller.driversStatusForSearchWindow,
style: AppStyle.headTitle.copyWith(fontSize: 20),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Searching for the nearest captain...'.tr,
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor.withOpacity(0.7)),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// --- [!! تعديل جوهري !!] ---
// لم نعد بحاجة لـ buildTimerForIncrease
// المؤقت الرئيسي في الكنترولر هو من يقرر متى يعرض الحوار
// وهذا الجزء من الواجهة أصبح "غبياً" (لا يحتوي على منطق)
// الكنترولر سيستدعي _showIncreaseFeeDialog مباشرة
SizedBox(
height: 40,
width: 40,
child: Stack(
fit: StackFit.expand,
children: [
CircularProgressIndicator(
strokeWidth: 3,
color: AppColor.primaryColor,
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
),
Center(
child: Icon(
Icons.search,
color: AppColor.writeColor.withOpacity(0.8),
),
),
],
),
),
],
),
],
),
);
}
}
// --- [!! تعديل جوهري !!] ---
// تم حذف دالة `buildTimerForIncrease` بالكامل.
// تم حذف دالة `_showIncreaseFeeDialog` من هذا الملف.
// لماذا؟ لأن الكنترولر الآن هو المسؤول الوحيد عن إظهار الحوار.
// دالة `_showIncreaseFeeDialog` موجودة بالفعل داخل `map_passenger_controller.dart`
// وسيتم استدعاؤها من `_handleRideState` عند انتهاء مهلة الـ 90 ثانية.
// // --- الويدجت الرئيسية بالتصميم الجديد ---
// class SearchingCaptainWindow extends StatefulWidget {
// const SearchingCaptainWindow({super.key});
// @override
// State<SearchingCaptainWindow> createState() => _SearchingCaptainWindowState();
// }
// class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
// with SingleTickerProviderStateMixin {
// late AnimationController _animationController;
// @override
// void initState() {
// super.initState();
// _animationController = AnimationController(
// vsync: this,
// duration: const Duration(seconds: 2),
// )..repeat();
// }
// @override
// void dispose() {
// _animationController.dispose();
// super.dispose();
// }
// @override
// Widget build(BuildContext context) {
// return GetBuilder<MapPassengerController>(
// builder: (controller) {
// return AnimatedPositioned(
// duration: const Duration(milliseconds: 300),
// curve: Curves.easeInOut,
// bottom: controller.isSearchingWindow ? 0 : -Get.height * 0.4,
// left: 0,
// right: 0,
// child: Container(
// padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// borderRadius: const BorderRadius.only(
// topLeft: Radius.circular(24),
// topRight: Radius.circular(24),
// ),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.2),
// blurRadius: 20,
// offset: const Offset(0, -5),
// ),
// ],
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // --- 1. أنيميشن الرادار ---
// _buildRadarAnimation(controller),
// const SizedBox(height: 20),
// // --- 2. زر الإلغاء ---
// SizedBox(
// width: double.infinity,
// child: OutlinedButton(
// onPressed: () {
// // --- نفس منطقك للإلغاء ---
// controller.changeCancelRidePageShow();
// },
// style: OutlinedButton.styleFrom(
// foregroundColor: AppColor.writeColor,
// side: BorderSide(
// color: AppColor.writeColor.withOpacity(0.3)),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12)),
// padding: const EdgeInsets.symmetric(vertical: 12),
// ),
// child: Text('Cancel Search'.tr),
// ),
// ),
// ],
// ),
// ),
// );
// },
// );
// }
// // --- ويدجت بناء أنيميشن الرادار ---
// Widget _buildRadarAnimation(MapPassengerController controller) {
// return SizedBox(
// height: 180, // ارتفاع ثابت لمنطقة الأنيميشن
// child: Stack(
// alignment: Alignment.center,
// children: [
// // --- دوائر الرادار المتحركة ---
// ...List.generate(3, (index) {
// return FadeTransition(
// opacity: Tween<double>(begin: 1.0, end: 0.0).animate(
// CurvedAnimation(
// parent: _animationController,
// curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
// ),
// ),
// child: ScaleTransition(
// scale: Tween<double>(begin: 0.3, end: 1.0).animate(
// CurvedAnimation(
// parent: _animationController,
// curve: Interval((index) / 3, 1.0, curve: Curves.easeInOut),
// ),
// ),
// child: Container(
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// border: Border.all(
// color: AppColor.primaryColor.withOpacity(0.7),
// width: 2,
// ),
// ),
// ),
// ),
// );
// }),
// // --- المحتوى في المنتصف ---
// Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// controller.driversStatusForSearchWindow,
// style: AppStyle.headTitle.copyWith(fontSize: 20),
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 8),
// Text(
// 'Searching for the nearest captain...'.tr,
// style: AppStyle.subtitle
// .copyWith(color: AppColor.writeColor.withOpacity(0.7)),
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 16),
// // --- استدعاء نفس دالة المؤقت الخاصة بك ---
// buildTimerForIncrease(controller),
// ],
// ),
// ],
// ),
// );
// }
// }
// // --- نفس دالة المؤقت الخاصة بك مع تعديلات شكلية بسيطة ---
// Widget buildTimerForIncrease(MapPassengerController mapPassengerController) {
// return StreamBuilder<int>(
// stream: Stream.periodic(const Duration(seconds: 1))
// .map((_) => ++mapPassengerController.currentTimeSearchingCaptainWindow),
// initialData: 0,
// builder: (context, snapshot) {
// if (snapshot.hasData && snapshot.data! > 45) {
// // --- عرض زر زيادة الأجرة بنفس منطقك القديم ---
// return TextButton(
// onPressed: () =>
// _showIncreaseFeeDialog(context, mapPassengerController),
// child: Text(
// "No one accepted? Try increasing the fare.".tr,
// style: AppStyle.title.copyWith(
// color: AppColor.primaryColor,
// decoration: TextDecoration.underline),
// textAlign: TextAlign.center,
// ),
// );
// }
// final double progress = (snapshot.data ?? 0).toDouble() / 30.0;
// return SizedBox(
// height: 40,
// width: 40,
// child: Stack(
// fit: StackFit.expand,
// children: [
// CircularProgressIndicator(
// value: progress,
// strokeWidth: 3,
// color: AppColor.primaryColor,
// backgroundColor: AppColor.primaryColor.withOpacity(0.2),
// ),
// Center(
// child: Text(
// '${snapshot.data ?? 0}',
// style: AppStyle.title.copyWith(
// color: AppColor.writeColor, fontWeight: FontWeight.bold),
// ),
// ),
// ],
// ),
// );
// },
// );
// }
// // --- دالة لعرض نافذة زيادة الأجرة (مأخوذة من منطقك القديم) ---
// void _showIncreaseFeeDialog(
// BuildContext context, MapPassengerController mapPassengerController) {
// Get.defaultDialog(
// barrierDismissible: false,
// title: "Increase Your Trip Fee (Optional)".tr,
// titleStyle: AppStyle.title,
// content: Column(
// children: [
// Text(
// "We haven't found any drivers yet. Consider increasing your trip fee to make your offer more attractive to drivers."
// .tr,
// style: AppStyle.subtitle,
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 16),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// IconButton(
// onPressed: () {
// mapPassengerController.increasFeeFromPassenger.text =
// (mapPassengerController.totalPassenger + 3)
// .toStringAsFixed(1);
// mapPassengerController.update();
// },
// icon: const Icon(Icons.add_circle,
// size: 40, color: AppColor.greenColor),
// ),
// SizedBox(
// width: 100,
// child: Form(
// key: mapPassengerController.increaseFeeFormKey,
// child: MyTextForm(
// controller: mapPassengerController.increasFeeFromPassenger,
// label:
// mapPassengerController.totalPassenger.toStringAsFixed(2),
// hint:
// mapPassengerController.totalPassenger.toStringAsFixed(2),
// type: TextInputType.number,
// ),
// ),
// ),
// IconButton(
// onPressed: () {
// mapPassengerController.increasFeeFromPassenger.text =
// (mapPassengerController.totalPassenger - 3)
// .toStringAsFixed(1);
// mapPassengerController.update();
// },
// icon: const Icon(Icons.remove_circle,
// size: 40, color: AppColor.redColor),
// ),
// ],
// ),
// ],
// ),
// actions: [
// TextButton(
// child: Text("No, thanks".tr,
// style: const TextStyle(color: AppColor.redColor)),
// onPressed: () {
// Get.back();
// // mapPassengerController.cancelRide();
// mapPassengerController.changeCancelRidePageShow();
// },
// ),
// ElevatedButton(
// style: ElevatedButton.styleFrom(backgroundColor: AppColor.greenColor),
// child: Text("Increase Fee".tr),
// onPressed: () =>
// mapPassengerController.increaseFeeByPassengerAndReOrder(),
// ),
// ],
// );
// }
@@ -1,340 +0,0 @@
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/map_passenger_controller.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/links.dart';
import '../../../print.dart';
class CupertinoDriverListWidget extends StatelessWidget {
MapPassengerController mapPassengerController =
Get.put(MapPassengerController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Driver List'.tr), // Ensure text is properly localized
),
child: SafeArea(
child: mapPassengerController.driversForMishwari.isEmpty
? Center(
child: Text(
'No drivers available at the moment. Please try again later.'
.tr,
style: const TextStyle(
fontSize: 18, // Adjust the size as needed
fontWeight: FontWeight.w600,
color: CupertinoColors.inactiveGray, // Customize color
),
textAlign: TextAlign.center, // Center-align the text
),
)
: ListView.separated(
itemCount: mapPassengerController.driversForMishwari.length,
separatorBuilder: (context, index) =>
const Divider(height: 1),
itemBuilder: (context, index) {
var driver =
mapPassengerController.driversForMishwari[index];
return Container(
decoration: AppStyle.boxDecoration1,
child: CupertinoListTile(
padding: const EdgeInsets.symmetric(
vertical: 4, horizontal: 8),
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${driver['id']}.jpg',
),
child: Builder(
builder: (context) {
return Image.network(
'${AppLink.server}/portrate_captain_image/${driver['id']}.jpg',
fit: BoxFit.cover,
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child; // Image is loaded
} else {
return Center(
child: CircularProgressIndicator(
value: loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
(loadingProgress
.expectedTotalBytes ??
1)
: null,
),
);
}
},
errorBuilder: (BuildContext context,
Object error, StackTrace? stackTrace) {
return const Icon(
Icons
.person, // Icon to show when image fails to load
size: 25, // Adjust the size as needed
color: AppColor
.blueColor, // Color for the error icon
);
},
);
},
),
),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${driver['NAME'].toString().split(' ')[0]} ${driver['NAME'].toString().split(' ')[1]}',
style:
const TextStyle(fontWeight: FontWeight.bold),
),
Text('${'Age'.tr}: ${driver['age'].toString()}'),
Row(
children: [
const Icon(CupertinoIcons.star_fill,
size: 16,
color: CupertinoColors.systemYellow),
const SizedBox(width: 4),
Text(driver['rating']?.toStringAsFixed(1) ??
'N/A'.tr),
const SizedBox(width: 8),
Text('${'Rides'.tr}: ${driver['ride_count']}'),
],
),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'),
Text('${'Plate'.tr}: ${driver['car_plate']}'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
// width: Get.width * .3,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('${'Color'.tr}: ${driver['color']}'),
const SizedBox(width: 8),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: driver['color_hex']
.toString() ==
'null'
? Colors.amber
: hexToColor(driver['color_hex']
.toString()),
borderRadius:
BorderRadius.circular(4),
border: Border.all(),
),
),
],
),
),
],
),
],
),
onTap: () {
Log.print(' driver["id"]: ${driver['driver_id']}');
Get.find<MapPassengerController>().driverIdVip =
driver['driver_id'];
// Handle driver selection
Get.defaultDialog(
title:
'${'Selected driver'.tr}: ${driver['NAME']}',
content: Column(
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'),
Text(
'${'Plate'.tr}: ${driver['car_plate']}'),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
'${'Color'.tr}: ${driver['color']}'),
const SizedBox(width: 8),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: driver['color_hex']
.toString() ==
'null'
? Colors.amber
: hexToColor(
driver['color_hex']
.toString()),
borderRadius:
BorderRadius.circular(4),
border: Border.all(),
),
),
],
),
],
),
],
),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
showDateTimePickerDialog(driver);
}));
print('${'Selected driver'.tr}: ${driver['NAME']}');
// Get.back(); // Close the dialog
},
),
);
},
)),
);
}
Color hexToColor(String hexColor) {
hexColor = hexColor.replaceAll("#", "");
String colorString = "ff$hexColor";
return Color(int.parse(colorString, radix: 16));
}
void showDriverSelectionDialog(Map<String, dynamic> driver) {
Get.defaultDialog(
title: '${'Selected driver'.tr}: ${driver['name']}',
content: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'),
Text('${'Plate'.tr}: ${driver['car_plate']}'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${'Color'.tr}: ${driver['color']}'),
const SizedBox(width: 8),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: driver['color_hex'].toString() == 'null'
? Colors.amber
: hexToColor(driver['color_hex'].toString()),
borderRadius: BorderRadius.circular(4),
border: Border.all(),
),
),
],
),
],
),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
showDateTimePickerDialog(driver);
},
),
);
}
void showDateTimePickerDialog(Map<String, dynamic> driver) {
DateTime selectedDateTime = DateTime.now();
Get.defaultDialog(
barrierDismissible: false,
title: "Select date and time of trip".tr,
content: SizedBox(
// height: 400, // Adjust height as needed
width: double.maxFinite,
child: Column(
children: [
DateTimePickerWidget(),
],
),
),
confirm: MyElevatedButton(
title: 'Confirm Trip'.tr,
onPressed: () async {
DateTime selectedDateTime =
mapPassengerController.selectedDateTime.value;
// Save trip data and set up notifications
Get.back();
await mapPassengerController.saveTripData(driver, selectedDateTime);
},
),
cancel: MyElevatedButton(
kolor: AppColor.redColor,
title: 'Cancel'.tr,
onPressed: () {
Get.back();
},
),
);
}
}
class DateTimePickerWidget extends StatelessWidget {
final MapPassengerController controller = Get.put(MapPassengerController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
transitionBetweenRoutes: false,
automaticallyImplyLeading: false,
middle: Text('Date and Time Picker'.tr),
),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text(
'${'Selected Date and Time'.tr}: ${controller.selectedDateTime.value}',
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.center,
)),
const SizedBox(height: 20),
SizedBox(
height: 200,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
initialDateTime: controller.selectedDateTime.value,
onDateTimeChanged: (newDateTime) {
controller.updateDateTime(newDateTime);
},
),
),
],
),
),
);
}
}
@@ -1,63 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
GetBuilder<MapPassengerController> timerForCancelTripFromPassenger() {
return GetBuilder<MapPassengerController>(
builder: (controller) {
final isNearEnd =
controller.remainingTime <= 5; // Define a threshold for "near end"
return controller.remainingTime > 0 && controller.remainingTime != 25
? Positioned(
bottom: 5,
left: 10,
right: 10,
child: Container(
height: 180,
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: controller.progress,
// Set the color based on the "isNearEnd" condition
color: isNearEnd ? Colors.red : Colors.blue,
),
Text(
'${controller.remainingTime}',
style: AppStyle.number,
),
],
),
const SizedBox(
width: 30,
),
Text(
'You can cancel Ride now'.tr,
style: AppStyle.title,
)
],
),
Text(
'After this period\nYou can\'t cancel!'.tr,
style: AppStyle.title,
)
],
),
),
),
)
: const SizedBox();
},
);
}
@@ -1,148 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_passenger_controller.dart';
import 'ride_begin_passenger.dart';
class TimerToPassengerFromDriver extends StatelessWidget {
const TimerToPassengerFromDriver({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(builder: (controller) {
if (controller.remainingTime == 0 &&
(controller.isDriverInPassengerWay == true ||
controller.timeToPassengerFromDriverAfterApplied > 0)) {
// ) {
return Positioned(
left: 10,
right: 10,
bottom: 5,
child: Container(
decoration: AppStyle.boxDecoration,
height: controller.remainingTime == 0 &&
(controller.isDriverInPassengerWay == true ||
controller.timeToPassengerFromDriverAfterApplied > 0)
? 200
: 0,
// width: 100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'You Can cancel Ride After Captain did not come in the time'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
Stack(
children: [
LinearProgressIndicator(
backgroundColor: AppColor.accentColor,
color: controller
.remainingTimeToPassengerFromDriverAfterApplied <
60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 25,
borderRadius: BorderRadius.circular(15),
value: controller
.progressTimerToPassengerFromDriverAfterApplied
.toDouble(),
),
Center(
child: Text(
controller.stringRemainingTimeToPassenger,
style: AppStyle.title,
),
)
],
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.phone,
color: AppColor.blueColor,
),
),
controller.remainingTimeToPassengerFromDriverAfterApplied < 60
? MyElevatedButton(
title: 'You can cancel trip'.tr,
onPressed: () async {
await controller
.calculateDistanceBetweenPassengerAndDriverBeforeCancelRide();
})
: const SizedBox()
],
),
),
),
);
} else if (controller.remainingTime == 0 &&
controller.isDriverArrivePassenger == true) {
return Positioned(
left: 10,
right: 10,
bottom: 5,
child: Container(
decoration: AppStyle.boxDecoration,
height: controller.remainingTime == 0 &&
controller.isDriverArrivePassenger == true
? 150
: 0,
// width: 100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'The driver waiting you in picked location .'.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
Stack(
children: [
LinearProgressIndicator(
backgroundColor: AppColor.accentColor,
color:
controller.remainingTimeDriverWaitPassenger5Minute <
60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 50,
borderRadius: BorderRadius.circular(15),
value: controller
.progressTimerDriverWaitPassenger5Minute
.toDouble(),
),
Center(
child: Text(
controller
.stringRemainingTimeDriverWaitPassenger5Minute,
style: AppStyle.title,
),
)
],
),
Text(
'Please go to Car now '.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
],
),
),
),
);
} else {
return const RideBeginPassenger();
}
});
}
}
@@ -1,319 +0,0 @@
import 'package:Tripz/constant/links.dart';
import 'package:Tripz/views/home/profile/complaint_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/controller/profile/profile_controller.dart';
import 'package:Tripz/main.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
class VipRideBeginPassenger extends StatelessWidget {
const VipRideBeginPassenger({
super.key,
});
@override
Widget build(BuildContext context) {
ProfileController profileController = Get.put(ProfileController());
AudioRecorderController audioController =
Get.put(AudioRecorderController());
// Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(builder: (controller) {
if (controller.statusRideVip == 'Begin' ||
!controller.statusRideFromStart) {
return Positioned(
left: 10,
right: 10,
bottom: 10,
child: Container(
decoration: AppStyle.boxDecoration,
height: controller.statusRideVip == 'Begin' ? Get.height * .33 : 0,
// width: 100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg',
),
onBackgroundImageError: (_, __) {
// Handle error here
},
backgroundColor: Colors.grey,
child: const Icon(
Icons.person, // Default icon or placeholder
size: 30,
color: Colors.white,
), // Placeholder background color
),
Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
child: Text(
controller.driverName,
style: AppStyle.title,
),
),
),
const SizedBox(
height: 10,
),
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
controller.make,
style: AppStyle.title,
),
const SizedBox(
width: 10,
),
Text(
controller.model,
style: AppStyle.title,
),
],
),
),
),
],
),
Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(3),
child: Text(
'vip',
style: AppStyle.title,
),
),
),
Text(
'${controller.driverRate} 📈',
style: AppStyle.title,
),
],
),
],
),
// SizedBox(
// height: 5,
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration,
child: IconButton(
onPressed: () => Get.to(
() => ComplaintPage(),
transition: Transition.downToUp,
),
icon: const Icon(
Icons.note_add,
color: AppColor.redColor,
),
tooltip: ' Add Note', // Optional tooltip for clarity
),
),
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration,
child: audioController.isRecording == false
? IconButton(
onPressed: () async {
await audioController.startRecording();
Toast.show(context, 'Start Record'.tr,
AppColor.greenColor);
},
icon: const Icon(
Icons.play_circle_fill_outlined,
color: AppColor.greenColor,
),
tooltip:
' Add Note', // Optional tooltip for clarity
)
: IconButton(
onPressed: () async {
await audioController.stopRecording();
Toast.show(context, 'Record saved'.tr,
AppColor.greenColor);
},
icon: const Icon(
Icons.stop_circle,
color: AppColor.greenColor,
),
tooltip:
' Add Note', // Optional tooltip for clarity
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null) {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
makePhoneCall('122');
// box.read(BoxName.sosPhonePassenger));
}
},
icon: const Icon(
Icons.sos_rounded,
color: AppColor.redColor,
),
),
),
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null ||
box.read(BoxName.sosPhonePassenger) == 'sos') {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
String phoneNumber = box
.read(BoxName.sosPhonePassenger)
.toString();
// phoneNumber = phoneNumber.replaceAll('0', '');
var phone = box.read(BoxName.countryCode) ==
'Egypt'
? '+2${box.read(BoxName.sosPhonePassenger)}'
: '+962${box.read(BoxName.sosPhonePassenger)}';
controller.sendWhatsapp(phone);
}
},
icon: const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
),
),
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
await controller.shareTripWithFamily();
},
icon: const Icon(
AntDesign.Safety,
color: AppColor.blueColor,
),
),
),
],
),
Stack(
children: [
// StreamCounter(),
LinearProgressIndicator(
backgroundColor: AppColor.accentColor,
color:
// controller.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor
// :
AppColor.greenColor,
minHeight: 25,
borderRadius: BorderRadius.circular(15),
value:
24 //controller.progressTimerRideBegin.toDouble(),
),
Center(
child: Text(
controller.stringElapsedTimeRideBeginVip,
style: AppStyle.title,
),
)
],
)
],
),
),
),
);
} else {
return const SizedBox();
}
});
}
}
class StreamCounter extends StatelessWidget {
const StreamCounter({Key? key}) : super(key: key);
@override
// Build the UI based on the timer value
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(builder: (controller) {
return StreamBuilder<int>(
initialData: 0,
stream: controller.timerController.stream,
builder: (context, snapshot) {
// Calculate the remaining time based on the current tick
final remainingTime = controller.durationToRide - snapshot.data!;
// Format the remaining time as a string
final formattedRemainingTime =
'${(remainingTime / 60).floor()}:${(remainingTime % 60).toString().padLeft(2, '0')}';
// Return the UI widgets based on the remaining time
return Column(
children: [
Text(formattedRemainingTime),
// ElevatedButton(
// onPressed: () {
// // Handle button press here
// },
// ),
],
);
},
);
});
}
}
@@ -1,260 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/views/home/my_wallet/payment_history_passenger_page.dart';
import 'dart:ui'; // لاستخدام تأثيرات متقدمة
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/info.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/payment/credit_card_controller.dart';
import '../../../controller/payment/payment_controller.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import 'passenger_wallet_dialoge.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class PassengerWallet extends StatelessWidget {
const PassengerWallet({super.key});
@override
Widget build(BuildContext context) {
// نفس منطق استدعاء الكنترولرز
Get.put(PaymentController());
Get.put(CreditCardController());
return MyScafolld(
title: 'My Balance'.tr,
isleading: true,
body: [
// استخدام Stack فقط لعرض الـ Dialog فوق المحتوى عند الحاجة
Stack(
children: [
// استخدام Column لتنظيم المحتوى بشكل أفضل
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
// --- 1. بطاقة المحفظة العصرية ---
_buildModernWalletCard(),
const SizedBox(height: 32),
Text("Actions".tr,
style: AppStyle.title.copyWith(
color: AppColor.writeColor.withOpacity(0.6))),
const Divider(height: 24),
// --- 2. قائمة الخيارات المنظمة ---
_buildActionTile(
icon: Icons.add_card_rounded,
title: 'Top up Balance'.tr,
subtitle: 'Add funds using our secure methods'.tr,
onTap: () =>
showPaymentBottomSheet(context), // نفس دالتك القديمة
),
_buildActionTile(
icon: Icons.history_rounded,
title: 'Payment History'.tr,
subtitle: 'View your past transactions'.tr,
onTap: () => Get.to(
() => const PaymentHistoryPassengerPage(),
transition: Transition.rightToLeftWithFade),
),
_buildActionTile(
icon: Icons.phone_iphone_rounded,
title: 'Set Phone Number'.tr,
subtitle: 'Link a phone number for transfers'.tr,
onTap: () => _showWalletPhoneDialog(context,
Get.find<PaymentController>()), // نفس دالتك القديمة
),
],
),
),
// --- عرض الـ Dialog بنفس طريقتك القديمة ---
const PassengerWalletDialog(),
],
),
],
);
}
// --- ويدجت مساعدة لبناء بطاقة المحفظة ---
Widget _buildModernWalletCard() {
return GetBuilder<PaymentController>(
builder: (paymentController) {
return Container(
width: double.infinity,
height: Get.height * 0.25,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: const LinearGradient(
colors: [
AppColor.primaryColor,
Color(0xFF1E3A8A)
], // تدرج لوني أنيق
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: AppColor.primaryColor.withOpacity(0.3),
blurRadius: 25,
offset: const Offset(0, 10),
),
],
),
child: Stack(
children: [
// --- عنصر تزييني (شكل موجة) ---
Positioned(
right: -100,
bottom: -100,
child: Icon(
Icons.waves,
size: 250,
color: Colors.white.withOpacity(0.05),
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${AppInformation.appName} ${'Balance'.tr}',
style: AppStyle.headTitle.copyWith(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Icon(Icons.memory_rounded,
color: Colors.white.withOpacity(0.7),
size: 30), // أيقونة الشريحة
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Current Balance".tr,
style: AppStyle.subtitle
.copyWith(color: Colors.white.withOpacity(0.7)),
),
Text(
'${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'SYP'.tr}',
style: AppStyle.headTitle2.copyWith(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.w600,
letterSpacing: 1.5,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
(box.read(BoxName.name) ?? "User Name").toString(),
style: AppStyle.title.copyWith(
color: Colors.white.withOpacity(0.8),
fontSize: 16,
),
),
],
),
],
),
),
],
),
);
},
);
}
// --- ويدجت مساعدة لبناء عناصر القائمة ---
Widget _buildActionTile({
required IconData icon,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return ListTile(
onTap: onTap,
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
leading: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: AppColor.primaryColor, size: 24),
),
title: Text(title.tr, style: AppStyle.title),
subtitle: Text(subtitle.tr,
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor.withOpacity(0.6))),
trailing: const Icon(Icons.arrow_forward_ios_rounded,
size: 16, color: AppColor.writeColor),
);
}
// --- نفس دالة الـ Dialog الخاصة بك ---
void _showWalletPhoneDialog(
BuildContext context, PaymentController controller) {
Get.dialog(
CupertinoAlertDialog(
title: Text('Insert Wallet phone number'.tr),
content: Column(
children: [
const SizedBox(height: 10),
Form(
key: controller.formKey,
child: CupertinoTextField(
controller: controller.walletphoneController,
placeholder: 'Insert Wallet phone number'.tr,
keyboardType: TextInputType.phone,
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 10),
),
),
],
),
actions: <Widget>[
CupertinoDialogAction(
child: Text('Cancel'.tr,
style: const TextStyle(color: CupertinoColors.destructiveRed)),
onPressed: () => Get.back(),
),
CupertinoDialogAction(
child: Text('OK'.tr,
style: const TextStyle(color: CupertinoColors.activeGreen)),
onPressed: () {
Get.back();
box.write(
BoxName.phoneWallet, (controller.walletphoneController.text));
Toast.show(context, 'Phone Wallet Saved Successfully'.tr,
AppColor.greenColor);
},
),
],
),
barrierDismissible: false,
);
}
}
// الكلاس القديم CardTripzWallet لم نعد بحاجة إليه لأنه تم دمجه وتطويره
@@ -1,498 +0,0 @@
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/functions/encrypt_decrypt.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/controller/functions/toast.dart';
import 'package:Tripz/controller/payment/payment_controller.dart';
import 'package:local_auth/local_auth.dart';
import '../../../main.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_textField.dart';
import 'payment_screen_sham.dart';
class PassengerWalletDialog extends StatelessWidget {
const PassengerWalletDialog({
super.key,
});
@override
Widget build(BuildContext context) {
return GetBuilder<PaymentController>(
builder: (controller) => Positioned(
top: Get.height * .1,
right: Get.width * .15,
left: Get.width * .15,
bottom: Get.height * .1,
child: controller.isPromoSheetDialogue
? CupertinoActionSheet(
title: Text('Select Payment Amount'.tr),
actions: [
CupertinoActionSheetAction(
onPressed: () {
controller.updateSelectedAmount(
box.read(BoxName.countryCode) == 'Syria' ? 10000 : 10,
);
showPaymentOptions(context, controller);
},
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '10000 ${'LE'.tr}'
: '10 ${'SYP'.tr}',
),
),
CupertinoActionSheetAction(
onPressed: () {
controller.updateSelectedAmount(
box.read(BoxName.countryCode) == 'Syria' ? 20000 : 20,
);
showPaymentOptions(context, controller);
},
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '20000 ${'LE'.tr} = 2050 ${'LE'.tr}'
: '20 ${'SYP'.tr}',
),
),
CupertinoActionSheetAction(
onPressed: () {
controller.updateSelectedAmount(
box.read(BoxName.countryCode) == 'Syria' ? 40000 : 40,
);
showPaymentOptions(context, controller);
},
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '40000 ${'LE'.tr} = 4150 ${'LE'.tr}'
: '40 ${'SYP'.tr}',
),
),
CupertinoActionSheetAction(
onPressed: () {
controller.updateSelectedAmount(
box.read(BoxName.countryCode) == 'Syria' ? 100000 : 50,
);
showPaymentOptions(context, controller);
},
child: Text(
box.read(BoxName.countryCode) == 'Syria'
? '100000 ${'LE'.tr} = 11000 ${'LE'.tr}'
: '50 ${'SYP'.tr}',
),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
controller.changePromoSheetDialogue();
},
child: Text('Cancel'.tr),
),
)
: const SizedBox(),
),
);
}
}
// class PassengerWalletDialog extends StatelessWidget {
// const PassengerWalletDialog({
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// return GetBuilder<PaymentController>(
// builder: (controller) {
// return Positioned(
// top: Get.height * .1,
// right: Get.width * .15,
// left: Get.width * .15,
// bottom: Get.height * .1,
// child: controller.isPromoSheetDialogue
// ? Container()
// : SizedBox
// .shrink(), // If condition is false, return an empty widget
// );
// },
// );
// }
// }
void showPaymentBottomSheet(BuildContext context) {
final controller = Get.find<PaymentController>();
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
Get.back();
return false;
},
child: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Select Payment Amount'.tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
const SizedBox(height: 16.0),
// Payment Options List
_buildPaymentOption(
context: context,
controller: controller,
amount: 500,
bonusAmount: 30,
currency: 'SYP'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: 1000,
bonusAmount: 70,
currency: 'SYP'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: 2000,
bonusAmount: 180,
currency: 'SYP'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: 5000,
bonusAmount: 700,
currency: 'SYP'.tr,
),
const SizedBox(height: 16.0),
TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr),
),
],
),
),
);
},
);
}
Widget _buildPaymentOption({
required BuildContext context,
required PaymentController controller,
required int amount,
required double bonusAmount,
required String currency,
}) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
controller.updateSelectedAmount(amount);
Get.back();
showPaymentOptions(context, controller);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
bonusAmount > 0
? '${'Pay'.tr} $amount $currency, ${'Get'.tr} ${amount + bonusAmount} $currency'
: '$amount $currency',
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
),
);
}
void showPaymentOptions(BuildContext context, PaymentController controller) {
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: Text('Payment Options'.tr),
actions: [
box.read(BoxName.countryCode) == 'Syria'
? CupertinoActionSheetAction(
child: Text('💳 Pay with Credit Card'.tr),
onPressed: () async {
if (controller.selectedAmount != 0) {
controller.payWithEcash(
context,
controller.selectedAmount.toString(),
// () async {
// await controller.addPassengerWallet();
// controller.changePromoSheetDialogue();
);
await controller.getPassengerWallet();
} else {
Toast.show(context, '⚠️ You need to choose an amount!'.tr,
AppColor.redColor);
}
},
)
: const SizedBox(),
box.read(BoxName.countryCode) != 'Syria'
? CupertinoActionSheetAction(
child: Text('Pay with PayPal'.tr),
onPressed: () {
if (controller.selectedAmount != 0) {
controller.makePaymentPayPal(context);
} else {
Toast.show(context, 'You will choose one of above!'.tr,
AppColor.redColor);
}
},
)
: const SizedBox(),
// box.read(BoxName.phoneWallet) != null
// ? CupertinoActionSheetAction(
// child: Text('💰 Pay with Wallet'.tr),
// onPressed: () async {
// if (controller.selectedAmount != 0) {
// controller.isLoading = true;
// controller.update();
// controller.payWithMTNWallet(
// context,
// controller.selectedAmount.toString(),
// 'SYP',
// );
// await controller.getPassengerWallet();
// controller.isLoading = false;
// controller.update();
// } else {
// Toast.show(context, '⚠️ You need to choose an amount!'.tr,
// AppColor.redColor);
// }
// },
// )
// : CupertinoActionSheetAction(
// child: Text('Add wallet phone you use'.tr),
// onPressed: () {
// Get.dialog(
// CupertinoAlertDialog(
// title: Text('Insert Wallet phone number'.tr),
// content: Column(
// children: [
// const SizedBox(height: 10),
// CupertinoTextField(
// controller: controller.walletphoneController,
// placeholder: 'Insert Wallet phone number'.tr,
// keyboardType: TextInputType.phone,
// padding: const EdgeInsets.symmetric(
// vertical: 12,
// horizontal: 10,
// ),
// ),
// ],
// ),
// actions: [
// CupertinoDialogAction(
// child: Text('Cancel'.tr,
// style: const TextStyle(
// color: CupertinoColors.destructiveRed)),
// onPressed: () {
// Get.back();
// },
// ),
// CupertinoDialogAction(
// child: Text('OK'.tr,
// style: const TextStyle(
// color: CupertinoColors.activeGreen)),
// onPressed: () async {
// Get.back();
// box.write(BoxName.phoneWallet,
// (controller.walletphoneController.text));
// Toast.show(
// context,
// 'Phone Wallet Saved Successfully'.tr,
// AppColor.greenColor);
// },
// ),
// ],
// ),
// barrierDismissible: false,
// );
// },
// ),
// GestureDetector(
// onTap: () async {
// Get.back();
// Get.defaultDialog(
// barrierDismissible: false,
// title: 'Insert Wallet phone number'.tr,
// content: Form(
// key: controller.formKey,
// child: MyTextForm(
// controller: controller.walletphoneController,
// label: 'Insert Wallet phone number'.tr,
// hint: '963941234567',
// type: TextInputType.phone)),
// confirm: MyElevatedButton(
// title: 'OK'.tr,
// onPressed: () async {
// Get.back();
// if (controller.formKey.currentState!.validate()) {
// if (controller.selectedAmount != 0) {
// controller.isLoading = true;
// controller.update();
// box.write(BoxName.phoneWallet,
// (controller.walletphoneController.text));
// Get.back();
// await controller.payWithMTNWallet(
// context,
// controller.selectedAmount.toString(),
// 'SYP',
// );
// await controller.getPassengerWallet();
// controller.isLoading = false;
// controller.update();
// } else {
// Toast.show(
// context,
// '⚠️ You need to choose an amount!'.tr,
// AppColor.redColor,
// );
// }
// }
// }));
// },
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Pay by MTN Wallet'.tr,
// style: AppStyle.title,
// ),
// const SizedBox(width: 10),
// Image.asset(
// 'assets/images/cashMTN.png',
// width: 70,
// height: 70,
// fit: BoxFit.contain,
// ),
// ],
// ),
// )),
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963941234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (controller.formKey.currentState!.validate()) {
box.write(BoxName.phoneWallet,
controller.walletphoneController.text);
await controller.payWithSyriaTelWallet(
controller.selectedAmount.toString(), 'SYP');
}
}));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Syriatel Wallet'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/syriatel.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
GestureDetector(
onTap: () async {
// التحقق بالبصمة قبل أي شيء
bool isAuthSupported =
await LocalAuthentication().isDeviceSupported();
if (isAuthSupported) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (!didAuthenticate) {
print("❌ User did not authenticate with biometrics");
return;
}
}
// الانتقال مباشرة لإنشاء الفاتورة بعد النجاح بالبصمة
Get.to(() => PaymentScreenSmsProvider(
amount: double.parse(controller.selectedAmount.toString())));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by Sham Cash'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/shamCash.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
),
)),
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () {
// controller.changePromoSheetDialogue();
Get.back();
},
),
),
);
}
@@ -1,54 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import 'package:Tripz/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);
}
}
@@ -1,61 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/payment/passenger_wallet_history_controller.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import 'package:Tripz/views/widgets/mycircular.dart';
class PaymentHistoryPassengerPage extends StatelessWidget {
const PaymentHistoryPassengerPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(PassengerWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<PassengerWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator() // iOS-style loading indicator
: controller.archive.isEmpty
? Center(
child: Text(
'No wallet record found'.tr,
style: AppStyle.title,
),
)
: CupertinoListSection.insetGrouped(
children: List.generate(
controller.archive.length,
(index) {
var list = controller.archive[index];
return CupertinoListTile(
backgroundColor: double.parse(list['balance']) < 0
? AppColor.redColor.withOpacity(.2)
: AppColor.greenColor.withOpacity(.2),
title: Text(
list['balance'],
style: AppStyle.title.copyWith(
color: CupertinoColors.black,
),
),
additionalInfo: Text(
list['created_at'],
style: AppStyle.title.copyWith(
fontSize: 12,
color: CupertinoColors.systemGrey,
),
),
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
);
},
),
),
)
],
isleading: true);
}
}
@@ -1,515 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../main.dart';
// --- خدمة الدفع (نفس المنطق السابق) ---
class PaymentService {
final String _baseUrl = "${AppLink.paymentServer}/ride/shamcash/passenger";
Future<String?> createInvoice({required double amount}) async {
final url = "$_baseUrl/create_invoice.php";
try {
final response = await CRUD().postWallet(
link: url,
payload: {
'passengerID': box.read(BoxName.passengerID),
'amount': amount.toString(),
},
).timeout(const Duration(seconds: 15));
if (response != 'failure') {
final data = response;
if (data['status'] == 'success' && data['invoice_number'] != null) {
return data['invoice_number'].toString();
}
}
return null;
} catch (e) {
debugPrint("Create Invoice Error: $e");
return null;
}
}
Future<bool> checkInvoiceStatus(String invoiceNumber) async {
final url = "$_baseUrl/check_status.php";
try {
final response = await CRUD().postWallet(link: url, payload: {
'invoice_number': invoiceNumber,
}).timeout(const Duration(seconds: 10));
if (response != 'failure') {
final data = response;
return data['status'] == 'success' &&
data['invoice_status'] == 'completed';
}
return false;
} catch (e) {
return false;
}
}
}
enum PaymentStatus {
creatingInvoice,
waitingForPayment,
paymentSuccess,
paymentTimeout,
paymentError
}
class PaymentScreenSmsProvider extends StatefulWidget {
final double amount;
final String providerName;
final String providerLogo;
final String qrImagePath;
const PaymentScreenSmsProvider({
super.key,
required this.amount,
this.providerName = 'شام كاش',
this.providerLogo = 'assets/images/shamCash.png',
this.qrImagePath = 'assets/images/shamcashsend.png',
});
@override
_PaymentScreenSmsProviderState createState() =>
_PaymentScreenSmsProviderState();
}
class _PaymentScreenSmsProviderState extends State<PaymentScreenSmsProvider>
with SingleTickerProviderStateMixin {
final PaymentService _paymentService = PaymentService();
Timer? _pollingTimer;
PaymentStatus _status = PaymentStatus.creatingInvoice;
String? _invoiceNumber;
// العنوان الثابت للدفع (المستخرج من الصورة)
final String _paymentAddress = "80f23afe40499b02f49966c3340ae0fc";
// متحكم الأنيميشن للوميض
late AnimationController _blinkController;
late Animation<Color?> _colorAnimation;
late Animation<double> _shadowAnimation;
@override
void initState() {
super.initState();
// إعداد الأنيميشن (وميض أحمر)
_blinkController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
)..repeat(reverse: true); // يكرر الحركة ذهاباً وإياباً
_colorAnimation = ColorTween(
begin: Colors.red.shade700,
end: Colors.red.shade100,
).animate(_blinkController);
_shadowAnimation = Tween<double>(begin: 2.0, end: 15.0).animate(
CurvedAnimation(parent: _blinkController, curve: Curves.easeInOut),
);
_createAndPollInvoice();
}
@override
void dispose() {
_pollingTimer?.cancel();
_blinkController.dispose();
super.dispose();
}
void _createAndPollInvoice() async {
setState(() => _status = PaymentStatus.creatingInvoice);
final invoiceNumber =
await _paymentService.createInvoice(amount: widget.amount);
if (invoiceNumber != null && mounted) {
setState(() {
_invoiceNumber = invoiceNumber;
_status = PaymentStatus.waitingForPayment;
});
_startPolling(invoiceNumber);
} else if (mounted) {
setState(() => _status = PaymentStatus.paymentError);
}
}
void _startPolling(String invoiceNumber) {
const timeoutDuration = Duration(minutes: 5);
var elapsed = Duration.zero;
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
elapsed += const Duration(seconds: 5);
if (elapsed >= timeoutDuration) {
timer.cancel();
if (mounted) setState(() => _status = PaymentStatus.paymentTimeout);
return;
}
final isCompleted =
await _paymentService.checkInvoiceStatus(invoiceNumber);
if (isCompleted && mounted) {
timer.cancel();
setState(() => _status = PaymentStatus.paymentSuccess);
}
});
}
Future<bool> _onPopInvoked() async {
if (_status == PaymentStatus.waitingForPayment) {
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('إلغاء العملية؟', textAlign: TextAlign.right),
content: const Text('الخروج الآن سيؤدي لإلغاء متابعة عملية الدفع.',
textAlign: TextAlign.right),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('البقاء')),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('خروج', style: TextStyle(color: Colors.red))),
],
),
);
return shouldPop ?? false;
}
return true;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onPopInvoked,
child: Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
title: Text("دفع عبر ${widget.providerName}"),
centerTitle: true,
elevation: 0,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(child: _buildContentByStatus()),
),
),
),
);
}
Widget _buildContentByStatus() {
switch (_status) {
case PaymentStatus.creatingInvoice:
return const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 20),
Text("جاري إنشاء رقم البيان...", style: TextStyle(fontSize: 16)),
],
);
case PaymentStatus.waitingForPayment:
return _buildWaitingForPaymentUI();
case PaymentStatus.paymentSuccess:
return _buildSuccessUI();
case PaymentStatus.paymentTimeout:
case PaymentStatus.paymentError:
return _buildErrorUI();
}
}
Widget _buildWaitingForPaymentUI() {
final currencyFormat = NumberFormat.decimalPattern('ar_SY');
final invoiceText = _invoiceNumber ?? '---';
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 1. المبلغ
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade800, Colors.blue.shade600]),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.25),
blurRadius: 10,
offset: const Offset(0, 5))
],
),
child: Column(
children: [
const Text("المبلغ المطلوب",
style: TextStyle(color: Colors.white70, fontSize: 14)),
const SizedBox(height: 5),
Text("${currencyFormat.format(widget.amount)} ل.س",
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold)),
],
),
),
const SizedBox(height: 25),
// 2. رقم البيان (هام جداً - وميض أحمر)
AnimatedBuilder(
animation: _blinkController,
builder: (context, child) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: _colorAnimation.value ?? Colors.red,
width: 3.0, // إطار سميك
),
boxShadow: [
BoxShadow(
color: (_colorAnimation.value ?? Colors.red)
.withOpacity(0.4),
blurRadius: _shadowAnimation.value,
spreadRadius: 2,
)
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.warning_rounded,
color: Colors.red.shade800, size: 28),
const SizedBox(width: 8),
Text(
"هام جداً: لا تنسَ!",
style: TextStyle(
color: Colors.red.shade900,
fontWeight: FontWeight.bold,
fontSize: 18),
),
],
),
const SizedBox(height: 10),
const Text(
"يجب نسخ (رقم البيان) هذا ووضعه في تطبيق شام كاش لضمان نجاح العملية.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
color: Colors.black87,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 15),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: invoiceText));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text("تم نسخ رقم البيان ✅",
textAlign: TextAlign.center),
backgroundColor: Colors.red.shade700));
},
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 12),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.red.shade200, width: 1)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("رقم البيان (Invoice No)",
style: TextStyle(
fontSize: 12, color: Colors.grey)),
Text(invoiceText,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
color: Colors.red.shade900)),
],
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.copy_rounded,
color: Colors.red.shade900, size: 24),
),
],
),
),
),
],
),
);
},
),
const SizedBox(height: 25),
// 3. عنوان الدفع (اختياري / عادي)
Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("عنوان الدفع (Payment Address)",
style: TextStyle(fontSize: 12, color: Colors.grey)),
const SizedBox(height: 8),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: _paymentAddress));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text("تم نسخ عنوان الدفع ✅",
textAlign: TextAlign.center),
backgroundColor: Colors.green.shade600));
},
child: Row(
children: [
Expanded(
child: Text(_paymentAddress,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: 'Courier',
color: Colors.black87,
),
overflow: TextOverflow.ellipsis),
),
const SizedBox(width: 8),
const Icon(Icons.copy, size: 18, color: Colors.grey),
],
),
),
],
),
),
const SizedBox(height: 30),
// 4. QR Code
const Text("أو امسح الرمز للدفع",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (ctx) => Dialog(
backgroundColor: Colors.transparent,
child: InteractiveViewer(
child: Image.asset(widget.qrImagePath))));
},
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.grey.shade300)),
child: Image.asset(widget.qrImagePath,
width: 150,
height: 150,
fit: BoxFit.contain,
errorBuilder: (c, o, s) => const Icon(Icons.qr_code_2,
size: 100, color: Colors.grey)),
),
),
const SizedBox(height: 30),
const LinearProgressIndicator(backgroundColor: Colors.white),
const SizedBox(height: 10),
const Text("جاري التحقق من الدفع تلقائياً...",
style: TextStyle(color: Colors.grey, fontSize: 12)),
const SizedBox(height: 20),
],
),
);
}
Widget _buildSuccessUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.verified_rounded, color: Colors.green, size: 100),
const SizedBox(height: 20),
const Text("تم الدفع بنجاح!",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 40),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16)),
onPressed: () => Navigator.of(context).pop(),
child: const Text("متابعة", style: TextStyle(fontSize: 18)),
),
),
],
);
}
Widget _buildErrorUI() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline_rounded, color: Colors.red.shade400, size: 80),
const SizedBox(height: 20),
Text(
_status == PaymentStatus.paymentTimeout
? "انتهى الوقت"
: "لم يتم التحقق",
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Text("لم يصلنا إشعار الدفع خلال الوقت المحدد.",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey))),
const SizedBox(height: 40),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15)),
onPressed: _createAndPollInvoice,
icon: const Icon(Icons.refresh),
label: const Text("حاول مرة أخرى"),
),
),
const SizedBox(height: 15),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("إلغاء", style: TextStyle(color: Colors.grey)))
],
);
}
}
@@ -1,71 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/payment/payment_controller.dart';
import '../../../constant/box_name.dart';
import '../../../main.dart';
import '../my_wallet/passenger_wallet.dart';
class PointsCaptain extends StatelessWidget {
PaymentController paymentController = Get.put(PaymentController());
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.to(() => const PassengerWallet());
paymentController.changePromoSheetDialogue();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
child: Container(
width: Get.width * .21,
height: Get.width * .29,
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 ${'LE'.tr}',
style: AppStyle.subtitle,
),
Text(
'$pricePoint ${box.read(BoxName.countryCode) == 'Jordan' ? 'JOD'.tr : 'LE'.tr}',
style: AppStyle.title,
textAlign: TextAlign.center,
),
],
),
)),
),
);
}
}
@@ -1,229 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/home/profile/complaint_controller.dart';
import 'package:Tripz/views/widgets/my_scafold.dart'; // سنستخدم السكافولد الخاص بك
import 'package:Tripz/views/widgets/mycircular.dart';
import 'package:Tripz/views/widgets/mydialoug.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart'; // سنستخدم الزر الخاص بك
import '../../../constant/colors.dart';
import '../../../controller/functions/audio_record1.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class ComplaintPage extends StatelessWidget {
ComplaintPage({super.key});
final ComplaintController complaintController =
Get.put(ComplaintController());
final AudioRecorderController audioRecorderController =
Get.put(AudioRecorderController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Submit a Complaint'.tr,
isleading: true,
body: [
GetBuilder<ComplaintController>(
builder: (controller) {
if (controller.isLoading && controller.feedBack.isEmpty) {
// عرض التحميل المبدئي فقط
return const MyCircularProgressIndicator();
}
return Stack(
children: [
Form(
key: controller.formKey,
child: ListView(
padding: const EdgeInsets.all(16.0),
children: [
// --- 1. بطاقة إدخال نص الشكوى ---
_buildSectionCard(
title: '1. Describe Your Issue'.tr,
child: TextFormField(
controller: controller.complaintController,
decoration: InputDecoration(
hintText: 'Enter your complaint here...'.tr,
filled: true,
fillColor: AppColor.secondaryColor.withOpacity(0.5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.all(16),
),
maxLines: 6,
style: AppStyle.subtitle,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a description of the issue.'
.tr;
}
return null;
},
),
),
// --- 2. بطاقة إرفاق التسجيل الصوتي ---
_buildSectionCard(
title: '2. Attach Recorded Audio (Optional)'.tr,
child: FutureBuilder<List<String>>(
future: audioRecorderController.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
}
if (snapshot.hasError ||
!snapshot.hasData ||
snapshot.data!.isEmpty) {
return Center(
child: Text('No audio files found.'.tr,
style: AppStyle.subtitle));
}
return Column(
children: snapshot.data!.map((audioFilePath) {
final audioFile = File(audioFilePath);
final isUploaded =
controller.audioLink.isNotEmpty &&
controller.audioLink.contains(
audioFilePath.split('/').last);
return ListTile(
leading: Icon(
isUploaded
? Icons.check_circle
: Icons.mic,
color: isUploaded
? AppColor.greenColor
: AppColor.redColor),
title: Text(audioFilePath.split('/').last,
style: AppStyle.subtitle,
overflow: TextOverflow.ellipsis),
subtitle: isUploaded
? Text('Uploaded'.tr,
style: const TextStyle(
color: AppColor.greenColor))
: null,
onTap: isUploaded
? null
: () {
MyDialogContent().getDialog(
'Confirm Attachment'.tr,
Text(
'Attach this audio file?'.tr),
() async {
await controller
.uploadAudioFile(audioFile);
});
},
);
}).toList(),
);
},
),
),
// --- 3. بطاقة تفاصيل الرحلة والرد ---
_buildSectionCard(
title: '3. Review Details & Response'.tr,
child: Column(
children: [
if (controller.feedBack.isNotEmpty) ...[
_buildDetailRow(Icons.calendar_today_outlined,
'Date'.tr, controller.feedBack[0]['date']),
_buildDetailRow(
Icons.monetization_on_outlined,
'Price'.tr,
'${controller.feedBack[0]['price']}'),
],
const Divider(height: 24),
ListTile(
leading: const Icon(Icons.support_agent_outlined,
color: AppColor.primaryColor),
title: Text("Tripz's Response".tr,
style: AppStyle.title),
subtitle: Text(
// --- تعديل هنا لعرض الرد من الخادم ---
controller.passengerReport?['body']
?.toString() ??
'Awaiting response...'.tr,
style: AppStyle.subtitle.copyWith(height: 1.5),
),
),
],
),
),
// --- 4. زر الإرسال ---
const SizedBox(height: 24),
MyElevatedButton(
kolor: AppColor.blueColor,
title: 'Submit Complaint'.tr,
onPressed: () async {
// --- تعديل: استدعاء الدالة الجديدة فقط ---
await controller.submitComplaintToServer();
},
),
const SizedBox(height: 24), // مسافة إضافية بالأسفل
],
),
),
// --- عرض مؤشر التحميل فوق كل شيء ---
if (controller.isLoading)
Container(
color: Colors.black.withOpacity(0.5),
child: const MyCircularProgressIndicator(),
),
],
);
},
),
],
);
}
// --- ويدجت مساعدة لبناء البطاقات ---
Widget _buildSectionCard({required String title, required Widget child}) {
return Card(
margin: const EdgeInsets.only(bottom: 20),
elevation: 4,
shadowColor: Colors.black.withOpacity(0.1),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: AppStyle.headTitle.copyWith(fontSize: 18)),
const SizedBox(height: 12),
child,
],
),
),
);
}
// --- ويدجت مساعدة لعرض صفوف التفاصيل ---
Widget _buildDetailRow(IconData icon, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Icon(icon, color: AppColor.writeColor.withOpacity(0.6), size: 20),
const SizedBox(width: 12),
Text('${label.tr}:',
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor.withOpacity(0.7))),
const Spacer(),
Text(value,
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
],
),
);
}
}
@@ -1,233 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import 'package:Tripz/views/widgets/mycircular.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../constant/colors.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/home/profile/order_history_controller.dart';
// --- الويدجت الرئيسية بالتصميم الجديد ---
class OrderHistory extends StatelessWidget {
const OrderHistory({super.key});
@override
Widget build(BuildContext context) {
// نفس منطق استدعاء الكنترولر
Get.put(OrderHistoryController());
return MyScafolld(
title: 'Order History'.tr,
isleading: true,
body: [
GetBuilder<OrderHistoryController>(
builder: (controller) {
// --- نفس منطق التحميل والحالة الفارغة ---
if (controller.isloading) {
return const MyCircularProgressIndicator();
}
if (controller.orderHistoryListPassenger.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.map_outlined,
size: 80, color: AppColor.writeColor.withOpacity(0.4)),
const SizedBox(height: 16),
Text('No trip history found'.tr,
style: AppStyle.headTitle2),
Text("Your past trips will appear here.".tr,
style: AppStyle.subtitle),
],
),
);
}
// --- استخدام ListView.separated لفصل البطاقات ---
return ListView.separated(
padding: const EdgeInsets.all(16.0),
itemCount: controller.orderHistoryListPassenger.length,
separatorBuilder: (context, index) => const SizedBox(height: 16),
itemBuilder: (BuildContext context, int index) {
final ride = controller.orderHistoryListPassenger[index];
// --- استدعاء ويدجت البطاقة الجديدة ---
return _buildHistoryCard(context, ride);
},
);
},
)
],
);
}
// --- ويدجت بناء بطاقة الرحلة ---
Widget _buildHistoryCard(BuildContext context, Map<String, dynamic> ride) {
// --- نفس منطق حساب إحداثيات الخريطة ---
final LatLng startLocation = LatLng(
double.parse(ride['start_location'].toString().split(',')[0]),
double.parse(ride['start_location'].toString().split(',')[1]),
);
final LatLng endLocation = LatLng(
double.parse(ride['end_location'].toString().split(',')[0]),
double.parse(ride['end_location'].toString().split(',')[1]),
);
final LatLngBounds bounds = LatLngBounds(
northeast: LatLng(
startLocation.latitude > endLocation.latitude
? startLocation.latitude
: endLocation.latitude,
startLocation.longitude > endLocation.longitude
? startLocation.longitude
: endLocation.longitude,
),
southwest: LatLng(
startLocation.latitude < endLocation.latitude
? startLocation.latitude
: endLocation.latitude,
startLocation.longitude < endLocation.longitude
? startLocation.longitude
: endLocation.longitude,
),
);
return InkWell(
// --- نفس دالة onTap القديمة ---
onTap: () {
String mapUrl =
'https://www.google.com/maps/dir/${ride['start_location']}/${ride['end_location']}/';
showInBrowser(mapUrl);
},
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 1. قسم الخريطة ---
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
child: SizedBox(
height: 150, // ارتفاع ثابت للخريطة
child: AbsorbPointer(
// لمنع التفاعل المباشر مع الخريطة داخل القائمة
child: GoogleMap(
initialCameraPosition:
CameraPosition(target: startLocation, zoom: 12),
// --- نفس منطق الخريطة والخطوط ---
onMapCreated: (GoogleMapController controller) {
controller.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 60));
},
polylines: {
Polyline(
polylineId: const PolylineId('route'),
points: [startLocation, endLocation],
color: AppColor.primaryColor,
width: 4,
),
},
markers: {
Marker(
markerId: const MarkerId('start'),
position: startLocation),
Marker(
markerId: const MarkerId('end'),
position: endLocation),
},
mapToolbarEnabled: false,
zoomControlsEnabled: false,
),
),
),
),
// --- 2. قسم تفاصيل الرحلة ---
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${ride['date']} - ${ride['time']}',
style: AppStyle.subtitle.copyWith(
color: AppColor.writeColor.withOpacity(0.7)),
),
// --- ويدجت جديدة لعرض حالة الرحلة ---
_buildStatusChip(ride['status']),
],
),
const Divider(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Total Price'.tr,
style: AppStyle.title.copyWith(fontSize: 16)),
Text(
'${ride['price']} ${'SYP'.tr}',
style: AppStyle.headTitle.copyWith(
fontSize: 20, color: AppColor.primaryColor),
),
],
),
],
),
),
],
),
),
);
}
// --- ويدجت مساعدة لعرض حالة الرحلة بشكل أنيق ---
Widget _buildStatusChip(String status) {
Color chipColor;
IconData chipIcon;
// --- نفس منطق تحديد اللون ---
if (status == 'Canceled'.tr) {
chipColor = AppColor.redColor;
chipIcon = Icons.cancel_outlined;
} else if (status == 'Finished'.tr) {
chipColor = AppColor.greenColor;
chipIcon = Icons.check_circle_outline;
} else {
chipColor = AppColor.yellowColor;
chipIcon = Icons.hourglass_empty_rounded;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: chipColor.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(chipIcon, color: chipColor, size: 16),
const SizedBox(width: 6),
Text(
status,
style: AppStyle.subtitle.copyWith(
color: chipColor, fontWeight: FontWeight.bold, fontSize: 12),
),
],
),
);
}
}
@@ -1,384 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/colors.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/profile/profile_controller.dart';
import 'package:Tripz/main.dart';
import 'package:Tripz/views/auth/login_page.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import 'package:Tripz/views/widgets/my_textField.dart';
import 'package:Tripz/views/widgets/mycircular.dart';
import '../../../controller/auth/login_controller.dart';
import '../../../controller/functions/log_out.dart';
class PassengerProfilePage extends StatelessWidget {
PassengerProfilePage({super.key});
final LogOutController logOutController = Get.put(LogOutController());
@override
Widget build(BuildContext context) {
Get.put(ProfileController());
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text('My Profile'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.grey[100],
elevation: 0,
centerTitle: true,
),
body: GetBuilder<ProfileController>(
builder: (controller) {
if (controller.isloading) {
return const MyCircularProgressIndicator();
}
return ListView(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
children: [
_buildProfileHeader(controller),
const SizedBox(height: 24),
_buildSectionCard(
'Personal Information'.tr,
[
_buildProfileTile(
icon: Icons.person_outline,
color: Colors.blue,
title: 'Name'.tr,
subtitle:
'${controller.prfoileData['first_name'] ?? ''} ${controller.prfoileData['last_name'] ?? ''}',
onTap: () =>
controller.updatField('first_name', TextInputType.name),
),
_buildProfileTile(
icon: Icons.wc_outlined,
color: Colors.pink,
title: 'Gender'.tr,
subtitle: controller.prfoileData['gender']?.toString() ??
'Not set'.tr,
onTap: () => _showGenderDialog(controller),
),
_buildProfileTile(
icon: Icons.school_outlined,
color: Colors.orange,
title: 'Education'.tr,
subtitle: controller.prfoileData['education']?.toString() ??
'Not set'.tr,
onTap: () => _showEducationDialog(controller),
),
],
),
const SizedBox(height: 24),
_buildSectionCard(
'Work & Contact'.tr,
[
_buildProfileTile(
icon: Icons.work_outline,
color: Colors.green,
title: 'Employment Type'.tr,
subtitle:
controller.prfoileData['employmentType']?.toString() ??
'Not set'.tr,
onTap: () => controller.updatField(
'employmentType', TextInputType.name),
),
_buildProfileTile(
icon: Icons.favorite_border,
color: Colors.purple,
title: 'Marital Status'.tr,
subtitle:
controller.prfoileData['maritalStatus']?.toString() ??
'Not set'.tr,
onTap: () => controller.updatField(
'maritalStatus', TextInputType.name),
),
_buildProfileTile(
icon: Icons.sos_outlined,
color: Colors.red,
title: 'SOS Phone'.tr,
subtitle: controller.prfoileData['sosPhone']?.toString() ??
'Not set'.tr,
onTap: () async {
await controller.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
controller.prfoileData['sosPhone']);
},
),
],
),
const SizedBox(height: 32),
_buildAccountActions(context, logOutController),
],
);
},
),
);
}
Widget _buildProfileHeader(ProfileController controller) {
String fullName =
'${controller.prfoileData['first_name'] ?? ''} ${controller.prfoileData['last_name'] ?? ''}';
String initials = (fullName.isNotEmpty && fullName.contains(" "))
? fullName.split(" ").map((e) => e.isNotEmpty ? e[0] : "").join()
: (fullName.isNotEmpty ? fullName[0] : "");
// Logic to hide email if it contains 'Tripzapp.com'
String email = box.read(BoxName.email) ?? '';
if (email.contains('Tripzapp.com')) {
email = ''; // Clear the email if it contains the domain
}
return Center(
child: Column(
children: [
CircleAvatar(
radius: 50,
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
child: Text(
initials,
style:
const TextStyle(fontSize: 40, color: AppColor.primaryColor),
),
),
const SizedBox(height: 12),
Text(
fullName,
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
if (email
.isNotEmpty) // Only show the Text widget if the email is not empty
Text(
email,
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
),
],
),
);
}
Widget _buildSectionCard(String title, List<Widget> children) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, bottom: 8.0),
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey[700]),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: children,
),
),
],
);
}
Widget _buildProfileTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return ListTile(
onTap: onTap,
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 24),
),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text(subtitle, style: TextStyle(color: Colors.grey[600])),
trailing:
Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey[400]),
);
}
Widget _buildAccountActions(
BuildContext context, LogOutController logOutController) {
return Column(
children: [
SizedBox(
width: double.infinity,
child: TextButton.icon(
icon: const Icon(Icons.logout),
label: Text('Sign Out'.tr),
style: TextButton.styleFrom(
foregroundColor: Colors.blueGrey,
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () {
logOutController.logOutPassenger();
},
),
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: TextButton.icon(
icon: const Icon(Icons.delete_forever_outlined),
label: Text('Delete My Account'.tr),
style: TextButton.styleFrom(
foregroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () =>
_showDeleteAccountDialog(context, logOutController),
),
),
],
);
}
void _showGenderDialog(ProfileController controller) {
Get.defaultDialog(
title: 'Update Gender'.tr,
content: Column(
children: [
GenderPicker(),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id'].toString(),
'gender': controller.gender,
});
Get.back();
},
)
],
),
);
}
void _showEducationDialog(ProfileController controller) {
Get.defaultDialog(
title: 'Update Education'.tr,
content: Column(
children: [
EducationDegreePicker(),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id'].toString(),
'education': controller.selectedDegree,
});
Get.back();
},
),
],
),
);
}
void _showDeleteAccountDialog(
BuildContext context, LogOutController logOutController) {
Get.defaultDialog(
title: 'Delete My Account'.tr,
middleText: 'Are you sure? This action cannot be undone.'.tr,
content: Form(
key: logOutController.formKey1,
child: MyTextForm(
controller: logOutController.emailTextController,
label: 'Confirm your Email'.tr,
hint: 'Type your Email'.tr,
type: TextInputType.emailAddress,
),
),
confirm: MyElevatedButton(
title: 'Delete Permanently'.tr,
kolor: AppColor.redColor,
onPressed: () async {
await logOutController.deletePassengerAccount();
},
),
cancel: TextButton(
child: Text('Cancel'.tr),
onPressed: () {
logOutController.emailTextController.clear();
Get.back();
},
),
);
}
}
// --- Helper Widgets for Pickers ---
class GenderPicker extends StatelessWidget {
final ProfileController controller = Get.find<ProfileController>();
final List<String> genderOptions = ['Male'.tr, 'Female'.tr, 'Other'.tr];
GenderPicker({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 150,
child: CupertinoPicker(
itemExtent: 40.0,
onSelectedItemChanged: (int index) {
controller.setGender(genderOptions[index]);
},
children: genderOptions.map((String value) {
return Center(child: Text(value));
}).toList(),
),
);
}
}
class EducationDegreePicker extends StatelessWidget {
final ProfileController controller = Get.find<ProfileController>();
final List<String> degreeOptions = [
'High School Diploma'.tr,
'Associate Degree'.tr,
"Bachelor's Degree".tr,
"Master's Degree".tr,
'Doctoral Degree'.tr,
];
EducationDegreePicker({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 180,
child: CupertinoPicker(
itemExtent: 40.0,
onSelectedItemChanged: (int index) {
controller.setDegree(degreeOptions[index]);
},
children: degreeOptions.map((String value) {
return Center(child: Text(value));
}).toList(),
),
);
}
}
// NOTE: The CountryPicker and CountryPickerFromSetting widgets were not part of the main
// profile page UI, so they are excluded here to keep the file focused.
// If they are needed elsewhere, they should be moved to their own files.
@@ -1,157 +0,0 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/controller/profile/captain_profile_controller.dart';
import 'package:Tripz/main.dart';
import 'package:Tripz/views/widgets/elevated_btn.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import '../../../constant/api_key.dart';
import '../../widgets/my_textField.dart';
class ProfileCaptain extends StatelessWidget {
const ProfileCaptain({super.key});
@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: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleAvatar(
radius: Get.width * 0.26,
backgroundColor: Colors.white,
backgroundImage: CachedNetworkImageProvider(
'${AK.serverPHP}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg',
),
),
const SizedBox(height: 8.0),
Text(
box.read(BoxName.nameDriver) +
' ' +
box.read(BoxName.lastNameDriver).toString(),
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Email is'.tr} :${box.read(BoxName.emailDriver)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text(
'${'Phone Number is'.tr} :${box.read(BoxName.phoneDriver)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text(
'${'Date of Birth is'.tr} :${box.read(BoxName.dobDriver)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Sex is '.tr}:${box.read(BoxName.sexDriver)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
const Divider(
// height: 2,
endIndent: 1,
indent: 2,
thickness: 2,
),
const SizedBox(height: 8.0),
Text('Car Details'.tr, style: AppStyle.headTitle2),
const SizedBox(height: 8.0),
Text('${'VIN is'.tr} :${box.read(BoxName.vin)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Color is '.tr} :${box.read(BoxName.color)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text(
'${'Car Plate is '.tr} :${box.read(BoxName.carPlate)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Make is '.tr}:${box.read(BoxName.make)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Model is'.tr} :${box.read(BoxName.model)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text('${'Year is'.tr} :${box.read(BoxName.year)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
Text(
'${'Expiration Date '.tr} :${box.read(BoxName.expirationDate)}',
style: AppStyle.title),
const SizedBox(height: 8.0),
],
),
),
),
),
)
],
isleading: true,
action: GetBuilder<CaptainProfileController>(
builder: (controller) => IconButton(
onPressed: () {
Get.defaultDialog(
title: 'Edit Your data'.tr,
titleStyle: AppStyle.title,
content: SizedBox(
height: Get.height * .4,
child: SingleChildScrollView(
child: Column(
children: [
MyTextForm(
controller: controller.vin,
hint: 'write vin for your car'.tr,
label: 'VIN'.tr,
type: TextInputType.emailAddress,
),
MyTextForm(
controller: controller.color,
hint: 'write Color for your car'.tr,
label: 'Color'.tr,
type: TextInputType.emailAddress,
),
MyTextForm(
controller: controller.make,
hint: 'write Make for your car'.tr,
label: 'Make'.tr,
type: TextInputType.emailAddress,
),
MyTextForm(
controller: controller.model,
hint: 'write Model for your car'.tr,
label: 'Model'.tr,
type: TextInputType.emailAddress,
),
MyTextForm(
controller: controller.year,
hint: 'write Year for your car'.tr,
label: 'Year'.tr,
type: TextInputType.number,
),
MyTextForm(
controller: controller.expirationDate,
hint: 'write Expiration Date for your car'.tr,
label: 'Expiration Date'.tr,
type: TextInputType.datetime),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () => controller.updateFields())
],
),
),
));
},
icon: const Icon(Icons.edit),
),
));
}
}
@@ -1,206 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:Tripz/controller/home/profile/promos_controller.dart';
import 'package:Tripz/views/widgets/my_scafold.dart';
import 'dart:ui'; // لاستخدامه في الفاصل
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../widgets/mycircular.dart';
import 'package:dotted_line/dotted_line.dart'; // ستحتاج لإضافة هذا الباكج
// ملاحظة: ستحتاج لإضافة هذا الباكج إلى ملف pubspec.yaml الخاص بك
// flutter pub add dotted_line
// --- الويدجت الرئيسية بالتصميم الجديد ---
class PromosPassengerPage extends StatelessWidget {
const PromosPassengerPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(PromosController()); // نفس منطقك القديم
return MyScafolld(
title: "Today's Promos".tr, // عنوان أكثر جاذبية
isleading: true,
body: [
GetBuilder<PromosController>(
builder: (controller) {
if (controller.isLoading) {
return const MyCircularProgressIndicator();
}
if (controller.promoList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.local_offer_outlined,
size: 80, color: Colors.grey),
const SizedBox(height: 16),
Text("No promos available right now.".tr,
style: AppStyle.headTitle2),
Text("Check back later for new offers!".tr,
style: AppStyle.subtitle),
],
),
);
}
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: controller.promoList.length,
itemBuilder: (BuildContext context, int index) {
final promo = controller.promoList[index];
// --- استدعاء ويدجت الكوبون الجديدة ---
return _buildPromoTicket(context, promo);
},
);
},
)
],
);
}
// --- ويدجت بناء كوبون الخصم ---
Widget _buildPromoTicket(BuildContext context, Map<String, dynamic> promo) {
return Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Container(
height: 140, // ارتفاع ثابت للكوبون
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: ClipPath(
clipper: TicketClipper(), // Clipper مخصص لرسم شكل التذكرة
child: Container(
color: AppColor.secondaryColor,
child: Row(
children: [
// --- الجزء الأيسر: تفاصيل العرض ---
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
promo['description'],
style: AppStyle.headTitle.copyWith(fontSize: 18),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const Spacer(),
Row(
children: [
const Icon(Icons.calendar_today_outlined,
size: 14, color: Colors.grey),
const SizedBox(width: 6),
Text(
'${'Valid Until:'.tr} ${promo['validity_end_date']}',
style: AppStyle.subtitle
.copyWith(fontSize: 12, color: Colors.grey),
),
],
),
],
),
),
),
// --- الفاصل المنقط ---
SizedBox(
height: 110,
child: DottedLine(
direction: Axis.vertical,
lineThickness: 2.0,
dashLength: 8.0,
dashColor: AppColor.writeColor.withOpacity(0.2),
dashGapLength: 4.0,
),
),
// --- الجزء الأيمن: كود الخصم وزر النسخ ---
Expanded(
flex: 2,
child: GestureDetector(
onTap: () {
// --- نفس منطقك القديم للنسخ ---
Clipboard.setData(
ClipboardData(text: promo['promo_code']));
Get.snackbar(
'Promo Copied!'.tr,
'${'Code'.tr} ${promo['promo_code']} ${'copied to clipboard'.tr}',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.greenColor,
colorText: Colors.white,
);
},
child: Container(
color: AppColor.primaryColor.withOpacity(0.1),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'CODE'.tr,
style: AppStyle.subtitle.copyWith(
color: AppColor.primaryColor, letterSpacing: 2),
),
const SizedBox(height: 8),
Text(
promo['promo_code'],
style: AppStyle.headTitle.copyWith(
fontSize: 24, color: AppColor.primaryColor),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.copy,
size: 14, color: AppColor.primaryColor),
const SizedBox(width: 4),
Text('Copy'.tr,
style: AppStyle.subtitle
.copyWith(color: AppColor.primaryColor)),
],
),
],
),
),
),
),
],
),
),
),
),
);
}
}
// --- كلاس مخصص لرسم شكل التذكرة ---
class TicketClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
Path path = Path();
path.lineTo(0.0, size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0.0);
double radius = 10;
path.addOval(
Rect.fromCircle(center: Offset(0, size.height / 2), radius: radius));
path.addOval(Rect.fromCircle(
center: Offset(size.width, size.height / 2), radius: radius));
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
@@ -1,88 +0,0 @@
import 'package:Tripz/constant/box_name.dart';
import 'package:Tripz/main.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/constant/style.dart';
import 'package:Tripz/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),
],
),
),
]);
}
}
-213
View File
@@ -1,213 +0,0 @@
import 'package:Tripz/controller/home/home_page_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Tripz/views/lang/languages.dart';
import 'HomePage/about_page.dart';
import 'HomePage/frequentlyQuestionsPage.dart';
import 'HomePage/share_app_page.dart';
import 'HomePage/trip_record_page.dart';
// NOTE: This is a placeholder for your actual CountryPickerFromSetting widget.
// You should remove this and import your own widget.
class CountryPickerFromSetting extends StatelessWidget {
const CountryPickerFromSetting({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Change Country'.tr)),
body: Center(
child: Text('Country Picker Page Placeholder'.tr),
),
);
}
}
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
@override
Widget build(BuildContext context) {
// Using lazyPut to ensure the controller is available when needed.
Get.lazyPut(() => HomePageController());
return Scaffold(
backgroundColor:
const Color(0xFFF5F5F7), // A slightly off-white background
appBar: AppBar(
title: Text('Setting'.tr,
style: const TextStyle(
color: Colors.black87, fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
elevation: 0.5,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black87),
onPressed: () => Get.back(),
),
),
body: SafeArea(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
children: [
_buildSectionHeader('General'.tr),
_buildSettingsCard(
children: [
_buildSettingsTile(
icon: Icons.language,
color: Colors.blue,
title: 'Language'.tr,
subtitle: 'To change Language the App'.tr,
onTap: () => Get.to(() => const Language()),
),
// const Divider(height: 1, indent: 68, endIndent: 16),
// _buildSettingsTile(
// icon: Icons.map_outlined,
// color: Colors.green,
// title: 'Change Country'.tr,
// subtitle: 'You can change the Country to get all features'.tr,
// onTap: () => Get.to(() => const CountryPickerFromSetting()),
// ),
],
),
const SizedBox(height: 24),
_buildSectionHeader('Preferences'.tr),
_buildSettingsCard(
children: [
GetBuilder<HomePageController>(
builder: (controller) {
return _buildSettingsSwitchTile(
icon: Icons.vibration,
color: Colors.purple,
title: 'Vibration'.tr,
subtitle: 'Vibration feedback for all buttons'.tr,
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
);
},
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.mic_none,
color: Colors.orange,
title: 'Trips recorded'.tr,
subtitle: 'Here recorded trips audio'.tr,
onTap: () => Get.to(() => const TripsRecordedPage()),
),
],
),
const SizedBox(height: 24),
_buildSectionHeader('Support & Info'.tr),
_buildSettingsCard(
children: [
_buildSettingsTile(
icon: Icons.help_outline,
color: Colors.cyan,
title: 'Frequently Questions'.tr,
subtitle: 'Find answers to common questions'.tr,
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.info_outline,
color: Colors.indigo,
title: 'About Us'.tr,
subtitle: 'Learn more about our app and mission'.tr,
onTap: () => Get.to(() => const AboutPage()),
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.share_outlined,
color: Colors.redAccent,
title: 'Share App'.tr,
subtitle: 'Share with friends and earn rewards'.tr,
onTap: () => Get.to(() => ShareAppPage()),
),
],
),
],
),
),
);
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0, left: 8.0),
child: Text(
title,
style: TextStyle(
color: Colors.grey[700],
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
);
}
Widget _buildSettingsCard({required List<Widget> children}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
),
);
}
Widget _buildSettingsTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return ListTile(
onTap: onTap,
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
);
}
Widget _buildSettingsSwitchTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required bool value,
required ValueChanged<bool> onChanged,
}) {
return SwitchListTile(
secondary: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF007AFF), // iOS-like blue
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
);
}
}