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:
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user