This commit is contained in:
Hamza-Ayed
2025-08-04 22:43:04 +03:00
parent 83a97baed1
commit ba02d41e6d
35 changed files with 3437 additions and 2959 deletions

View File

@@ -46,8 +46,8 @@ android {
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = 3
versionName = '1.0.3'
versionCode = 7
versionName = '1.0.7'
multiDexEnabled =true
}

View File

@@ -26,7 +26,7 @@ class AK {
static final String accountSIDTwillo =
X.r(X.r(X.r(Env.accountSIDTwillo, cn), cC), cs);
static final String serverAPI = X.r(X.r(X.r(Env.serverAPI, cn), cC), cs);
static final String mapAPIKEY = X.r(X.r(X.r(Env.mapAPIKEY, cn), cC), cs);
static final String mapAPIKEY = Env.mapAPIKEY;
static final String twilloRecoveryCode =
X.r(X.r(X.r(Env.twilloRecoveryCode, cn), cC), cs);
static final String authTokenTwillo =

View File

@@ -1,7 +1,7 @@
class AppInformation {
static const String companyName = 'Intaleq';
static const String appName = 'Intaleq DRIVER';
static const String appVersion = 'Intaleq Captain';
static const String appVersion = 'Intaleq DRIVER';
static const String phoneNumber = '962798583052';
static const String linkedInProfile =
'https://www.linkedin.com/in/hamza-ayed/';

View File

@@ -296,6 +296,7 @@ class AppLink {
static String addprofile = "$profile/add.php";
static String deleteprofile = "$profile/delete.php";
static String updateprofile = "$profile/update.php";
static String updateDriverEmail = "$profile/updateDriverEmail.php";
//===================Auth============

View File

@@ -69,10 +69,8 @@ class LoginDriverController extends GetxController {
@override
void onInit() async {
box.write(BoxName.countryCode, 'Syria');
box.read(BoxName.isTest) == null ||
box.read(BoxName.isTest).toString() == '0'
? await getAppTester()
: null;
// box.write(BoxName.driverID, '34feffd3fa72d6bee56b');
await getAppTester();
super.onInit();
}
@@ -83,10 +81,15 @@ class LoginDriverController extends GetxController {
payload: {'appPlatform': AppInformation.appName});
if (res != 'failure') {
var d = jsonDecode(res);
isTest = d['message'][0]['isTest'];
box.write(BoxName.isTest, isTest);
Log.print('isTest: ${box.read(BoxName.isTest)}');
update();
} else {
isTest = 0;
box.write(BoxName.isTest, isTest);
update();
return false;
}
}

View File

@@ -77,7 +77,7 @@ class OtpVerificationController extends GetxController {
try {
final response = await CRUD().post(
link:
'${AppLink.server}/auth/token_passenger/driver/verify_otp_driver/.php',
'${AppLink.server}/auth/token_passenger/driver/verify_otp_driver.php',
payload: {
'phone_number': phone,
'otp': otpCode.value,

View File

@@ -2,10 +2,10 @@ import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/foundation.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/box_name.dart';
import '../../constant/char_map.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
class EncryptionHelper {
static EncryptionHelper? _instance;
@@ -29,16 +29,14 @@ class EncryptionHelper {
return; // Prevent re-initialization
}
debugPrint("Initializing EncryptionHelper...");
// Read stored keys
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
var initializationVector =
r(Env.initializationVector).toString().split(Env.addd)[0];
// Log.print('initializationVector: ${initializationVector}');
// Set the global instance
_instance = EncryptionHelper._(
encrypt.Key.fromUtf8(keyOfApp),
encrypt.IV.fromUtf8(initializationVector),
encrypt.Key.fromUtf8(keyOfApp!),
encrypt.IV.fromUtf8(initializationVector!),
);
debugPrint("EncryptionHelper initialized successfully.");
}
@@ -46,8 +44,8 @@ class EncryptionHelper {
/// Encrypts a string
String encryptData(String plainText) {
try {
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc)); // AES-GCM
final encrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
} catch (e) {

View File

@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:sefer_driver/constant/table_names.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
@@ -87,7 +88,7 @@ class LocationController extends GetxController {
Get.find<CaptainWalletController>().totalPoints.toString();
isActive = Get.find<HomeCaptainController>().isActive;
if (isActive && double.parse(totalPoints) > -300) {
if (isActive && double.parse(totalPoints) > -30000) {
await getLocation();
if (myLocation.latitude == 0 && myLocation.longitude == 0) return;
@@ -147,13 +148,25 @@ class LocationController extends GetxController {
Get.find<HomeCaptainController>()
.mapHomeCaptainController
?.animateCamera(
CameraUpdate.newLatLng(
LatLng(
myLocation.latitude,
myLocation.longitude,
CameraUpdate.newCameraPosition(
CameraPosition(
bearing: Get.find<LocationController>().heading,
target: myLocation,
zoom: 17, // Adjust zoom level as needed
),
),
);
// if (Get.isRegistered()) {
// Get.find<MapDriverController>().mapController?.animateCamera(
// CameraUpdate.newCameraPosition(
// CameraPosition(
// bearing: Get.find<LocationController>().heading,
// target: myLocation,
// zoom: 17, // Adjust zoom level as needed
// ),
// ),
// );
// }
}
} catch (e) {
print('Location update error: $e');

View File

@@ -15,13 +15,15 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
Future<void> checkForUpdate(BuildContext context) async {
final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.buildNumber;
final version = packageInfo.version;
// print('currentVersion is : $currentVersion');
Log.print('version: ${version}');
print('currentVersion is : $currentVersion');
// Fetch the latest version from your server
String latestVersion = await getPackageInfo();
box.write(BoxName.packagInfo, version);
@@ -45,8 +47,8 @@ Future<String> getPackageInfo() async {
void showUpdateDialog(BuildContext context) {
final String storeUrl = Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=com.sefer_driver'
: 'https://apps.apple.com/ae/app/sefer-driver/id6502189302';
? 'https://play.google.com/store/apps/details?id=com.intaleq_driver'
: 'https://apps.apple.com/ae/app/intaleq-driver/id6502189302';
showGeneralDialog(
context: context,

View File

@@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart' as path_provider;
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
@@ -310,17 +311,20 @@ class ImageController extends GetxController {
Log.print('request: ${request}');
var length = await file.length();
var stream = http.ByteStream(file.openRead());
final headers = {
'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
};
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
request.headers.addAll(headers);
// Set the file name to the driverID
request.files.add(
http.MultipartFile(
'image',
@@ -418,14 +422,11 @@ class ImageController extends GetxController {
try {
await uploadImage(
compressedImage,
{
'driverID':
(box.read(BoxName.driverID)) ?? (box.read(BoxName.passengerID)),
'imageType': imageType
},
{'driverID': (box.read(BoxName.driverID)), 'imageType': imageType},
link,
);
} catch (e) {
Log.print('e: ${e}');
mySnackeBarError('Image Upload Failed'.tr);
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor);

View File

@@ -8,6 +8,7 @@ import 'package:sefer_driver/controller/functions/crud.dart';
import '../../../constant/table_names.dart';
import '../../../main.dart';
import '../../../print.dart';
class DriverBehaviorController extends GetxController {
Future<List<Map<String, dynamic>>> getAllData() async {
@@ -27,16 +28,17 @@ class DriverBehaviorController extends GetxController {
);
if (response != 'failure') {
final json = jsonDecode(response.body);
final json = jsonDecode(response);
overallScore.value =
double.parse(json['data']['overall_behavior_score'].toString());
lastTrips.value = json['data']['last_10_trips'];
double.parse(json['message']['overall_behavior_score'].toString());
lastTrips.value = json['message']['last_10_trips'];
} else {
// Get.snackbar("Error", json['message'] ?? "Unknown error");
}
} catch (e) {
Get.snackbar("Error", "Exception: $e");
// Get.snackbar("Error", "Exception: $e");
Log.print('e: ${e}');
} finally {
isLoading.value = false;
}

View File

@@ -281,6 +281,7 @@ class HomeCaptainController extends GetxController {
onMapCreated(mapHomeCaptainController!);
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
getRefusedOrderByCaptain();
box.write(BoxName.statusDriverLocation, 'off');
// LocationController().getLocation();
super.onInit();
}

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
@@ -107,11 +108,16 @@ class MapDriverController extends GetxController {
LatLng latLngPassengerLocation = LatLng(0, 0);
late LatLng latLngPassengerDestination = LatLng(0, 0);
List<Map<String, dynamic>> routeSteps = [];
String currentInstruction = "";
int currentStepIndex = 0;
void onMapCreated(GoogleMapController controller) async {
myLocation = Get.find<LocationController>().myLocation;
// myLocation = myLocation;
mapController = controller;
controller.getVisibleRegion();
// LatLngBounds bounds = await controller.getVisibleRegion();
controller.animateCamera(
CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
);
@@ -410,20 +416,20 @@ class MapDriverController extends GetxController {
'order_id': (rideId).toString(),
'status': 'Begin'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
'id': (rideId),
'rideTimeStart': DateTime.now().toString(),
'status': 'Begin',
});
CRUD().post(
link: '${AppLink.endPoint}/rides/driver_order/add.php',
payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(),
'status': 'Begin'
});
}
// if (AppLink.endPoint != AppLink.seferCairoServer) {
// CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
// 'id': (rideId),
// 'rideTimeStart': DateTime.now().toString(),
// 'status': 'Begin',
// });
// CRUD().post(
// link: '${AppLink.endPoint}/rides/driver_order/add.php',
// payload: {
// 'driver_id': box.read(BoxName.driverID).toString(),
// 'order_id': (rideId).toString(),
// 'status': 'Begin'
// });
// }
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Trip is Begin'.tr,
box.read(BoxName.nameDriver).toString(),
@@ -591,28 +597,34 @@ class MapDriverController extends GetxController {
}
Future<void> finishRideFromDriver() async {
double distanceToDestination = Geolocator.distanceBetween(
latLngPassengerDestination.latitude,
latLngPassengerDestination.longitude,
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
// double distanceToDestination = Geolocator.distanceBetween(
// latLngPassengerDestination.latitude,
// latLngPassengerDestination.longitude,
// Get.find<LocationController>().myLocation.latitude,
// Get.find<LocationController>().myLocation.longitude,
// );
final originalDistanceM = double.parse(distance.toString()) * 1000;
// 2. احسب المسافة التي قطعها السائق حتى الآن
final movedDistanceM = originalDistanceM - distanceToDestination;
final movedDistanceM = Geolocator.distanceBetween(
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
latLngPassengerDestination.latitude,
latLngPassengerDestination.longitude,
);
// originalDistanceM - distanceToDestination;
// 3. عتبة ثلث المسافة
final oneThirdDistanceM = originalDistanceM / 3;
// Logging للتتبع
Log.print('originalDistanceM: $originalDistanceM');
Log.print('distanceToDestinationM: $distanceToDestination');
// Log.print('distanceToDestinationM: $distanceToDestination');
Log.print('movedDistanceM: $movedDistanceM');
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
if (movedDistanceM < oneThirdDistanceM) {
if (movedDistanceM > oneThirdDistanceM * 2) {
MyDialog().getDialog(
'Are you sure to exit ride?'.tr,
'',
@@ -1177,6 +1189,7 @@ class MapDriverController extends GetxController {
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
var response = await CRUD().getGoogleApi(link: url, payload: {});
Log.print('response: ${response}');
data = response['routes'][0]['legs'];
distanceBetweenDriverAndPassengerWhenConfirm =
(data[0]['distance']['value']) / 1000;
@@ -1230,6 +1243,84 @@ class MapDriverController extends GetxController {
}
}
void checkForNextStep(LatLng currentPosition) {
if (currentStepIndex >= routeSteps.length) return;
final step = routeSteps[currentStepIndex];
final endLocation = step['end_location'];
final endLatLng = LatLng(endLocation['lat'], endLocation['lng']);
final distance = calculateDistance(
currentPosition.latitude,
currentPosition.longitude,
endLatLng.latitude,
endLatLng.longitude,
);
if (distance < 50) {
// 50 متر قبل النقطة
currentStepIndex++;
if (currentStepIndex < routeSteps.length) {
currentInstruction = _parseInstruction(
routeSteps[currentStepIndex]['html_instructions']);
Get.isRegistered<TextToSpeechController>()
? Get.find<TextToSpeechController>().speakText(currentInstruction)
: Get.put(TextToSpeechController()).speakText(currentInstruction);
Log.print('Current Instruction: $currentInstruction');
update();
}
}
}
/// Calculates the distance in meters between two latitude/longitude points.
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
const double earthRadius = 6371000; // meters
double dLat = _degreesToRadians(lat2 - lat1);
double dLon = _degreesToRadians(lon2 - lon1);
double a = (sin(dLat / 2) * sin(dLat / 2)) +
cos(_degreesToRadians(lat1)) *
cos(_degreesToRadians(lat2)) *
(sin(dLon / 2) * sin(dLon / 2));
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
double distance = earthRadius * c;
return distance;
}
double _degreesToRadians(double degrees) {
return degrees * (3.1415926535897932 / 180.0);
}
String _parseInstruction(String htmlInstruction) {
return htmlInstruction.replaceAll(RegExp(r'<[^>]*>'), '');
}
void checkDestinationProximity() {
final distance = calculateDistance(
myLocation.latitude,
myLocation.longitude,
latLngPassengerDestination.latitude,
latLngPassengerDestination.longitude,
);
if (distance < 300) {
// 300 متر قبل الوجهة
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
"You are near the destination".tr,
"You are near the destination".tr,
tokenPassenger,
[
box.read(BoxName.driverID),
rideId,
box.read(BoxName.tokenDriver),
paymentAmount.toString()
],
'ding.wav',
);
// يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة
}
}
getMapDestination(String origin, destination) async {
var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
@@ -1243,6 +1334,22 @@ class MapDriverController extends GetxController {
double lng = points[i][1].toDouble();
polylineCoordinatesDestination.add(LatLng(lat, lng));
}
// استخراج الخطوات
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
Log.print('routeSteps: ${routeSteps}');
currentStepIndex = 0;
if (routeSteps.isNotEmpty) {
currentInstruction =
_parseInstruction(routeSteps[0]['html_instructions']);
Log.print('currentInstruction: ${currentInstruction}');
Get.isRegistered<TextToSpeechController>()
? Get.find<TextToSpeechController>().speakText(currentInstruction)
: Get.put(TextToSpeechController()).speakText(currentInstruction);
}
update();
// دالة مساعدة لتنظيف التعليمات
if (polyLinesDestination.isNotEmpty) {
// clearPolyline();
var polyline = Polyline(
@@ -1404,6 +1511,7 @@ class MapDriverController extends GetxController {
hours = durationToAdd.inHours;
minutes = (durationToAdd.inMinutes % 60).round();
calculateConsumptionFuel();
updateLocation();
// cancelCheckRidefromPassenger();
// checkIsDriverNearPassenger();
super.onInit();

View File

@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class NavigationStep {
final String instruction;
final String maneuver;
final double distance;
final String duration;
final LatLng startLocation;
final LatLng endLocation;
final String htmlInstructions;
NavigationStep({
required this.instruction,
required this.maneuver,
required this.distance,
required this.duration,
required this.startLocation,
required this.endLocation,
required this.htmlInstructions,
});
factory NavigationStep.fromJson(Map<String, dynamic> json) {
return NavigationStep(
instruction: json['html_instructions'] ?? '',
maneuver: json['maneuver'] ?? 'straight',
distance: (json['distance']['value'] ?? 0).toDouble(),
duration: json['duration']['text'] ?? '',
startLocation: LatLng(
json['start_location']['lat'].toDouble(),
json['start_location']['lng'].toDouble(),
),
endLocation: LatLng(
json['end_location']['lat'].toDouble(),
json['end_location']['lng'].toDouble(),
),
htmlInstructions: json['html_instructions'] ?? '',
);
}
// Get clean instruction text (remove HTML tags)
String get cleanInstruction {
return instruction
.replaceAll(RegExp(r'<[^>]*>'), '')
.replaceAll('&nbsp;', ' ');
}
// Get instruction icon based on maneuver
IconData get instructionIcon {
switch (maneuver.toLowerCase()) {
case 'turn-left':
return Icons.turn_left;
case 'turn-right':
return Icons.turn_right;
case 'turn-slight-left':
return Icons.turn_slight_left;
case 'turn-slight-right':
return Icons.turn_slight_right;
case 'turn-sharp-left':
return Icons.turn_sharp_left;
case 'turn-sharp-right':
return Icons.turn_sharp_right;
case 'uturn-left':
case 'uturn-right':
return Icons.u_turn_left;
case 'straight':
return Icons.straight;
case 'ramp-left':
return Icons.ramp_left;
case 'ramp-right':
return Icons.ramp_right;
case 'merge':
return Icons.merge;
case 'fork-left':
case 'fork-right':
return Icons.call_split;
case 'ferry':
return Icons.directions_boat;
case 'roundabout-left':
case 'roundabout-right':
return Icons.roundabout_left;
default:
return Icons.navigation;
}
}
}

View File

@@ -335,7 +335,83 @@ Raih Gai: For same-day return trips longer than 50km.
"ATTIJARIWAFA BANK Egypt": "البنك التجاري وفا مصر",
"Morning Promo": "بونص الصباح",
"Show my Cars": "عرض سياراتي",
"Add criminal page": "إضافة الفيش الجنائي",
'Behavior Score': "درجة السلوك",
"Driver Behavior": "سلوك السائق",
"Last 10 Trips": "آخر 10 رحلات",
"Trip ID": "رقم الرحلة", 'Vehicle Details': "تفاصيل المركبة",
'Hard Brake': "فرملة قوية",
'Show behavior page': "عرض صفحة السلوك",
'Coming Soon': "قريبًا",
'Logout': "تسجيل الخروج",
'Contact Support to Recharge': "تواصل مع الدعم لإعادة الشحن",
'Are you sure you want to logout?':
"هل أنت متأكد أنك تريد تسجيل الخروج؟",
"How to use App": "كيفية استخدام التطبيق",
'This service will be available soon.':
"هذه الخدمة ستكون متاحة قريبًا.",
'Change the app language': "تغيير لغة التطبيق",
'Get features for your country': "احصل على ميزات لبلدك",
'Vibration feedback for buttons': "ردود فعل الاهتزاز للأزرار",
'Run Google Maps directly': "تشغيل خرائط جوجل مباشرة",
'reviews': "المراجعات",
"Trip Summary with": "ملخص الرحلة مع",
"Original Fare": "الأجرة الأصلية",
"Your Earnings": "أرباحك",
"Exclusive offers and discounts always with the Sefer app":
"عروض وخصومات حصرية دائماً مع تطبيق سفر",
"Would the passenger like to settle the remaining fare using their wallet?":
"هل يرغب الراكب بتسوية الأجرة المتبقية من محفظته؟",
"Yes, Pay": "نعم، ادفع",
"How much Passenger pay?": "كم دفع الراكب؟",
"Passenger paid amount": "المبلغ الذي دفعه الراكب",
"Add to Passenger Wallet": "أضف إلى محفظة الراكب",
"How was the passenger?": "كيف كان الراكب؟",
"Add a comment (optional)": "أضف تعليقاً (اختياري)",
"Type something...": "اكتب شيئاً...",
"Submit rating": "إرسال التقييم",
'Trip Summary with': "ملخص الرحلة مع",
'Original Fare': "الأجرة الأصلية",
'Your Earnings': "أرباحك",
'Exclusive offers and discounts always with the Intaleq app':
"عروض وخصومات حصرية دائماً مع تطبيق انطلق",
'Enter your email'
'''Types of Trips in Intaleq:
- Comfort: For cars newer than 2017 with air conditioning.
- Lady: For girl drivers.
- Speed: For fixed salary and endpoints.
- Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
- Raih Gai: For same-day return trips longer than 50km.''':
'''أنواع الرحلات في Intaleq:
- راحة: للسيارات الأحدث من 2017 مع تكييف الهواء.
- للسائقات الإناث.
- سبيد: براتب ثابت ونقاط نهاية محددة.
- مشاوير: للرحلات المرنة حيث يختار الركاب السيارة والسائق باتفاق مسبق.
- رحّي غاي: للرحلات ذات العودة في نفس اليوم التي تزيد عن 50 كم.''',
'''Intaleq Wallet Features:
- Transfer money multiple times.
- Transfer to anyone.
- Make purchases.
- Charge your account.
- Charge a friend's Intaleq account.
- Store your money with us and receive it in your bank as a monthly salary.''':
'''ميزات محفظة Intaleq:
- تحويل الأموال عدة مرات.
- التحويل إلى أي شخص.
- إجراء عمليات شراء.
- شحن حسابك.
- شحن حساب Intaleq لصديق.
- قم بتخزين أموالك معنا واستلامها في بنكك كراتب شهري.''',
'Are you sure you want to logout?':
"هل أنت متأكد أنك تريد تسجيل الخروج؟",
'My Cars': "سياراتي",
'Bank Account': "الحساب البنكي",
'Behavior Page': "صفحة السلوك",
'Personal Information': "المعلومات الشخصية",
"Add criminal page": "إضافة صفحة جنائية",
"Overall Behavior Score": "درجة السلوك العامة",
"Add new car": "إضافة سيارة جديدة",
"You have gift 300 L.E": "لديك هدية بقيمة 300 جنيه.",
// "VIP Order": "طلب VIP",

View File

@@ -7,6 +7,8 @@ import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/main.dart';
import '../../views/widgets/error_snakbar.dart';
class CaptainProfileController extends GetxController {
bool isLoading = false;
TextEditingController vin = TextEditingController();
@@ -15,6 +17,25 @@ class CaptainProfileController extends GetxController {
TextEditingController model = TextEditingController();
TextEditingController year = TextEditingController();
TextEditingController expirationDate = TextEditingController();
final TextEditingController emailController = TextEditingController();
updateEmail() async {
var payload = {
'id': box.read(BoxName.driverID).toString(),
'email': emailController.text,
};
var res =
await CRUD().post(link: AppLink.updateDriverEmail, payload: payload);
if ((res)['status'] == 'success') {
box.write(BoxName.email, emailController.text);
update();
Get.back();
} else {
mySnackeBarError((res)['message']);
}
}
Future updateFields() async {
var payload = {

View File

@@ -1,16 +1,15 @@
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../constant/style.dart';
import '../../controller/rate/rate_conroller.dart';
// Changed: تم إعادة بناء الصفحة بالكامل لتحسين التصميم وتجربة المستخدم
class RatePassenger extends StatelessWidget {
final RateController controller = Get.put(RateController());
@@ -18,120 +17,147 @@ class RatePassenger extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Rate Passenger'.tr,
body: [
GetBuilder<RateController>(builder: (controller) {
return Positioned(
top: 40,
left: Get.width * .1,
right: Get.width * .1,
child: Container(
decoration: AppStyle.boxDecoration,
// New: استخدام Scaffold القياسي لهيكل أكثر قوة ومرونة
return Scaffold(
appBar: AppBar(
title: Text('Rate Passenger'.tr),
centerTitle: true,
automaticallyImplyLeading: false, // New: إزالة سهم الرجوع
backgroundColor: Colors.white,
elevation: 1,
),
// New: استخدام GetBuilder على مستوى الجسم لضمان تحديث الواجهة
body: GetBuilder<RateController>(
builder: (controller) {
// New: استخدام SingleChildScrollView لتجنب مشاكل الـ overflow عند ظهور لوحة المفاتيح
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(4),
child: Container(
height: Get.height * .25,
decoration: AppStyle.boxDecoration1,
// New: استدعاء ودجت منفصلة لكل قسم لزيادة التنظيم
_buildPriceSummaryCard(context, controller),
const SizedBox(height: 16),
// New: قسم المحفظة يظهر فقط إذا لم يتم التحقق منه
if (controller.walletChecked != 'true')
_buildWalletSection(context, controller),
const SizedBox(height: 16),
_buildRatingSection(context, controller),
const SizedBox(height: 24),
MyElevatedButton(
title: 'Submit rating'.tr,
onPressed: () => controller.addRateToPassenger(),
// New: جعل الزر يأخذ العرض الكامل لمزيد من الوضوح
// isFullWidth: true,
),
],
),
),
);
},
),
);
}
// New: ودجت منفصلة لعرض بطاقة ملخص السعر
Widget _buildPriceSummaryCard(
BuildContext context, RateController controller) {
final MapDriverController mapController = Get.find<MapDriverController>();
final double originalPrice =
double.tryParse(controller.price.toString()) ?? 0.0;
final double priceAfterDiscount = originalPrice - (originalPrice * 0.12);
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${'Total price from '.tr}${Get.find<MapDriverController>().passengerName}',
style: AppStyle.title,
'${'Trip Summary with'.tr} ${mapController.passengerName}',
style: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
Container(
decoration: BoxDecoration(
border: Border.all(
width: 2,
color: AppColor.redColor,
)),
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
(double.parse(controller.price.toString()) -
double.parse(controller.price
.toString()) *
.12)
.toStringAsFixed(2),
const Divider(height: 24, thickness: 1),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Original Fare'.tr, style: AppStyle.title),
Text(
priceAfterDiscount.toStringAsFixed(2),
style: AppStyle.number.copyWith(
fontSize: 16,
color: AppColor.redColor,
textBaseline: TextBaseline.ideographic,
decoration: TextDecoration.lineThrough,
decorationColor: AppColor.redColor),
),
),
],
),
const SizedBox(
height: 10,
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Your Earnings'.tr,
style:
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
border: Border.all(
width: 2,
color: AppColor.greenColor,
)),
child: Padding(
padding: const EdgeInsets.all(4),
color: AppColor.greenColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.greenColor),
),
child: Text(
Get.find<MapDriverController>()
.paymentAmount,
style: AppStyle.number,
mapController.paymentAmount,
style: AppStyle.number
.copyWith(color: AppColor.greenColor, fontSize: 20),
),
),
],
),
const SizedBox(
height: 10,
),
const SizedBox(height: 12),
Text(
'Exclusive offers and discounts always with the Sefer app'
.tr,
'Exclusive offers and discounts always with the Sefer app'.tr,
textAlign: TextAlign.center,
style: AppStyle.title
.copyWith(color: AppColor.redColor),
.copyWith(color: AppColor.redColor, fontSize: 13),
)
],
)),
),
controller.walletChecked != 'true'
? controller.ispassengerWantWalletFromDriver
? Container(
decoration: AppStyle.boxDecoration1,
),
);
}
// New: ودجت منفصلة لقسم الدفع عبر المحفظة
Widget _buildWalletSection(BuildContext context, RateController controller) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
"How much Passenger pay?".tr,
style: AppStyle.title,
),
Form(
key: controller.formKey,
child: MyTextForm(
controller:
controller.passengerPayAmount,
label: "passenger amount to me".tr,
hint: "passenger amount to me".tr,
type: const TextInputType
.numberWithOptions(decimal: true),
padding: const EdgeInsets.all(16.0),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: controller.ispassengerWantWalletFromDriver
? _buildAmountInput(controller)
: _buildWalletQuery(controller),
),
),
MyElevatedButton(
title: "Press here".tr,
onPressed: () {
controller.addPassengerWallet();
},
)
],
),
),
)
: Container(
width: Get.width * .73,
decoration: AppStyle.boxDecoration1,
child: Column(
);
}
// New: واجهة سؤال استخدام المحفظة
Widget _buildWalletQuery(RateController controller) {
return Column(
key: const ValueKey('walletQuery'),
children: [
Text(
"Would the passenger like to settle the remaining fare using their wallet?"
@@ -139,120 +165,165 @@ class RatePassenger extends StatelessWidget {
style: AppStyle.title,
textAlign: TextAlign.center,
),
MyElevatedButton(
title: "Press here".tr,
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'No'.tr,
onPressed: () {
// يمكنك هنا تحديد ما يحدث عند الضغط على "لا"
// حاليًا، ستبقى الواجهة كما هي أو يمكنك إخفاؤها
},
kolor: AppColor.redColor,
),
),
const SizedBox(width: 10),
Expanded(
child: MyElevatedButton(
title: 'Yes, Pay'.tr,
onPressed: () {
controller.passengerWantPay();
},
),
),
],
)
],
);
}
// New: واجهة إدخال المبلغ المدفوع
Widget _buildAmountInput(RateController controller) {
return Column(
key: const ValueKey('amountInput'),
children: [
Text(
"How much Passenger pay?".tr,
style: AppStyle.title,
),
const SizedBox(height: 12),
Form(
key: controller.formKey,
child: MyTextForm(
controller: controller.passengerPayAmount,
label: "Passenger paid amount".tr,
hint: "0.00",
type: const TextInputType.numberWithOptions(decimal: true),
),
),
const SizedBox(height: 12),
MyElevatedButton(
title: "Add to Passenger Wallet".tr,
// isFullWidth: true,
onPressed: () {
controller.addPassengerWallet();
},
)
: const SizedBox(),
const SizedBox(
height: 20,
),
Center(
child: RatingBar.builder(
],
);
}
// New: ودجت منفصلة لقسم التقييم وكتابة الملاحظات
Widget _buildRatingSection(BuildContext context, RateController controller) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text('How was the passenger?'.tr,
style: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 20),
RatingBar.builder(
initialRating: 0,
itemCount: 5,
itemSize: 50,
itemPadding: const EdgeInsets.symmetric(horizontal: 2),
itemPadding: const EdgeInsets.symmetric(horizontal: 4),
itemBuilder: (context, index) {
switch (index) {
case 0:
return const Icon(
Icons.sentiment_very_dissatisfied,
color: Colors.red,
);
return const Icon(Icons.sentiment_very_dissatisfied,
color: Colors.red);
case 1:
return const Icon(
Icons.sentiment_dissatisfied,
color: Colors.redAccent,
);
return const Icon(Icons.sentiment_dissatisfied,
color: Colors.redAccent);
case 2:
return const Icon(
Icons.sentiment_neutral,
color: Colors.amber,
);
return const Icon(Icons.sentiment_neutral,
color: Colors.amber);
case 3:
return const Icon(
Icons.sentiment_satisfied,
color: Colors.lightGreen,
);
return const Icon(Icons.sentiment_satisfied,
color: Colors.lightGreen);
case 4:
return const Icon(
Icons.sentiment_very_satisfied,
color: Colors.green,
);
return const Icon(Icons.sentiment_very_satisfied,
color: Colors.green);
default:
return const Icon(
Icons.sentiment_neutral,
color: Colors.amber,
);
} //
return const Icon(Icons.sentiment_neutral,
color: Colors.amber);
}
},
onRatingUpdate: (rating) {
controller.selectRateItem(rating);
},
),
),
const SizedBox(
height: 20,
),
SizedBox(
width: Get.width * .75,
child: TextFormField(
const SizedBox(height: 24),
TextFormField(
maxLines: 4,
minLines: 1,
minLines: 2,
keyboardType: TextInputType.multiline,
controller: controller.comment,
decoration: InputDecoration(
labelText: 'Enter your Note'.tr,
hintText: 'Type something...',
prefixIcon: const Icon(
Icons.rate_review), // Add an icon as a prefix
suffixIcon: IconButton(
icon: const Icon(
Icons.clear,
color: AppColor.redColor,
), // Add an icon as a suffix
onPressed: () {
controller.comment.clear();
},
),
border:
const OutlineInputBorder(), // Add a border around the input field
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color:
Colors.blue), // Customize the border color
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors
.green), // Customize the border color when focused
),
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors
.red), // Customize the border color when there's an error
labelText: 'Add a comment (optional)'.tr,
hintText: 'Type something...'.tr,
prefixIcon: const Icon(Icons.rate_review_outlined),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
),
),
),
const SizedBox(
height: 20,
),
MyElevatedButton(
title: 'Submit rating'.tr,
onPressed: () => controller.addRateToPassenger())
],
),
));
}),
],
isleading: false,
),
);
}
}
// New: إضافة isFullWidth إلى MyElevatedButton لتسهيل التحكم في العرض
// تأكد من تحديث ملف elevated_btn.dart بهذا التغيير
/*
class MyElevatedButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
final Color? kolor;
final bool isFullWidth; // New property
const MyElevatedButton({
Key? key,
required this.title,
required this.onPressed,
this.kolor,
this.isFullWidth = false, // Default value
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: isFullWidth ? double.infinity : null, // Apply width
height: 50, // Standard height
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: kolor ?? AppColor.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: AppStyle.title.copyWith(color: Colors.white),
),
child: Text(title, style: const TextStyle(color: Colors.white)),
),
);
}
}
*/

View File

@@ -155,11 +155,12 @@ class AuthScreen extends StatelessWidget {
.validate()) {
controller.logintest(
controller
.emailController.text
.passwordController.text
.trim(),
controller
.passwordController.text
.trim());
.emailController.text
.trim(),
);
}
},
child: Text(

View File

@@ -1,13 +1,13 @@
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/profile/setting_controller.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/profile/setting_controller.dart';
import 'package:sefer_driver/views/lang/languages.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import '../../../../controller/functions/vibrate.dart';
// تأكد من صحة مسارات الاستيراد هذه
import '../../../../controller/functions/vibrate.dart'; // Controller with isVibrate
import '../../../auth/country_widget.dart';
import 'about_us.dart';
import 'frequantly_question.dart';
@@ -18,140 +18,188 @@ class SettingsCaptain extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(SettingController());
Get.put(HomePageController());
// تحميل الـ Controllers المطلوبة
final settingsController = Get.put(SettingController());
final homeController = Get.put(HomePageController());
return MyScafolld(
title: 'Settings'.tr,
isleading: true,
body: [
ListView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
children: <Widget>[
// General Section
_buildSectionHeader('General'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
// --- القسم الأول: عام ---
_buildSectionHeader('General'.tr, context),
_buildSettingsCard(
children: [
CupertinoListTile(
leading: const Icon(CupertinoIcons.globe),
title: Text('Language'.tr, style: AppStyle.headTitle2),
subtitle: Text('You can change the language of the app'.tr,
style: AppStyle.subtitle),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(const Language()),
_buildListTile(
icon: Icons.language_outlined,
title: 'Language'.tr,
subtitle: 'Change the app language'.tr,
onTap: () => Get.to(() => const Language()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.flag_fill),
title: Text('Change Country'.tr, style: AppStyle.headTitle2),
subtitle: Text(
'You can change the Country to get all features'.tr,
style: AppStyle.subtitle),
trailing: const CupertinoListTileChevron(),
_buildListTile(
icon: Icons.flag_outlined,
title: 'Change Country'.tr,
subtitle: 'Get features for your country'.tr,
onTap: () => Get.to(
MyScafolld(
() => MyScafolld(
title: 'Change Country'.tr,
body: [CountryPickerFromSetting()],
isleading: true,
// isCupertino: true, // Indicate it's a Cupertino style page
),
),
),
],
),
const SizedBox(height: 20),
// App Preferences Section
_buildSectionHeader('App Preferences'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
// --- القسم الثاني: تفضيلات التطبيق ---
_buildSectionHeader('App Preferences'.tr, context),
_buildSettingsCard(
children: [
CupertinoListTile(
leading: Icon(
CupertinoIcons.map_pin_ellipse,
_buildSwitchTile(
icon: Icons.map_outlined,
color: AppColor.redColor,
title: 'Google Map App'.tr,
subtitle: 'Run Google Maps directly'.tr,
controller: settingsController,
valueGetter: (ctrl) => (ctrl).isGoogleMapsEnabled,
onChanged: (ctrl) => (ctrl).onChangMapApp(),
),
title: Text('Google Map App'.tr, style: AppStyle.headTitle2),
subtitle: Text(
'If you want to make Google Map App run directly when you apply order'
.tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<SettingController>(
builder: (settingController) {
return CupertinoSwitch(
value: settingController.isGoogleMapsEnabled,
activeTrackColor: AppColor.primaryColor,
onChanged: (bool value) {
settingController.onChangMapApp();
},
);
},
),
),
CupertinoListTile(
leading: Icon(Icons.vibration),
title: Text('Vibration'.tr, style: AppStyle.headTitle2),
subtitle: Text(
"You can change the vibration feedback for all buttons".tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<HomePageController>(
builder: (controller) => CupertinoSwitch(
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
activeTrackColor: AppColor.primaryColor,
),
),
onTap: () => print('3'),
_buildSwitchTile(
icon: Icons.vibration,
title: 'Vibration'.tr,
subtitle: 'Vibration feedback for buttons'.tr,
controller: homeController,
valueGetter: (ctrl) => (ctrl).isVibrate,
onChanged: (ctrl) => (ctrl)
.changeVibrateOption(true), // قد تحتاج لتعديل الدالة
),
],
),
const SizedBox(height: 20),
// Help & Support Section
_buildSectionHeader('Help & Support'.tr),
CupertinoListSection(
margin: EdgeInsets.zero,
// --- القسم الثالث: المساعدة والدعم ---
_buildSectionHeader('Help & Support'.tr, context),
_buildSettingsCard(
children: [
CupertinoListTile(
leading: const Icon(CupertinoIcons.question_circle_fill),
title: Text('Frequently Questions'.tr,
style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
_buildListTile(
icon: Icons.quiz_outlined,
title: 'Frequently Questions'.tr,
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.hand_raised_fill),
title:
Text("How to use Intaleq".tr, style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
_buildListTile(
icon: Icons.support_agent,
title: "How to use App".tr,
onTap: () => Get.to(() => const UsingAppPage()),
),
CupertinoListTile(
leading: const Icon(CupertinoIcons.info_circle_fill),
title: Text('About Us'.tr, style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
_buildListTile(
icon: Icons.info_outline,
title: 'About Us'.tr,
onTap: () => Get.to(() => const AboutPage()),
),
],
),
const SizedBox(height: 16),
const SizedBox(height: 20),
// --- القسم الرابع: تسجيل الخروج ---
_buildSectionHeader('Account'.tr, context),
_buildSettingsCard(
children: [
ListTile(
leading: const Icon(Icons.logout, color: Colors.red),
title: Text(
'Logout'.tr,
style: const TextStyle(
color: Colors.red, fontWeight: FontWeight.w500),
),
onTap: () {
MyDialog().getDialog(
'Logout'.tr,
'Are you sure you want to logout?'.tr,
() {
// أضف دالة تسجيل الخروج هنا
Get.back(); // لإغلاق مربع الحوار
},
// isConfirmation: true,
);
},
)
],
)
],
),
],
isleading: true,
// isCupertino: true, // Indicate this screen is generally Cupertino style
);
}
Widget _buildSectionHeader(String title) {
// ويدجت لبناء عنوان كل قسم
Widget _buildSectionHeader(String title, BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 16.0, top: 20.0, bottom: 10.0),
padding: const EdgeInsets.only(left: 8.0, bottom: 12.0),
child: Text(
title,
style: const TextStyle(
fontSize: 17.0,
fontWeight: FontWeight.w600,
color: CupertinoColors.secondaryLabel,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Colors.grey.shade600,
fontWeight: FontWeight.bold,
),
),
);
}
// ويدجت لبناء بطاقة الإعدادات
Widget _buildSettingsCard({required List<Widget> children}) {
return Card(
elevation: 2,
shadowColor: Colors.black.withOpacity(0.1),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
clipBehavior: Clip.antiAlias, // مهم لجعل splash effect داخل حدود البطاقة
child: Column(children: children),
);
}
// ويدجت لبناء عنصر قابل للضغط (مثل اللغة، عن التطبيق)
Widget _buildListTile({
required IconData icon,
required String title,
String? subtitle,
required VoidCallback onTap,
}) {
return ListTile(
leading: Icon(icon, color: Colors.grey.shade700),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: subtitle != null ? Text(subtitle) : null,
trailing:
const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey),
onTap: onTap,
);
}
// ويدجت لبناء عنصر يحتوي على مفتاح تفعيل/إلغاء (Switch)
Widget _buildSwitchTile<T extends GetxController>({
required IconData icon,
Color? color,
required String title,
required String subtitle,
required T controller,
required bool Function(T) valueGetter,
required Function(T) onChanged,
}) {
return GetBuilder<T>(
init: controller,
builder: (ctrl) {
return SwitchListTile(
secondary: Icon(icon, color: color ?? Colors.grey.shade700),
title:
Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text(subtitle),
value: valueGetter(ctrl),
onChanged: (value) => onChanged(ctrl),
activeColor: AppColor.primaryColor,
);
},
);
}
}

View File

@@ -1,112 +1,112 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
// 1. إنشاء Class لتمثيل بيانات كل سؤال وجواب
class FaqItem {
final String question;
final Widget answer; // استخدام Widget يسمح بوضع نصوص أو صور
final IconData icon;
FaqItem({required this.question, required this.answer, required this.icon});
}
class UsingAppPage extends StatelessWidget {
const UsingAppPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: "How to use Intaleq".tr,
body: [
SizedBox(
child: Padding(
// 2. تجهيز قائمة البيانات بشكل منظم
final List<FaqItem> faqItems = [
FaqItem(
question: "What are the order details we provide to you?".tr,
icon: Icons.receipt_long_outlined,
answer: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: [
InkWell(
onTap: () {
MyDialogContent().getDialog(
"What are the order details we provide to you?".tr,
Image.network(
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg',
height: 300,
width: 300,
child: Image.network(
'https://api.tripz-egypt.com/tripz/imageForUsingApp/order_page.jpg',
fit: BoxFit.cover,
), () {
Get.back();
});
// يمكنك إضافة مؤشر تحميل هنا
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) {
return const Center(
child:
Icon(Icons.error_outline, color: Colors.red, size: 40));
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What are the order details we provide to you?".tr,
style: AppStyle.title,
),
),
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What are the order details we provide to you?".tr,
FaqItem(
question: "What is the feature of our wallet?".tr,
icon: Icons.account_balance_wallet_outlined,
answer: Text(
'''Intaleq Wallet Features:
Transfer money multiple times.
Transfer to anyone.
Make purchases.
Charge your account.
Charge a friend's Intaleq account.
Store your money with us and receive it in your bank as a monthly salary.'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is the feature of our wallet?".tr,
style: AppStyle.title,
- Transfer money multiple times.
- Transfer to anyone.
- Make purchases.
- Charge your account.
- Charge a friend's Intaleq account.
- Store your money with us and receive it in your bank as a monthly salary.'''
.tr,
style:
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
),
),
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What is Types of Trips in Intaleq?".tr,
FaqItem(
question: "What is Types of Trips in Intaleq?".tr,
icon: Icons.map_outlined,
answer: Text(
'''Types of Trips in Intaleq:
Comfort: For cars newer than 2017 with air conditioning.
Lady: For girl drivers.
Speed: For fixed salary and endpoints.
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
Raih Gai: For same-day return trips longer than 50km.
'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is Types of Trips in Intaleq?".tr,
style: AppStyle.title,
- Comfort: For cars newer than 2017 with air conditioning.
- Lady: For girl drivers.
- Speed: For fixed salary and endpoints.
- Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
- Raih Gai: For same-day return trips longer than 50km.'''
.tr,
style:
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
),
),
),
),
],
),
),
)
],
];
// 3. بناء الواجهة الرسومية باستخدام البيانات
return MyScafolld(
title: "How to use App".tr, // تم تغيير العنوان ليكون أعم
isleading: true,
body: [
ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
itemCount: faqItems.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemBuilder: (context, index) {
final item = faqItems[index];
return Card(
elevation: 2,
shadowColor: Colors.black.withOpacity(0.1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
clipBehavior: Clip.antiAlias,
child: ExpansionTile(
leading: Icon(item.icon, color: Theme.of(context).primaryColor),
title: Text(item.question,
style: const TextStyle(fontWeight: FontWeight.w600)),
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
expandedCrossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(height: 1),
const SizedBox(height: 12),
item.answer,
],
),
);
},
),
],
);
}
}

View File

@@ -13,6 +13,7 @@ import 'mapDriverWidgets/google_driver_map_page.dart';
import 'mapDriverWidgets/passenger_info_window.dart';
import 'mapDriverWidgets/sos_connect.dart';
// Changed: تم إعادة بناء الصفحة بالكامل لتكون أكثر تنظيمًا
class PassengerLocationMapPage extends StatelessWidget {
PassengerLocationMapPage({super.key});
final LocationController locationController = Get.put(LocationController());
@@ -21,36 +22,44 @@ class PassengerLocationMapPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// New: استخدام addPostFrameCallback لضمان أن تحميل البيانات يتم بعد بناء الواجهة
WidgetsBinding.instance.addPostFrameCallback((_) {
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول
// هذا يعطي GetX وقته لتجهيز كل شيء
WidgetsBinding.instance.addPostFrameCallback((_) {
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
mapDriverController.argumentLoading();
});
} else {
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ
WidgetsBinding.instance.addPostFrameCallback((_) {
Get.snackbar("Error", "No order data found.");
Get.back();
});
}
mapDriverController.argumentLoading();
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
} else {
// في حال عدم وجود arguments، يتم التعامل مع هذا الخطأ
Get.snackbar("Error", "No order data found.");
Get.back();
}
});
return Scaffold(
// backgroundColor: AppColor.blueColor,
// title: 'Map Passenger'.tr,
body: SafeArea(
child: Stack(
children: [
// 1. الخريطة في الخلفية
GoogleDriverMap(locationController: locationController),
const PassengerInfoWindow(),
// 2. شريط تعليمات الطريق في الأعلى
const InstructionsOfRoads(),
// 3. زر إلغاء الرحلة في الأعلى يسارًا
CancelWidget(mapDriverController: mapDriverController),
// 4. نافذة معلومات الراكب في الأسفل (تظهر قبل بدء الرحلة)
const PassengerInfoWindow(),
// 5. شريط معلومات وإنهاء الرحلة (يظهر بعد بدء الرحلة)
driverEndRideBar(),
// 6. أزرار الطوارئ والاتصال
const SosConnect(),
// 7. دائرة عرض السرعة
speedCircle(),
// const GoogleMapApp(),
// 8. نافذة عرض السعر النهائي (تظهر بعد انتهاء الرحلة)
const PricesWindow(),
],
),
@@ -58,6 +67,59 @@ class PassengerLocationMapPage extends StatelessWidget {
}
}
// New: تصميم جديد لشريط تعليمات الطريق في أعلى الشاشة
class InstructionsOfRoads extends StatelessWidget {
const InstructionsOfRoads({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (controller) =>
// يتم إظهار التعليمات فقط إذا كانت متوفرة
controller.currentInstruction.isNotEmpty
? Positioned(
bottom: 10,
left: MediaQuery.of(context).size.width * 0.15,
right: MediaQuery.of(context).size.width * 0.15,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.directions,
color: AppColor.primaryColor),
const SizedBox(width: 10),
Expanded(
child: Text(
controller.currentInstruction,
style: AppStyle.title.copyWith(fontSize: 16),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
)
: const SizedBox(), // في حالة عدم وجود تعليمات، لا يظهر شيء
);
}
}
// Changed: تم تعديل تصميم وموضع زر الإلغاء ليكون أيقونة بسيطة في الأعلى
class CancelWidget extends StatelessWidget {
const CancelWidget({
super.key,
@@ -70,8 +132,13 @@ class CancelWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Positioned(
top: 10,
left: 5,
child: GestureDetector(
left: 10,
child: GetBuilder<MapDriverController>(
builder: (controller) {
// يظهر زر الإلغاء فقط قبل انتهاء الرحلة
if (controller.isRideFinished) return const SizedBox.shrink();
return GestureDetector(
onTap: () {
Get.defaultDialog(
title: "Are you sure you want to cancel this trip?".tr,
@@ -93,36 +160,44 @@ class CancelWidget extends StatelessWidget {
title: 'Ok'.tr,
kolor: AppColor.redColor,
onPressed: () async {
// todo add cancel and inform passenger to get new driver
await mapDriverController
.cancelTripFromDriverAfterApplied();
Get.back();
}),
cancel: MyElevatedButton(
title: 'No'.tr,
// kolor: AppColor.redColor,
onPressed: () {
Get.back();
}));
},
child: Container(
decoration: BoxDecoration(
color: AppColor.redColor,
borderRadius: BorderRadius.circular(15)),
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 5,
),
],
),
child: const Padding(
padding: EdgeInsets.all(3),
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.clear,
size: 40,
color: AppColor.secondaryColor,
size: 30,
color: AppColor.redColor,
),
),
),
);
},
),
);
}
}
// Changed: تم تعديل تصميم نافذة السعر لتكون أكثر وضوحًا
class PricesWindow extends StatelessWidget {
const PricesWindow({
super.key,
@@ -132,26 +207,30 @@ class PricesWindow extends StatelessWidget {
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(builder: (mapDriverController) {
return mapDriverController.isPriceWindow
? Positioned(
bottom: Get.height * 1.2,
// top: Get.height * 3,
left: Get.height * 1,
right: Get.height * 1,
? Container(
color: Colors.black.withOpacity(0.5),
child: Center(
child: Container(
height: Get.height * 3,
decoration: AppStyle.boxDecoration1,
width: Get.width * 0.8,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(3),
child: Text(
Text(
'Total Price is '.tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
)),
const SizedBox(height: 10),
Text(
'${mapDriverController.totalPricePassenger} ${'\$'.tr}',
style: AppStyle.headTitle2.copyWith(
color: AppColor.primaryColor, fontSize: 36),
),
const SizedBox(
height: 20,
),
@@ -166,6 +245,7 @@ class PricesWindow extends StatelessWidget {
],
),
),
),
)
: const SizedBox();
});

View File

@@ -1,434 +1,170 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/functions/upload_image.dart';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/main.dart';
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
import 'package:sefer_driver/views/Rate/rate_app_page.dart';
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
import 'package:sefer_driver/views/auth/captin/invite_driver_screen.dart';
import 'package:sefer_driver/views/notification/available_rides_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/auth/captin/logout_captain.dart';
import 'package:sefer_driver/views/home/Captin/history/history_captain.dart';
import 'package:sefer_driver/views/home/Captin/home_captain/help_captain.dart';
import 'package:sefer_driver/views/home/Captin/About%20Us/settings_captain.dart';
import 'package:sefer_driver/views/home/Captin/About Us/settings_captain.dart';
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
import 'package:sefer_driver/views/notification/notification_captain.dart';
import '../../../../constant/colors.dart';
import '../../../../controller/functions/upload_image.dart';
import '../About Us/video_page.dart';
import '../assurance_health_page.dart';
import '../maintain_center_page.dart';
import 'package:flutter/cupertino.dart';
// class CupertinoDrawerCaptain extends StatelessWidget {
// final ImageController imageController = Get.put(ImageController());
// 1. إنشاء Class لتعريف بيانات كل عنصر في القائمة
class DrawerItem {
final String title;
final IconData icon;
final Color color;
final VoidCallback onTap;
// @override
// Widget build(BuildContext context) {
// return CupertinoPageScaffold(
// navigationBar: CupertinoNavigationBar(
// middle: Text('Menu'.tr),
// ),
// child: SafeArea(
// child: CustomScrollView(
// slivers: [
// const SliverToBoxAdapter(child: const UserAccountHeader()),
// SliverList(
// delegate: SliverChildListDelegate([
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.money_dollar,
// text: 'Wallet'.tr,
// onTap: () => Get.to(() => WalletCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.person,
// text: 'Profile'.tr,
// onTap: () => Get.to(() => ProfileCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.clock,
// text: 'History of Trip'.tr,
// onTap: () => Get.to(() => const HistoryCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.car_detailed,
// text: 'Available for rides'.tr,
// onTap: () => Get.to(() => const AvailableRidesPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.bell,
// text: 'Notifications'.tr,
// onTap: () => Get.to(() => const NotificationCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.question_circle,
// text: 'Helping Center'.tr,
// onTap: () => Get.to(() => HelpCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.share,
// text: 'Share App'.tr,
// onTap: () => Get.to(() => InviteScreen()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.wrench,
// text: "Maintenance Center".tr,
// onTap: () => Get.to(() => MaintainCenterPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon:
// CupertinoIcons.heart, // Updated icon to represent health
// text: "Health Insurance".tr, // Updated English text
// onTap: () => Get.to(() => AssuranceHealthPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.mail,
// text: "Contact Us".tr,
// onTap: () => Get.to(() => ContactUsPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon:
// Icons.play_circle_filled, // Icon representing video play
// text: 'Videos Tutorials'.tr,
// onTap: () => Get.to(() => VideoListPage()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: Icons.star, // Another option with a filled star icon
// text: "Rate Our App".tr,
// onTap: () => Get.to(() => RatingScreen()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.settings,
// text: 'Settings'.tr,
// onTap: () => Get.to(() => const SettingsCaptain()),
// ),
// _buildDivider(),
// _buildDrawerItem(
// icon: CupertinoIcons.square_arrow_right,
// text: 'Sign Out'.tr,
// onTap: () => Get.to(() => const LogoutCaptain()),
// ),
// _buildDivider(),
// ]),
// ),
// ],
// ),
// ),
// );
// }
DrawerItem(
{required this.title,
required this.icon,
required this.color,
required this.onTap});
}
// Widget _buildDivider() {
// return const Divider(
// thickness: 1,
// height: 1,
// color: CupertinoColors.systemGrey4,
// );
// }
// --- الويدجت الرئيسية للقائمة الجانبية ---
class AppDrawer extends StatelessWidget {
AppDrawer({super.key});
// Widget _buildDrawerItem({
// required IconData icon,
// required String text,
// required VoidCallback onTap,
// }) {
// return CupertinoButton(
// onPressed: onTap,
// child: Row(
// children: [
// Icon(icon, color: CupertinoColors.activeBlue),
// const SizedBox(width: 10),
// Text(text, style: const TextStyle(color: CupertinoColors.label)),
// const Spacer(),
// const Icon(CupertinoIcons.right_chevron,
// color: CupertinoColors.systemGrey),
// ],
// ),
// );
// }
// }
// class UserAccountHeader extends StatelessWidget {
// const UserAccountHeader({super.key});
// @override
// Widget build(BuildContext context) {
// return Container(
// padding: const EdgeInsets.all(16),
// decoration: const BoxDecoration(
// gradient: LinearGradient(
// colors: [AppColor.blueColor, AppColor.twitterColor],
// begin: Alignment.topLeft,
// end: Alignment.bottomRight,
// ),
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// GetBuilder<ImageController>(
// builder: (imageController) {
// return Stack(
// children: [
// imageController.isloading
// ? const CupertinoActivityIndicator()
// : Container(
// width: 100,
// height: 100,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// image: DecorationImage(
// fit: BoxFit.cover,
// image: NetworkImage(
// '${AppLink.seferCairoServer}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg',
// ),
// ),
// ),
// ),
// Positioned(
// right: 0,
// top: 0,
// child: CupertinoButton(
// onPressed: () {
// imageController.choosImagePicture(
// AppLink.uploadImage1, 'portrait');
// },
// child: const Icon(CupertinoIcons.pencil_circle_fill,
// color: CupertinoColors.white),
// ),
// ),
// ],
// );
// },
// ),
// const SizedBox(height: 10),
// Text(
// '${box.read(BoxName.nameDriver).toString().split(' ')[0]} ${box.read(BoxName.nameDriver).toString().split(' ')[1]}',
// style: const TextStyle(
// color: CupertinoColors.white,
// fontSize: 18,
// fontWeight: FontWeight.bold),
// ),
// const SizedBox(height: 5),
// Text(
// box.read(BoxName.emailDriver),
// style: const TextStyle(color: CupertinoColors.white, fontSize: 14),
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Text(
// Get.find<HomeCaptainController>().rating.toString(),
// style: const TextStyle(
// color: CupertinoColors.systemYellow, fontSize: 16),
// ),
// const SizedBox(width: 5),
// // You might want to replace this with a Cupertino-style rating widget
// // For now, we'll use text to represent stars
// // const Text('★★★★★',
// // style: TextStyle(color: CupertinoColors.systemYellow)),
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
// color: AppColor.greenColor,
// child: RatingBar.builder(
// initialRating: double.parse(
// Get.find<HomeCaptainController>().rating.toString()),
// minRating: 1,
// direction: Axis.horizontal,
// itemCount: 5,
// itemSize: 20,
// itemPadding: const EdgeInsets.symmetric(horizontal: 2),
// itemBuilder: (context, _) => const Icon(
// Icons.star,
// color: Colors.amber,
// ),
// onRatingUpdate: (rating) {},
// ),
// ),
// ],
// ),
// ],
// ),
// );
// }
// }
class CupertinoDrawerCaptain extends StatelessWidget {
final ImageController imageController = Get.put(ImageController());
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
backgroundColor: CupertinoColors.systemGroupedBackground,
navigationBar: CupertinoNavigationBar(
middle: Text('Menu'.tr,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
backgroundColor: Colors.transparent,
border: null,
),
child: SafeArea(
child: CustomScrollView(
slivers: [
const SliverToBoxAdapter(child: UserAccountHeader()),
SliverList(
delegate: SliverChildListDelegate([
const SizedBox(height: 10),
_buildSectionHeader('Account'.tr),
_buildDrawerItem(
icon: CupertinoIcons.money_dollar_circle_fill,
text: 'Wallet'.tr,
onTap: () => Get.to(() => WalletCaptainRefactored()),
),
_buildDrawerItem(
icon: CupertinoIcons.person_fill,
text: 'Profile'.tr,
onTap: () => Get.to(() => ProfileCaptain()),
),
_buildSectionHeader('Activities'.tr),
_buildDrawerItem(
icon: CupertinoIcons.clock_fill,
text: 'History of Trip'.tr,
onTap: () => Get.to(() => const HistoryCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.car_fill,
text: 'Available for rides'.tr,
onTap: () => Get.to(() => const AvailableRidesPage()),
),
_buildSectionHeader('Support'.tr),
_buildDrawerItem(
icon: CupertinoIcons.bell_fill,
text: 'Notifications'.tr,
onTap: () => Get.to(() => const NotificationCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.question_circle_fill,
text: 'Helping Center'.tr,
onTap: () => Get.to(() => HelpCaptain()),
),
_buildSectionHeader('More'.tr),
...moreMenuItems(),
]),
),
],
),
),
);
}
List<Widget> moreMenuItems() => [
_buildDrawerItem(
icon: CupertinoIcons.share_up,
text: 'Share App'.tr,
onTap: () => Get.to(() => InviteScreen()),
),
_buildDrawerItem(
icon: CupertinoIcons.wrench_fill,
text: "Maintenance Center".tr,
onTap: () => Get.to(() => MaintainCenterPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.heart_fill,
text: "Health Insurance".tr,
onTap: () => Get.to(() => AssuranceHealthPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.mail_solid,
text: "Contact Us".tr,
onTap: () => Get.to(() => ContactUsPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.play_circle_fill,
text: 'Videos Tutorials'.tr,
onTap: () => Get.to(() => VideoListPage()),
),
_buildDrawerItem(
icon: CupertinoIcons.star_fill,
text: "Rate Our App".tr,
onTap: () => Get.to(() => RatingScreen()),
),
_buildDrawerItem(
icon: CupertinoIcons.settings_solid,
text: 'Settings'.tr,
onTap: () => Get.to(() => const SettingsCaptain()),
),
_buildDrawerItem(
icon: CupertinoIcons.square_arrow_right,
text: 'Sign Out'.tr,
onTap: () => Get.to(() => const LogoutCaptain()),
isDestructive: true,
),
// 2. تعريف بيانات القائمة بشكل مركزي ومنظم
final List<DrawerItem> drawerItems = [
DrawerItem(
title: 'Wallet'.tr,
icon: Icons.account_balance_wallet,
color: Colors.green,
onTap: () => Get.to(() => WalletCaptainRefactored())),
DrawerItem(
title: 'Profile'.tr,
icon: Icons.person,
color: Colors.blue,
onTap: () => Get.to(() => ProfileCaptain())),
DrawerItem(
title: 'History of Trip'.tr,
icon: Icons.history,
color: Colors.orange,
onTap: () => Get.to(() => const HistoryCaptain())),
DrawerItem(
title: 'Available for rides'.tr,
icon: Icons.drive_eta,
color: Colors.teal,
onTap: () => Get.to(() => const AvailableRidesPage())),
DrawerItem(
title: 'Notifications'.tr,
icon: Icons.notifications,
color: Colors.purple,
onTap: () => Get.to(() => const NotificationCaptain())),
DrawerItem(
title: 'Helping Center'.tr,
icon: Icons.help_center,
color: Colors.cyan,
onTap: () => Get.to(() => HelpCaptain())),
DrawerItem(
title: 'Share App'.tr,
icon: Icons.share,
color: Colors.indigo,
onTap: () => Get.to(() => InviteScreen())),
DrawerItem(
title: 'Maintenance Center'.tr,
icon: Icons.build,
color: Colors.brown,
onTap: () => Get.to(() => MaintainCenterPage())),
DrawerItem(
title: 'Health Insurance'.tr,
icon: Icons.favorite,
color: Colors.pink,
onTap: () => Get.to(() => AssuranceHealthPage())),
DrawerItem(
title: 'Contact Us'.tr,
icon: Icons.email,
color: Colors.blueGrey,
onTap: () => Get.to(() => ContactUsPage())),
DrawerItem(
title: 'Videos Tutorials'.tr,
icon: Icons.video_library,
color: Colors.redAccent,
onTap: () => Get.to(() => VideoListPage())),
DrawerItem(
title: 'Rate Our App'.tr,
icon: Icons.star,
color: Colors.amber,
onTap: () => Get.to(() => RatingScreen())),
DrawerItem(
title: 'Settings'.tr,
icon: Icons.settings,
color: Colors.grey.shade600,
onTap: () => Get.to(() => const SettingsCaptain())),
];
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: const TextStyle(
color: CupertinoColors.systemGrey,
fontSize: 13,
fontWeight: FontWeight.w600,
@override
Widget build(BuildContext context) {
return Drawer(
child: Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
children: [
// --- الجزء العلوي من القائمة (بيانات المستخدم) ---
UserHeader(), // استخدمنا الويدجت المحسنة بالأسفل
// --- قائمة العناصر المتحركة ---
Expanded(
child: AnimationLimiter(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: drawerItems.length + 1, // +1 لزر تسجيل الخروج
itemBuilder: (BuildContext context, int index) {
// --- زر تسجيل الخروج في النهاية ---
if (index == drawerItems.length) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _DrawerItemTile(
item: DrawerItem(
title: 'Sign Out'.tr,
icon: Icons.logout,
color: Colors.red,
onTap: () =>
Get.to(() => const LogoutCaptain())),
),
),
),
);
}
Widget _buildDrawerItem({
required IconData icon,
required String text,
required VoidCallback onTap,
bool isDestructive = false,
}) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: CupertinoColors.white,
borderRadius: BorderRadius.circular(10),
// --- بقية العناصر ---
final item = drawerItems[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _DrawerItemTile(item: item),
),
child: CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onPressed: onTap,
child: Row(
children: [
Icon(
icon,
color: isDestructive
? CupertinoColors.destructiveRed
: CupertinoColors.activeBlue,
size: 22,
),
const SizedBox(width: 12),
Text(
text,
style: AppStyle.title,
// TextStyle(
// color: isDestructive
// ? CupertinoColors.destructiveRed
// : CupertinoColors.label,
// fontSize: 16,
// ),
);
},
),
),
const Spacer(),
Icon(
CupertinoIcons.chevron_right,
color: CupertinoColors.systemGrey3,
size: 18,
),
],
),
@@ -437,152 +173,123 @@ class CupertinoDrawerCaptain extends StatelessWidget {
}
}
class UserAccountHeader extends StatelessWidget {
const UserAccountHeader({super.key});
// --- ويدجت خاصة بكل عنصر في القائمة ---
class _DrawerItemTile extends StatelessWidget {
final DrawerItem item;
const _DrawerItemTile({required this.item});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColor.blueColor, Color(0xFF2196F3)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
color: item.color.withOpacity(0.1),
shape: BoxShape.circle,
),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 10,
spreadRadius: 2,
child: Icon(item.icon, color: item.color, size: 24),
),
],
title: Text(
item.title,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.w500),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GetBuilder<ImageController>(
builder: (imageController) {
return Stack(
onTap: () {
Get.back(); // لإغلاق القائمة عند الضغط
Future.delayed(const Duration(milliseconds: 250), () {
item.onTap(); // الانتقال للصفحة بعد تأخير بسيط لإظهار الأنيميشن
});
},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
splashColor: item.color.withOpacity(0.2),
),
);
}
}
// --- ويدجت محسنة للجزء العلوي من القائمة ---
class UserHeader extends StatelessWidget {
UserHeader({super.key});
final ImageController imageController = Get.find<ImageController>();
final HomeCaptainController homeCaptainController =
Get.find<HomeCaptainController>();
@override
Widget build(BuildContext context) {
return UserAccountsDrawerHeader(
accountName: Text(
box.read(BoxName.nameDriver).toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
shadows: [Shadow(blurRadius: 2, color: Colors.black26)]),
),
accountEmail: Text(box.read(BoxName.emailDriver)),
currentAccountPicture: GetBuilder<ImageController>(
builder: (controller) => Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
spreadRadius: 2,
),
BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 5)
],
),
child: imageController.isloading
? const CupertinoActivityIndicator()
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
'${AppLink.seferCairoServer}/portrate_captain_image/${(box.read(BoxName.driverID))}.jpg',
fit: BoxFit.cover,
),
child: controller.isloading
? const CircularProgressIndicator(color: Colors.white)
: CircleAvatar(
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg'),
),
),
Positioned(
right: 0,
bottom: 0,
child: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () {
imageController.choosImagePicture(
AppLink.uploadImagePortrate, 'portrait');
},
bottom: -5,
right: -5,
child: InkWell(
onTap: () => controller.choosImagePicture(
AppLink.uploadImagePortrate, 'portrait'),
child: Container(
padding: const EdgeInsets.all(6),
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Icon(
CupertinoIcons.camera_fill,
color: AppColor.blueColor,
size: 20,
),
child: Icon(Icons.camera_alt,
color: Theme.of(context).primaryColor, size: 18),
),
),
),
],
);
},
),
const SizedBox(height: 16),
Text(
box.read(BoxName.nameDriver).toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
color: Colors.black26,
blurRadius: 2,
offset: Offset(0, 1),
),
],
),
),
const SizedBox(height: 4),
Text(
box.read(BoxName.emailDriver),
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
),
child: Row(
otherAccountsPictures: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
// const Icon(
// CupertinoIcons.star_fill,
// color: CupertinoColors.systemYellow,
// size: 18,
// ),
// const SizedBox(width: 4),
Text(
Get.find<HomeCaptainController>().rating.toString(),
homeCaptainController.rating.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 8),
RatingBar.builder(
initialRating: double.parse(
Get.find<HomeCaptainController>().rating.toString()),
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 16,
ignoreGestures: true,
itemBuilder: (context, _) => const Icon(
CupertinoIcons.star_fill,
color: CupertinoColors.systemYellow,
),
onRatingUpdate: (rating) {},
fontWeight: FontWeight.bold,
fontSize: 16),
),
const SizedBox(width: 4),
const Icon(Icons.star, color: Colors.amber, size: 20),
],
),
),
],
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Theme.of(context).primaryColor, Colors.blue.shade700],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
);
}

View File

@@ -1,12 +1,12 @@
import 'dart:io';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:sefer_driver/views/notification/available_rides_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:sefer_driver/views/home/Captin/home_captain/drawer_captain.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
@@ -26,94 +26,18 @@ import 'widget/connect.dart';
import 'widget/left_menu_map_captain.dart';
import '../../../../main.dart';
// =================================================================
// STEP 1: Modify your LocationController
// =================================================================
/*
In your `location_controller.dart` file, change `myLocation` and `heading`
to be observable by adding `.obs`. This will allow other parts of your app,
like the map, to automatically react to changes.
// BEFORE:
// LatLng myLocation = LatLng(....);
// double heading = 0.0;
// AFTER:
final myLocation = const LatLng(30.0444, 31.2357).obs; // Default to Cairo or a sensible default
final heading = 0.0.obs;
// When you update these values elsewhere in your controller,
// make sure to update their `.value` property.
// e.g., myLocation.value = newLatLng;
// e.g., heading.value = newHeading;
*/
// =================================================================
// STEP 2: Modify your HomeCaptainController
// =================================================================
/*
In your `home_captain_controller.dart` file, you need to add logic to
listen for changes from the LocationController and animate the camera.
class HomeCaptainController extends GetxController {
// ... your existing variables (mapController, carIcon, etc.)
// Make sure you have a reference to the GoogleMapController
GoogleMapController? mapHomeCaptainController;
@override
void onInit() {
super.onInit();
_setupLocationListener();
}
void onMapCreated(GoogleMapController controller) {
mapHomeCaptainController = controller;
// Any other map setup logic
}
// THIS IS THE NEW LOGIC TO ADD
void _setupLocationListener() {
final locationController = Get.find<LocationController>();
// The 'ever' worker from GetX listens for changes to an observable variable.
// Whenever `heading` or `myLocation` changes, it will call our method.
ever(locationController.heading, (_) => _updateCameraPosition());
ever(locationController.myLocation, (_) => _updateCameraPosition());
}
void _updateCameraPosition() {
final locationController = Get.find<LocationController>();
if (mapHomeCaptainController != null) {
final newPosition = CameraPosition(
target: locationController.myLocation.value,
zoom: 17.5, // A bit closer for a navigation feel
tilt: 50.0, // A nice 3D perspective
bearing: locationController.heading.value, // This rotates the map
);
// Animate the camera smoothly to the new position and rotation
mapHomeCaptainController!.animateCamera(
CameraUpdate.newCameraPosition(newPosition),
);
}
}
// ... rest of your controller code
}
*/
// =================================================================
// STEP 3: Update the HomeCaptain Widget
// =================================================================
// الويدجت الرئيسية للصفحة بعد تنظيمها
class HomeCaptain extends StatelessWidget {
HomeCaptain({super.key});
// تم الإبقاء على تعريف الـ Controllers كما هو في الكود الأصلي
final LocationController locationController = Get.put(LocationController());
final HomeCaptainController homeCaptainController =
Get.put(HomeCaptainController());
@override
Widget build(BuildContext context) {
// لم يتم تغيير أي شيء في هذه الأوامر
Get.put(HomeCaptainController());
WidgetsBinding.instance.addPostFrameCallback((_) async {
closeOverlayIfFound();
@@ -121,8 +45,40 @@ class HomeCaptain extends StatelessWidget {
getPermissionOverlay();
showDriverGiftClaim(context);
});
// التصميم الجديد: أصبح الـ build الرئيسي نظيفاً وواضحاً جداً
return Scaffold(
appBar: AppBar(
appBar: const _HomeAppBar(), // 1. تم فصل الـ AppBar في ويدجت خاصة
drawer: AppDrawer(),
body: Stack(
children: [
// كل جزء من الواجهة أصبح ويدجت منفصلة
const _MapView(), // 2. تم فصل الخريطة
const _DriverStatsOverlay(), // 3. تم فصل كارت الإحصائيات العلوي
const _DriverDurationOverlay(), // 4. تم فصل كارت مدة العمل
const _FloatingActionButtons(), // 5. تم فصل الأزرار الجانبية العائمة
const _ConnectButtonOverlay(), // 6. تم فصل زر الاتصال السفلي
leftMainMenuCaptainIcons(), // هذه بقيت كما هي
],
),
);
}
}
// ==================================================================
// الأجزاء الصغيرة التي تم فصلها (Helper Widgets)
// هذه الويدجتس تحتوي على نفس كود التصميم الأصلي الخاص بك تماماً، ولكنها منظمة بشكل أفضل
// ==================================================================
/// 1. ويدجت الـ AppBar
class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
const _HomeAppBar();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي للـ AppBar
final homeCaptainController = Get.find<HomeCaptainController>();
return AppBar(
elevation: 2,
flexibleSpace: Container(
decoration: BoxDecoration(
@@ -197,9 +153,7 @@ class HomeCaptain extends StatelessWidget {
icon: Icons.my_location, // Changed for clarity
tooltip: 'Center on Me'.tr,
onPressed: () {
// This button now just re-centers without changing rotation
if (homeCaptainController.mapHomeCaptainController !=
null) {
if (homeCaptainController.mapHomeCaptainController != null) {
homeCaptainController.mapHomeCaptainController!
.animateCamera(CameraUpdate.newLatLngZoom(
Get.find<LocationController>().myLocation,
@@ -213,64 +167,72 @@ class HomeCaptain extends StatelessWidget {
),
const SizedBox(width: 8),
],
),
drawer: CupertinoDrawerCaptain(),
body: Stack(
children: [
// FIX: Replaced nested GetBuilder/Obx with a single GetX widget.
// GetX handles both observable (.obs) variables and standard controller updates.
GetBuilder<HomeCaptainController>(builder: (controller) {
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
/// 2. ويدجت الخريطة
class _MapView extends StatelessWidget {
const _MapView();
@override
Widget build(BuildContext context) {
final locationController = Get.find<LocationController>();
// نفس الكود الأصلي للخريطة
return GetBuilder<HomeCaptainController>(builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: GoogleMap(
padding: EdgeInsets.only(bottom: 50, top: 300),
fortyFiveDegreeImageryEnabled: true,
onMapCreated: controller.onMapCreated,
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
initialCameraPosition: CameraPosition(
// Use .value to get the latest location from the reactive variable
target: locationController.myLocation,
zoom: 15,
),
onCameraMove: (position) {
CameraPosition(
target: locationController.myLocation,
zoom: 17.5, // A bit closer for a navigation feel
tilt: 50.0, // A nice 3D perspective
bearing:
locationController.heading, // This rotates the map
zoom: 17.5,
tilt: 50.0,
bearing: locationController.heading,
);
},
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
// Use .value for position and rotation from the reactive variable
position: locationController.myLocation,
rotation: locationController.heading,
// IMPORTANT: These two properties make the marker look
// correct when the map is tilted and rotating.
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
)
},
mapType: controller.mapType
? MapType.satellite
: MapType.terrain,
myLocationButtonEnabled: false, // Disable default button
myLocationEnabled: false, // We use our custom marker
mapType: controller.mapType ? MapType.satellite : MapType.terrain,
myLocationButtonEnabled: false,
myLocationEnabled: false,
trafficEnabled: controller.mapTrafficON,
buildingsEnabled: true,
mapToolbarEnabled: true,
zoomControlsEnabled: false, // Cleaner UI for navigation
compassEnabled: true,
zoomControlsEnabled: false,
);
}),
});
}
}
// The rest of your UI remains the same...
Positioned(
bottom: 10,
right: Get.width * .1,
left: Get.width * .1,
child: const ConnectWidget()),
Positioned(
/// 3. ويدجت كارت الإحصائيات العلوي
class _DriverStatsOverlay extends StatelessWidget {
const _DriverStatsOverlay();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لكارت الإحصائيات
return Positioned(
top: 5,
right: Get.width * .05,
left: Get.width * .05,
@@ -293,8 +255,7 @@ class HomeCaptain extends StatelessWidget {
),
],
),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
width: Get.width * .8,
height: 120,
child: Column(
@@ -368,8 +329,8 @@ class HomeCaptain extends StatelessWidget {
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: int.parse((homeCaptainController
.countRideToday)) <
color: int.parse(
(homeCaptainController.countRideToday)) <
5
? AppColor.accentColor
: int.parse((homeCaptainController
@@ -401,13 +362,22 @@ class HomeCaptain extends StatelessWidget {
),
],
),
],
),
]),
);
},
),
),
Positioned(
);
}
}
/// 4. ويدجت كارت مدة العمل
class _DriverDurationOverlay extends StatelessWidget {
const _DriverDurationOverlay();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لكارت المدة
return Positioned(
bottom: 65,
right: Get.width * .1,
left: Get.width * .1,
@@ -425,15 +395,13 @@ class HomeCaptain extends StatelessWidget {
),
],
),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.timer_outlined,
color: AppColor.greenColor),
const Icon(Icons.timer_outlined, color: AppColor.greenColor),
const SizedBox(width: 8),
Text(
'Active Duration:'.tr,
@@ -453,8 +421,7 @@ class HomeCaptain extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.access_time,
color: AppColor.accentColor),
const Icon(Icons.access_time, color: AppColor.accentColor),
const SizedBox(width: 8),
Text(
'Total Connection Duration:'.tr,
@@ -474,11 +441,23 @@ class HomeCaptain extends StatelessWidget {
),
),
),
),
Positioned(
);
}
}
/// 5. ويدجت الأزرار الجانبية العائمة
class _FloatingActionButtons extends StatelessWidget {
const _FloatingActionButtons();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي للأزرار
return Positioned(
bottom: Get.height * .2,
right: 6,
child: Column(
child:
GetBuilder<HomeCaptainController>(builder: (homeCaptainController) {
return Column(
children: [
Platform.isAndroid
? AnimatedContainer(
@@ -525,9 +504,7 @@ class HomeCaptain extends StatelessWidget {
const SizedBox(
height: 5,
),
GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) {
return box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Begin'
? Positioned(
bottom: Get.height * .2,
@@ -548,18 +525,16 @@ class HomeCaptain extends StatelessWidget {
onPressed: () {
box.read(BoxName.rideStatus) == 'Applied'
? {
Get.to(
() => PassengerLocationMapPage(),
arguments: box
.read(BoxName.rideArguments)),
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.changeRideToBeginToPassenger()
}
: {
Get.to(
() => PassengerLocationMapPage(),
arguments: box
.read(BoxName.rideArguments)),
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.startRideFromStartApp()
};
@@ -573,148 +548,30 @@ class HomeCaptain extends StatelessWidget {
),
),
)
: const SizedBox();
})
: const SizedBox()
],
),
),
leftMainMenuCaptainIcons(),
],
),
);
}),
);
}
}
// These helper widgets and functions remain unchanged
showFirstTimeOfferNotification(BuildContext context) async {
bool isFirstTime = _checkIfFirstTime();
/// 6. ويدجت زر الاتصال السفلي
class _ConnectButtonOverlay extends StatelessWidget {
const _ConnectButtonOverlay();
if (isFirstTime) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: Offset(0.0, 10.0),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Welcome Offer!'.tr,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 15),
Text(
'As a new driver, you\'re eligible for a special offer!'.tr,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
Stack(
children: <Widget>[
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(15),
),
child: Text(
'300 LE'.tr,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
right: -10,
top: -10,
child: Container(
padding: const EdgeInsets.all(5),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Icon(
Icons.attach_money,
color: Colors.white,
size: 20,
),
),
),
],
),
const SizedBox(height: 20),
Text(
'for your first registration!'.tr,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Text(
"Get it Now!".tr,
style:
const TextStyle(fontSize: 18, color: Colors.white),
),
),
onPressed: () {
_markAsNotFirstTime();
Navigator.of(context).pop();
},
),
],
),
),
);
},
);
});
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لزر الاتصال
return Positioned(
bottom: 10,
right: Get.width * .1,
left: Get.width * .1,
child: const ConnectWidget());
}
}
bool _checkIfFirstTime() {
if (box.read(BoxName.isFirstTime).toString() == '') {
return true;
} else {
return false;
}
}
void _markAsNotFirstTime() {
box.write(BoxName.isFirstTime, 'false');
}
// هذه الويدجت المساعدة بقيت كما هي
class _MapControlButton extends StatelessWidget {
final IconData icon;
final VoidCallback onPressed;
@@ -748,3 +605,8 @@ class _MapControlButton extends StatelessWidget {
);
}
}
// الدوال المساعدة الأخرى تبقى كما هي في ملفك...
// showFirstTimeOfferNotification(BuildContext context) async { ... }
// bool _checkIfFirstTime() { ... }
// void _markAsNotFirstTime() { ... }

View File

@@ -26,7 +26,7 @@ class ConnectWidget extends StatelessWidget {
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) => double.parse(
(captainWalletController.totalPoints)) <
-300
-30000
? CupertinoButton(
onPressed: () {
Get.defaultDialog(
@@ -34,7 +34,7 @@ class ConnectWidget extends StatelessWidget {
barrierDismissible: false,
title: double.parse(
(captainWalletController.totalPoints)) <
-300
-30000
? 'You dont have Points'.tr
: 'You Are Stopped For this Day !'.tr,
titleStyle: AppStyle.title,
@@ -44,7 +44,7 @@ class ConnectWidget extends StatelessWidget {
onPressed: () async {
double.parse((captainWalletController
.totalPoints)) <
-300
-30000
? await Get.find<TextToSpeechController>()
.speakText(
'You must be recharge your Account'
@@ -59,7 +59,7 @@ class ConnectWidget extends StatelessWidget {
Text(
double.parse((captainWalletController
.totalPoints)) <
-300
-30000
? 'You must be recharge your Account'.tr
: 'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
.tr,
@@ -69,7 +69,7 @@ class ConnectWidget extends StatelessWidget {
),
confirm: double.parse(
(captainWalletController.totalPoints)) <
-300
-30000
? MyElevatedButton(
title: 'Recharge my Account'.tr,
onPressed: () {

View File

@@ -79,6 +79,9 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
border: Border.all(color: AppColor.blueColor),
borderRadius: BorderRadius.circular(15)),
child: IconButton(
onLongPress: () {
box.write(BoxName.statusDriverLocation, 'off');
},
onPressed: () {
// NotificationController1()
// .showNotification('Sefer Driver'.tr, ''.tr, '', '');

View File

@@ -1,101 +1,376 @@
import 'dart:io';
// import 'dart:io';
// import 'package:flutter/material.dart';
// import 'package:flutter/services.dart';
// import 'package:get/get.dart';
// import 'package:slide_to_act/slide_to_act.dart';
// import 'package:vibration/vibration.dart';
// import '../../../../constant/colors.dart';
// import '../../../../constant/style.dart';
// import '../../../../controller/home/captin/map_driver_controller.dart';
// import '../../../widgets/elevated_btn.dart';
// GetBuilder<MapDriverController> driverEndRideBar() {
// return GetBuilder<MapDriverController>(
// builder: (mapDriverController) => mapDriverController.isRideStarted
// ? Positioned(
// left: 5,
// top: 5,
// right: 5,
// child: Container(
// decoration: AppStyle.boxDecoration1.copyWith(
// borderRadius: BorderRadius.circular(15),
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.1),
// blurRadius: 10,
// offset: Offset(0, 5),
// ),
// ],
// ),
// padding: const EdgeInsets.all(10),
// height: mapDriverController.remainingTimeTimerRideBegin < 60
// ? mapDriverController.driverEndPage = 190
// : mapDriverController.carType == 'Mishwar Vip'
// ? 120
// : 170,
// child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// if (mapDriverController.carType != 'Mishwar Vip')
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// _buildInfoColumn(
// icon: Icons.social_distance,
// text: '${mapDriverController.distance} ${'KM'.tr}',
// ),
// _buildInfoColumn(
// icon: Icons.timelapse,
// text: mapDriverController.hours > 1
// ? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
// : '${mapDriverController.minutes} ${'m'.tr}',
// ),
// _buildInfoColumn(
// icon: Icons.money_sharp,
// text:
// '${mapDriverController.paymentAmount} ${'\$'.tr}',
// ),
// ],
// ),
// if (mapDriverController.carType != 'Speed' &&
// mapDriverController.carType != 'Awfar Car' &&
// mapDriverController.carType != 'Scooter')
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// _buildInfoBox(
// icon: Icons.timer,
// text:
// mapDriverController.stringRemainingTimeRideBegin1,
// ),
// _buildInfoBox(
// icon: Icons.location_on,
// text:
// '${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
// ),
// _buildInfoBox(
// icon: Icons.attach_money,
// text: mapDriverController.price.toStringAsFixed(2),
// ),
// ],
// ),
// _builtTimerAndCarType(),
// Container(
// width: Get.width * 0.8,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(15),
// boxShadow: [
// BoxShadow(
// color: AppColor.redColor.withOpacity(0.3),
// blurRadius: 8,
// offset: Offset(0, 4),
// ),
// ],
// ),
// child: SlideAction(
// height: 50,
// borderRadius: 15,
// elevation: 4,
// text: 'Slide to End Trip'.tr,
// textStyle: AppStyle.title.copyWith(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// color: Colors.white,
// ),
// outerColor: AppColor.redColor,
// innerColor: Colors.white,
// sliderButtonIcon: const Icon(
// Icons.arrow_forward_ios,
// color: AppColor.redColor,
// size: 24,
// ),
// sliderRotate: false,
// onSubmit: () {
// HapticFeedback.mediumImpact();
// mapDriverController.finishRideFromDriver();
// },
// ),
// )
// ],
// ),
// ),
// )
// : const SizedBox(),
// );
// }
// class _builtTimerAndCarType extends StatelessWidget {
// const _builtTimerAndCarType({
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// final mapDriverController = Get.find<MapDriverController>();
// return Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// Container(
// decoration: AppStyle.boxDecoration1.copyWith(
// boxShadow: [
// BoxShadow(
// color: AppColor.accentColor.withOpacity(0.2),
// blurRadius: 8,
// offset: Offset(0, 4),
// ),
// ],
// ),
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
// child: Text(
// mapDriverController.carType,
// style: AppStyle.title,
// ),
// ),
// if (mapDriverController.carType != 'Comfort' &&
// mapDriverController.carType != 'Mishwar Vip' &&
// mapDriverController.carType != 'Lady')
// Container(
// width: Get.width * 0.6,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(12),
// gradient: LinearGradient(
// colors: [
// mapDriverController.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor.withOpacity(0.8)
// : AppColor.greenColor.withOpacity(0.8),
// mapDriverController.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor
// : AppColor.greenColor,
// ],
// begin: Alignment.centerLeft,
// end: Alignment.centerRight,
// ),
// boxShadow: [
// BoxShadow(
// color: (mapDriverController.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor
// : AppColor.greenColor)
// .withOpacity(0.3),
// blurRadius: 8,
// offset: Offset(0, 4),
// ),
// ],
// ),
// child: ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: Stack(
// children: [
// LinearProgressIndicator(
// backgroundColor: Colors.white.withOpacity(0.2),
// valueColor: AlwaysStoppedAnimation<Color>(
// Colors.white.withOpacity(0.5),
// ),
// minHeight: 40,
// value:
// mapDriverController.progressTimerRideBegin.toDouble(),
// ),
// Center(
// child: AnimatedDefaultTextStyle(
// duration: Duration(milliseconds: 300),
// style: AppStyle.title.copyWith(
// color: Colors.white,
// fontWeight: FontWeight.bold,
// fontSize:
// mapDriverController.remainingTimeTimerRideBegin < 60
// ? 18
// : 16,
// shadows: [
// Shadow(
// color: Colors.black26,
// offset: Offset(0, 2),
// blurRadius: 4,
// ),
// ],
// ),
// child: Text(
// mapDriverController.stringRemainingTimeRideBegin,
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// );
// }
// }
// Widget _buildInfoColumn({required IconData icon, required String text}) {
// return Column(
// children: [
// Icon(icon),
// Text(
// text,
// style: AppStyle.title,
// ),
// ],
// );
// }
// Widget _buildInfoBox({required IconData icon, required String text}) {
// return Container(
// width: Get.width * .2,
// decoration: AppStyle.boxDecoration1,
// padding: const EdgeInsets.all(4),
// child: Row(
// children: [
// Icon(icon),
// SizedBox(width: 4),
// Text(
// text,
// style: AppStyle.number,
// ),
// ],
// ),
// );
// }
// GetBuilder<MapDriverController> speedCircle() {
// if (Get.find<MapDriverController>().speed > 100) {
// if (Platform.isIOS) {
// HapticFeedback.selectionClick();
// } else {
// Vibration.vibrate(duration: 1000);
// }
// Get.defaultDialog(
// barrierDismissible: false,
// titleStyle: AppStyle.title,
// title: 'Speed Over'.tr,
// middleText: 'Please slow down'.tr,
// middleTextStyle: AppStyle.title,
// confirm: MyElevatedButton(
// title: 'I will slow down'.tr,
// onPressed: () => Get.back(),
// ),
// );
// }
// return GetBuilder<MapDriverController>(
// builder: (mapDriverController) {
// return mapDriverController.isRideStarted
// ? Positioned(
// bottom: 25,
// right: 100,
// child: Container(
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: mapDriverController.speed > 100
// ? Colors.red
// : AppColor.secondaryColor,
// border: Border.all(width: 3, color: AppColor.redColor),
// ),
// height: 60,
// width: 60,
// child: Center(
// child: Text(
// mapDriverController.speed.toStringAsFixed(0),
// style: AppStyle.number,
// ),
// ),
// ),
// )
// : const SizedBox();
// },
// );
// }
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:slide_to_act/slide_to_act.dart';
import 'package:vibration/vibration.dart';
import 'dart:io';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../widgets/elevated_btn.dart';
// Changed: إعادة تصميم كاملة للشريط ليصبح شريطًا علويًا عند بدء الرحلة
GetBuilder<MapDriverController> driverEndRideBar() {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 5,
top: 5,
right: 5,
child: Container(
decoration: AppStyle.boxDecoration1.copyWith(
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: Offset(0, 5),
builder: (controller) => AnimatedPositioned(
duration: const Duration(milliseconds: 300),
// New: يظهر الشريط من الأعلى عندما تبدأ الرحلة
top: controller.isRideStarted ? 0 : -200,
left: 0,
right: 0,
child: Card(
margin: EdgeInsets.zero,
elevation: 10,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)),
),
],
),
padding: const EdgeInsets.all(10),
height: mapDriverController.remainingTimeTimerRideBegin < 60
? mapDriverController.driverEndPage = 190
: mapDriverController.carType == 'Mishwar Vip'
? 120
: 170,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
if (mapDriverController.carType != 'Mishwar Vip')
// -- معلومات الرحلة --
if (controller.carType != 'Mishwar Vip')
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildInfoColumn(
icon: Icons.social_distance,
text: '${mapDriverController.distance} ${'KM'.tr}',
text: '${controller.distance} ${'KM'.tr}',
label: 'Distance'.tr,
),
_buildInfoColumn(
icon: Icons.timelapse,
text: mapDriverController.hours > 1
? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
: '${mapDriverController.minutes} ${'m'.tr}',
text: controller.hours > 1
? '${controller.hours}h ${controller.minutes}m'
: '${controller.minutes}m',
label: 'Time'.tr,
),
_buildInfoColumn(
icon: Icons.money_sharp,
text:
'${mapDriverController.paymentAmount} ${'\$'.tr}',
),
],
),
if (mapDriverController.carType != 'Speed' &&
mapDriverController.carType != 'Awfar Car' &&
mapDriverController.carType != 'Scooter')
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoBox(
icon: Icons.timer,
text:
mapDriverController.stringRemainingTimeRideBegin1,
),
_buildInfoBox(
icon: Icons.location_on,
text:
'${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
),
_buildInfoBox(
icon: Icons.attach_money,
text: mapDriverController.price.toStringAsFixed(2),
text: '${controller.paymentAmount} ${'\$'.tr}',
label: 'Price'.tr,
),
],
),
if (controller.carType != 'Mishwar Vip')
const Divider(height: 20),
// -- مؤقت الرحلة المتبقي (إن وجد) --
_builtTimerAndCarType(),
Container(
width: Get.width * 0.8,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppColor.redColor.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: SlideAction(
height: 50,
const SizedBox(height: 12),
// -- زر إنهاء الرحلة المنزلق --
SlideAction(
height: 55,
borderRadius: 15,
elevation: 4,
text: 'Slide to End Trip'.tr,
@@ -114,150 +389,103 @@ GetBuilder<MapDriverController> driverEndRideBar() {
sliderRotate: false,
onSubmit: () {
HapticFeedback.mediumImpact();
mapDriverController.finishRideFromDriver();
controller.finishRideFromDriver();
return null; // New: onSubmit now returns null
},
),
)
],
),
),
)
: const SizedBox(),
),
),
);
}
// New: ودجت لعرض معلومات الرحلة في الشريط العلوي
Widget _buildInfoColumn(
{required IconData icon, required String text, required String label}) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.primaryColor),
const SizedBox(height: 4),
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Text(label,
style:
AppStyle.title.copyWith(color: Colors.grey[600], fontSize: 12)),
],
);
}
// Changed: تم تعديل تصميم ودجت عرض المؤقت ونوع السيارة
class _builtTimerAndCarType extends StatelessWidget {
const _builtTimerAndCarType({
super.key,
});
const _builtTimerAndCarType();
@override
Widget build(BuildContext context) {
final mapDriverController = Get.find<MapDriverController>();
final controller = Get.find<MapDriverController>();
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// -- نوع السيارة --
Container(
decoration: AppStyle.boxDecoration1.copyWith(
boxShadow: [
BoxShadow(
color: AppColor.accentColor.withOpacity(0.2),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
decoration: AppStyle.boxDecoration1.copyWith(color: Colors.grey[200]),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
mapDriverController.carType,
style: AppStyle.title,
controller.carType,
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
),
),
if (mapDriverController.carType != 'Comfort' &&
mapDriverController.carType != 'Mishwar Vip' &&
mapDriverController.carType != 'Lady')
Container(
width: Get.width * 0.6,
// -- مؤقت الرحلة --
if (controller.carType != 'Comfort' &&
controller.carType != 'Mishwar Vip' &&
controller.carType != 'Lady') ...[
const SizedBox(width: 10),
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
mapDriverController.remainingTimeTimerRideBegin < 60
controller.remainingTimeTimerRideBegin < 60
? AppColor.redColor.withOpacity(0.8)
: AppColor.greenColor.withOpacity(0.8),
mapDriverController.remainingTimeTimerRideBegin < 60
controller.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor,
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
boxShadow: [
BoxShadow(
color: (mapDriverController.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor)
.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
alignment: Alignment.center,
children: [
LinearProgressIndicator(
backgroundColor: Colors.white.withOpacity(0.2),
backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white.withOpacity(0.5),
),
Colors.white.withOpacity(0.2)),
minHeight: 40,
value:
mapDriverController.progressTimerRideBegin.toDouble(),
value: controller.progressTimerRideBegin.toDouble(),
),
Center(
child: AnimatedDefaultTextStyle(
duration: Duration(milliseconds: 300),
Text(
controller.stringRemainingTimeRideBegin,
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize:
mapDriverController.remainingTimeTimerRideBegin < 60
? 18
: 16,
shadows: [
Shadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 4,
),
],
),
child: Text(
mapDriverController.stringRemainingTimeRideBegin,
),
),
color: Colors.white, fontWeight: FontWeight.bold),
),
],
),
),
),
)
],
],
);
}
}
Widget _buildInfoColumn({required IconData icon, required String text}) {
return Column(
children: [
Icon(icon),
Text(
text,
style: AppStyle.title,
),
],
);
}
Widget _buildInfoBox({required IconData icon, required String text}) {
return Container(
width: Get.width * .2,
decoration: AppStyle.boxDecoration1,
padding: const EdgeInsets.all(4),
child: Row(
children: [
Icon(icon),
SizedBox(width: 4),
Text(
text,
style: AppStyle.number,
),
],
),
);
}
// Changed: تم تعديل مكان ومظهر دائرة السرعة
GetBuilder<MapDriverController> speedCircle() {
if (Get.find<MapDriverController>().speed > 100) {
if (Platform.isIOS) {
@@ -278,25 +506,36 @@ GetBuilder<MapDriverController> speedCircle() {
);
}
return GetBuilder<MapDriverController>(
builder: (mapDriverController) {
return mapDriverController.isRideStarted
builder: (controller) {
return controller.isRideStarted
? Positioned(
// New: تم وضع دائرة السرعة في الأسفل يمينًا
bottom: 25,
right: 100,
left: 16,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: mapDriverController.speed > 100
color: Colors.white,
boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
border: Border.all(
width: 4,
color: controller.speed > 100
? Colors.red
: AppColor.secondaryColor,
border: Border.all(width: 3, color: AppColor.redColor),
: AppColor.greenColor,
),
height: 60,
width: 60,
),
height: 70,
width: 70,
child: Center(
child: Text(
mapDriverController.speed.toStringAsFixed(0),
style: AppStyle.number,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
controller.speed.toStringAsFixed(0),
style: AppStyle.number.copyWith(fontSize: 24),
),
Text("km/h", style: TextStyle(fontSize: 10)),
],
),
),
),

View File

@@ -1,3 +1,117 @@
// import 'package:flutter/material.dart';
// import 'package:get/get.dart';
// import 'package:google_maps_flutter/google_maps_flutter.dart';
// import '../../../../controller/functions/location_controller.dart';
// import '../../../../controller/home/captin/map_driver_controller.dart';
// class GoogleDriverMap extends StatelessWidget {
// const GoogleDriverMap({
// super.key,
// required this.locationController,
// });
// final LocationController locationController;
// @override
// Widget build(BuildContext context) {
// Get.put(MapDriverController());
// return Padding(
// padding: const EdgeInsets.all(8.0),
// child: GetBuilder<MapDriverController>(
// builder: (controller) => Column(
// children: [
// SizedBox(
// height: Get.height * .92,
// child: GoogleMap(
// onMapCreated: controller.onMapCreated,
// zoomControlsEnabled: true,
// // initialCameraPosition: CameraPosition(
// // target: locationController.myLocation,
// // zoom: 13,
// // bearing: locationController.heading,
// // tilt: 40,
// // ),
// initialCameraPosition: CameraPosition(
// target: locationController.myLocation,
// zoom: 17,
// bearing: locationController.heading, // استخدام اتجاه السائق
// tilt: 60, // زاوية ميل
// ),
// cameraTargetBounds:
// CameraTargetBounds.unbounded, // Allow unrestricted movement
// onCameraMove: (position) {
// CameraPosition(
// target: locationController.myLocation,
// zoom: 13,
// bearing: locationController.heading,
// tilt: 40,
// );
// //todo
// // locationController.myLocation = position.target;
// //
// // controller.mapController
// // ?.animateCamera(CameraUpdate.newCameraPosition(position));
// },
// minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
// myLocationEnabled: true,
// myLocationButtonEnabled: true,
// compassEnabled: true,
// mapType: MapType.terrain,
// rotateGesturesEnabled: true,
// scrollGesturesEnabled: true,
// trafficEnabled: false,
// buildingsEnabled: true,
// mapToolbarEnabled: true,
// fortyFiveDegreeImageryEnabled: true,
// zoomGesturesEnabled: true,
// polylines: {
// Polyline(
// zIndex: 2,
// geodesic: true,
// polylineId: const PolylineId('route1'),
// points: controller.polylineCoordinates,
// color: const Color.fromARGB(255, 163, 81, 246),
// width: 5,
// ),
// Polyline(
// zIndex: 2,
// geodesic: true,
// polylineId: const PolylineId('route'),
// points: controller.polylineCoordinatesDestination,
// color: const Color.fromARGB(255, 10, 29, 126),
// width: 5,
// ),
// },
// markers: {
// Marker(
// markerId: MarkerId('MyLocation'.tr),
// position: locationController.myLocation,
// draggable: true,
// icon: controller.carIcon,
// rotation: locationController.heading,
// ),
// Marker(
// markerId: MarkerId('start'.tr),
// position: controller.latLngPassengerLocation,
// draggable: true,
// icon: controller.startIcon,
// ),
// Marker(
// markerId: MarkerId('end'.tr),
// position: controller.latLngPassengerDestination,
// draggable: true,
// icon: controller.endIcon,
// ),
// },
// ),
// ),
// ],
// ),
// ),
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -15,94 +129,89 @@ class GoogleDriverMap extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(MapDriverController());
return Padding(
padding: const EdgeInsets.all(8.0),
child: GetBuilder<MapDriverController>(
builder: (controller) => Column(
children: [
SizedBox(
height: Get.height * .92,
child: GoogleMap(
onMapCreated: controller.onMapCreated,
zoomControlsEnabled: true,
final MapDriverController controller = Get.put(MapDriverController());
// New: تحديد قيمة الـ padding لتحريك مركز الخريطة للأعلى
final double mapPaddingBottom = MediaQuery.of(context).size.height * 0.3;
return GetBuilder<MapDriverController>(
builder: (controller) => GoogleMap(
onMapCreated: (mapController) {
controller.onMapCreated(mapController);
// New: تطبيق الـ padding بعد إنشاء الخريطة مباشرة
mapController.setMapStyle('[]'); // يمكنك إضافة تصميم مخصص للخريطة هنا
// يمكنك استخدام CameraUpdate.scrollBy لتحريك الكاميرا إذا رغبت بذلك:
// controller.mapController!.animateCamera(CameraUpdate.scrollBy(0, mapPaddingBottom));
},
// New: إضافة padding لتحريك مركز الخريطة للأعلى، مما يجعل أيقونة السائق تظهر في الأسفل
zoomControlsEnabled: false, // Changed: تم إخفاء أزرار الزوم الافتراضية
initialCameraPosition: CameraPosition(
target: locationController.myLocation,
zoom: 13,
bearing: locationController.heading,
tilt: 40,
zoom: 17,
bearing: locationController.heading, // استخدام اتجاه السائق
tilt: 60, // زاوية ميل
),
cameraTargetBounds:
CameraTargetBounds.unbounded, // Allow unrestricted movement
onCameraMove: (position) {
CameraPosition(
target: locationController.myLocation,
zoom: 13,
zoom: 17.5,
tilt: 50.0,
bearing: locationController.heading,
tilt: 40,
);
//todo
// locationController.myLocation = position.target;
//
// controller.mapController
// ?.animateCamera(CameraUpdate.newCameraPosition(position));
},
minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
myLocationEnabled: true,
padding: EdgeInsets.only(bottom: 50, top: Get.height * 0.7),
minMaxZoomPreference: const MinMaxZoomPreference(8, 18),
myLocationEnabled: false, // Changed: تم الاعتماد على ماركر مخصص
myLocationButtonEnabled: true,
compassEnabled: true,
mapType: MapType.terrain,
rotateGesturesEnabled: true,
scrollGesturesEnabled: true,
trafficEnabled: false,
trafficEnabled: true, // Changed: تفعيل عرض حركة المرور
buildingsEnabled: true,
mapToolbarEnabled: true,
fortyFiveDegreeImageryEnabled: true,
zoomGesturesEnabled: true,
polylines: {
Polyline(
zIndex: 2,
geodesic: true,
polylineId: const PolylineId('route1'),
points: controller.polylineCoordinates,
color: const Color.fromARGB(255, 163, 81, 246),
width: 5,
width: 6, // Changed: زيادة عرض الخط
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
Polyline(
zIndex: 2,
geodesic: true,
polylineId: const PolylineId('route'),
points: controller.polylineCoordinatesDestination,
color: const Color.fromARGB(255, 10, 29, 126),
width: 5,
width: 6, // Changed: زيادة عرض الخط
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
},
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: locationController.myLocation,
draggable: true,
draggable: false, // Changed: لا يمكن سحب ماركر السائق
icon: controller.carIcon,
rotation: locationController.heading,
anchor: const Offset(
0.5, 0.5), // New: وضع نقطة ارتكاز الأيقونة في المنتصف
),
Marker(
markerId: MarkerId('start'.tr),
position: controller.latLngPassengerLocation,
draggable: true,
icon: controller.startIcon,
),
Marker(
markerId: MarkerId('end'.tr),
position: controller.latLngPassengerDestination,
draggable: true,
icon: controller.endIcon,
),
},
),
),
],
),
),
);
}
}

View File

@@ -1,11 +1,5 @@
import 'dart:io';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/info.dart';
@@ -15,169 +9,140 @@ import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../main.dart';
import '../../../../print.dart';
// Changed: إعادة تصميم كاملة لتصبح شريط معلومات علوي مدمج
class PassengerInfoWindow extends StatelessWidget {
const PassengerInfoWindow({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (controller) => controller.isPassengerInfoWindow == true
? Positioned(
bottom: 10,
left: 10,
right: 10,
builder: (controller) => AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
// Changed: تم تغيير الموضع من الأسفل إلى الأعلى
top: controller.isPassengerInfoWindow ? 15.0 : -200.0,
left: 15.0,
right: 15.0,
child: Card(
elevation: 5,
elevation: 8,
shadowColor: Colors.black.withOpacity(0.3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
// New: صف علوي للمعلومات الأساسية
_buildTopInfoRow(controller),
const Divider(height: 16),
// Changed: الأزرار الآن في صف أفقي ومدمج
if (!controller.isRideBegin) _buildActionButtons(controller),
// New: مؤشر انتظار الراكب المدمج
if (controller.remainingTimeInPassengerLocatioWait < 300 &&
controller.remainingTimeInPassengerLocatioWait != 0 &&
!controller.isRideBegin) ...[
const SizedBox(height: 10),
_buildWaitingIndicator(controller),
],
// زر الإلغاء بعد انتهاء وقت الانتظار
if (controller.isdriverWaitTimeEnd &&
!controller.isRideBegin) ...[
const SizedBox(height: 10),
_buildCancelAfterWaitButton(controller),
]
],
),
),
),
),
);
}
// New: ودجت لعرض المعلومات العلوية بشكل مدمج
Widget _buildTopInfoRow(MapDriverController controller) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// معلومات الراكب
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Go to passenger Location'.tr,
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold,
'Go to passenger:'.tr,
style: AppStyle.title
.copyWith(color: Colors.grey[600], fontSize: 13),
),
),
if (!controller.isRideBegin)
Wrap(
spacing: 16.0,
children: [
IconButton(
onPressed: () async {
controller.isSocialPressed = true;
await controller.driverCallPassenger();
makePhoneCall(
controller.passengerPhone.toString());
},
icon: const Icon(
Icons.phone,
color: AppColor.blueColor,
),
tooltip: 'Call Passenger',
),
IconButton(
onPressed: () {
Get.bottomSheet(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20)),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: _buildMessageOptions(controller),
),
);
},
icon: const Icon(
Icons.message,
color: AppColor.redColor,
),
tooltip: 'Send Message',
),
IconButton(
onPressed: () async {
if (Platform.isAndroid) {
Bubble().startBubbleHead(
sendAppToBackground: true);
}
await controller
.openGoogleMapFromDriverToPassenger();
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
size: 28,
color: AppColor.blueColor,
),
tooltip: 'Open in Maps',
Text(
controller.passengerName,
style: AppStyle.title
.copyWith(fontWeight: FontWeight.bold, fontSize: 18),
overflow: TextOverflow.ellipsis,
),
],
),
],
),
const SizedBox(height: 12),
// معلومات المسافة والزمن
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.timer,
text: controller.hours > 1
_buildInfoChip(Icons.map_outlined, '${controller.distance} km'),
const SizedBox(width: 8),
_buildInfoChip(
Icons.timer_outlined,
controller.hours > 1
? '${controller.hours}h ${controller.minutes}m'
: '${controller.minutes}m',
label: 'Duration',
),
_buildInfoTile(
icon: Icons.map,
text: '${controller.distance} km',
label: 'Distance',
),
_buildInfoTile(
icon: Icons.person,
text: controller.passengerName,
label: 'Passenger',
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.attach_money,
text: controller.totalPricePassenger,
label: 'Cost',
),
_buildInfoTile(
icon: Icons.directions_car,
text: controller.carType.tr,
label: 'Car Type',
),
],
),
const SizedBox(height: 16),
if (!controller.isRideBegin)
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: MyElevatedButton(
title: 'Start the Ride'.tr,
kolor: AppColor.greenColor,
onPressed: () {
MyDialog().getDialog(
"Is the Passenger in your Car?".tr,
"Don't start trip if passenger not in your car"
.tr,
() async {
await controller
.startRideFromDriver();
Get.back();
},
);
},
}
// New: ودجت مخصص لعرض المعلومات بشكل أنيق
Widget _buildInfoChip(IconData icon, String text) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColor.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.primaryColor, size: 16),
const SizedBox(width: 4),
Text(text,
style: TextStyle(
color: AppColor.primaryColor, fontWeight: FontWeight.bold)),
],
),
const SizedBox(width: 8),
);
}
// Changed: إعادة تصميم أزرار الإجراءات لتكون أكثر دمجًا
Widget _buildActionButtons(MapDriverController controller) {
return Row(
children: [
if (controller.isArrivedSend)
Expanded(
child: MyElevatedButton(
title: 'I Arrive'.tr,
kolor: AppColor.yellowColor,
child: ElevatedButton.icon(
icon: const Icon(Icons.location_on, size: 18),
label: Text('I Arrive'.tr),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.yellowColor,
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
onPressed: () async {
if (await controller
.calculateDistanceBetweenDriverAndPassengerLocation() <
@@ -190,218 +155,97 @@ class PassengerInfoWindow extends StatelessWidget {
[],
'ding.wav',
);
controller
.startTimerToShowDriverWaitPassengerDuration();
controller.startTimerToShowDriverWaitPassengerDuration();
controller.isArrivedSend = false;
} else {
MyDialog().getDialog(
'You are not near the passenger location'
.tr,
'Please go to the pickup location exactly'
.tr, () {
Get.back();
});
'You are not near the passenger location'.tr,
'Please go to the pickup location exactly'.tr,
() => Get.back());
}
},
),
),
],
if (controller.isArrivedSend) const SizedBox(width: 8),
Expanded(
flex: 2,
child: ElevatedButton.icon(
icon: const Icon(Icons.play_arrow_rounded, size: 20),
label: Text('Start the Ride'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.greenColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(height: 12),
if (controller.remainingTimeInPassengerLocatioWait <
300 &&
controller
.remainingTimeInPassengerLocatioWait !=
0)
Stack(
onPressed: () {
MyDialog().getDialog(
"Is the Passenger in your Car?".tr,
"Don't start trip if passenger not in your car".tr,
() async {
await controller.startRideFromDriver();
Get.back();
},
);
},
),
),
],
);
}
// Changed: مؤشر الانتظار الآن أكثر دمجًا
Widget _buildWaitingIndicator(MapDriverController controller) {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Stack(
alignment: Alignment.center,
children: [
LinearProgressIndicator(
backgroundColor: AppColor.greyColor,
color: controller
.remainingTimeInPassengerLocatioWait <
60
backgroundColor: AppColor.greyColor.withOpacity(0.3),
color: controller.remainingTimeInPassengerLocatioWait < 60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 20,
borderRadius: BorderRadius.circular(10),
value: controller
.progressInPassengerLocationFromDriver
.toDouble(),
minHeight: 25,
value: controller.progressInPassengerLocationFromDriver.toDouble(),
),
Text(
controller
.stringRemainingTimeWaitingPassenger,
style: AppStyle.title,
controller.stringRemainingTimeWaitingPassenger,
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13,
shadows: [
Shadow(color: Colors.black.withOpacity(0.5), blurRadius: 2)
]),
),
],
),
const SizedBox(height: 12),
if (controller.isdriverWaitTimeEnd)
MyElevatedButton(
title:
'You Can Cancel the Trip and get Cost From '
.tr +
);
}
// New: زر الإلغاء بعد انتهاء الانتظار
Widget _buildCancelAfterWaitButton(MapDriverController controller) {
return MyElevatedButton(
title: 'You Can Cancel the Trip and get Cost From '.tr +
AppInformation.appName.tr,
kolor: AppColor.deepPurpleAccent,
onPressed: () {
MyDialog().getDialog(
'Are you sure to cancel?'.tr, '',
() async {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
MyDialog().getDialog('Are you sure to cancel?'.tr, '', () async {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Driver Cancelled Your Trip',
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
.tr,
controller.tokenPassenger,
[],
'cancel.wav',
);
Log.print(
'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}');
'cancel.wav');
box.write(BoxName.rideStatus, 'Cancel');
await controller
.addWaitingTimeCostFromPassengerToDriverWallet();
await controller.addWaitingTimeCostFromPassengerToDriverWallet();
controller.isdriverWaitTimeEnd = false;
Get.back();
});
},
),
],
),
],
),
),
),
)
: const SizedBox(),
);
}
Widget _buildInfoTile({
required IconData icon,
required String text,
required String label,
}) {
return Column(
children: [
Icon(icon, color: Colors.grey[700]),
const SizedBox(height: 4),
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
Text(label.tr, style: AppStyle.title),
],
);
}
Widget _buildMessageOptions(MapDriverController controller) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Select a quick message'.tr, style: AppStyle.title),
const SizedBox(height: 16),
_buildMessageTile(
text: "Where are you, sir?".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Where are you, sir?".tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text: "I've been trying to reach you but your phone is off.".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"I've been trying to reach you but your phone is off.".tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text:
"Please don't be late, I'm waiting for you at the specified location."
.tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Please don't be late, I'm waiting for you at the specified location."
.tr,
controller.tokenPassenger,
[],
'ding.wav',
);
Get.back();
},
),
_buildMessageTile(
text: "Please don't be late".tr,
onTap: () {
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'message From Driver',
"Please don't be late".tr,
controller.tokenPassenger,
[],
'cancel.wav',
);
Get.back();
},
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Form(
key: controller.formKey2,
child: MyTextForm(
controller: controller.messageToPassenger,
label: 'Type something'.tr,
hint: 'Type something'.tr,
type: TextInputType.text,
),
),
),
IconButton(
onPressed: () {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'message From Driver',
controller.messageToPassenger.text,
controller.tokenPassenger,
[],
'ding.wav',
);
controller.messageToPassenger.clear();
Get.back();
},
icon: const Icon(Icons.send),
),
],
),
],
);
}
Widget _buildMessageTile(
{required String text, required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
),
child: Text(text, style: AppStyle.title),
),
);
}
}

View File

@@ -1,93 +1,287 @@
import 'dart:io';
// import 'dart:io';
// import 'package:bubble_head/bubble.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_font_icons/flutter_font_icons.dart';
// import 'package:get/get.dart';
// import 'package:sefer_driver/constant/info.dart';
// import 'package:sefer_driver/controller/functions/location_controller.dart';
// import 'package:sefer_driver/views/widgets/elevated_btn.dart';
// import 'package:sefer_driver/views/widgets/my_textField.dart';
// import 'package:url_launcher/url_launcher.dart';
// import '../../../../constant/box_name.dart';
// import '../../../../constant/colors.dart';
// import '../../../../constant/style.dart';
// import '../../../../controller/functions/launch.dart';
// import '../../../../controller/home/captin/map_driver_controller.dart';
// import '../../../../main.dart';
// class SosConnect extends StatelessWidget {
// const SosConnect({super.key});
// @override
// Widget build(BuildContext context) {
// return GetBuilder<MapDriverController>(
// builder: (mapDriverController) => mapDriverController.isRideStarted
// ? Positioned(
// left: 16,
// bottom: 16,
// child: Card(
// elevation: 4,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12),
// ),
// child: SizedBox(
// height: 60,
// width: 180,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// IconButton(
// onPressed: () {
// _handleSosCall(mapDriverController);
// },
// icon: const Icon(
// Icons.sos_sharp,
// size: 32,
// color: AppColor.redColor,
// ),
// tooltip: 'SOS - Call Emergency',
// ),
// VerticalDivider(
// color: Colors.grey[300],
// thickness: 1,
// ),
// IconButton(
// onPressed: () {
// _handleWhatsApp(mapDriverController);
// },
// icon: const Icon(
// FontAwesome.whatsapp,
// color: AppColor.greenColor,
// size: 32,
// ),
// tooltip: 'SOS - Send WhatsApp Message',
// ),
// VerticalDivider(
// color: Colors.grey[300],
// thickness: 1,
// ),
// IconButton(
// onPressed: () {
// _handleGoogleMap(mapDriverController);
// },
// icon: const Icon(
// MaterialCommunityIcons.map_marker_radius,
// color: AppColor.primaryColor,
// size: 32,
// ),
// tooltip: 'Google Maps - Navigate',
// ),
// ],
// ),
// ),
// ),
// )
// : const SizedBox(),
// );
// }
// void _handleSosCall(MapDriverController mapDriverController) {
// if (box.read(BoxName.sosPhoneDriver) == null) {
// Get.defaultDialog(
// title: 'Insert Emergency Number'.tr,
// content: Form(
// key: mapDriverController.formKey1,
// child: MyTextForm(
// controller: mapDriverController.sosEmergincyNumberCotroller,
// label: 'Emergency Number'.tr,
// hint: 'Enter phone number'.tr,
// type: TextInputType.phone,
// ),
// ),
// confirm: MyElevatedButton(
// title: 'Save'.tr,
// onPressed: () {
// if (mapDriverController.formKey1.currentState!.validate()) {
// box.write(BoxName.sosPhoneDriver,
// mapDriverController.sosEmergincyNumberCotroller.text);
// Get.back(); // Close the dialog
// launchCommunication(
// 'phone', box.read(BoxName.sosPhoneDriver), '');
// }
// },
// ),
// );
// } else {
// launchCommunication('phone', box.read(BoxName.sosPhoneDriver), '');
// }
// }
// void _handleWhatsApp(MapDriverController mapDriverController) {
// if (box.read(BoxName.sosPhoneDriver) == null) {
// Get.defaultDialog(
// title: 'Insert Emergency Number'.tr,
// content: Form(
// key: mapDriverController.formKey1,
// child: MyTextForm(
// controller: mapDriverController.sosEmergincyNumberCotroller,
// label: 'Emergency Number'.tr,
// hint: 'Enter phone number'.tr,
// type: TextInputType.phone,
// ),
// ),
// confirm: MyElevatedButton(
// title: 'Save'.tr,
// onPressed: () {
// if (mapDriverController.formKey1.currentState!.validate()) {
// box.write(BoxName.sosPhoneDriver,
// mapDriverController.sosEmergincyNumberCotroller.text);
// Get.back(); // Close the dialog
// _sendWhatsAppMessage(mapDriverController);
// }
// },
// ),
// );
// } else {
// _sendWhatsAppMessage(mapDriverController);
// }
// }
// void _handleGoogleMap(MapDriverController mapDriverController) {
// () async {
// if (Platform.isAndroid) {
// Bubble().startBubbleHead(sendAppToBackground: true);
// }
// var startLat =
// Get.find<MapDriverController>().latLngPassengerLocation.latitude;
// var startLng =
// Get.find<MapDriverController>().latLngPassengerLocation.longitude;
// var endLat =
// Get.find<MapDriverController>().latLngPassengerDestination.latitude;
// var endLng =
// Get.find<MapDriverController>().latLngPassengerDestination.longitude;
// String url =
// 'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
// if (await canLaunchUrl(Uri.parse(url))) {
// await launchUrl(Uri.parse(url));
// } else {
// throw 'Could not launch google maps';
// }
// }();
// }
// void _sendWhatsAppMessage(MapDriverController mapDriverController) {
// final sosNumber = box.read(BoxName.sosPhoneDriver);
// if (sosNumber != null) {
// launchCommunication(
// 'whatsapp',
// '+2$sosNumber', // Consider international format
// "${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
// "${"My current location is:".tr} "
// "https://www.google.com/maps/place/"
// "${Get.find<LocationController>().myLocation.latitude},"
// "${Get.find<LocationController>().myLocation.longitude} "
// "${"\nI have a trip on".tr} ${AppInformation.appName} "
// "${"app with passenger".tr} ${mapDriverController.passengerName}.",
// );
// }
// }
// }
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/info.dart';
import 'package:sefer_driver/controller/functions/location_controller.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/firebase/firbase_messge.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../../main.dart';
// Changed: إعادة تصميم وتغيير موضع أزرار التواصل والطوارئ
class SosConnect extends StatelessWidget {
const SosConnect({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 16,
bottom: 16,
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: SizedBox(
height: 60,
width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
builder: (controller) {
// New: تجميع الأزرار في عمود واحد على الجانب الأيمن
return Positioned(
bottom: 110, // New: فوق عداد السرعة
right: 16,
child: Column(
children: [
IconButton(
onPressed: () {
_handleSosCall(mapDriverController);
// زر الاتصال بالراكب (يظهر قبل بدء الرحلة)
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
_buildSocialButton(
icon: Icons.phone,
color: AppColor.blueColor,
tooltip: 'Call Passenger',
onPressed: () async {
controller.isSocialPressed = true;
await controller.driverCallPassenger();
makePhoneCall(controller.passengerPhone.toString());
},
icon: const Icon(
Icons.sos_sharp,
size: 32,
color: AppColor.redColor,
),
tooltip: 'SOS - Call Emergency',
),
VerticalDivider(
color: Colors.grey[300],
thickness: 1,
),
IconButton(
onPressed: () {
_handleWhatsApp(mapDriverController);
},
icon: const Icon(
FontAwesome.whatsapp,
// زر الرسائل للراكب (يظهر قبل بدء الرحلة)
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
const SizedBox(height: 12),
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
_buildSocialButton(
icon: Icons.message,
color: AppColor.greenColor,
size: 32,
),
tooltip: 'SOS - Send WhatsApp Message',
),
VerticalDivider(
color: Colors.grey[300],
thickness: 1,
),
IconButton(
tooltip: 'Send Message',
onPressed: () {
_handleGoogleMap(mapDriverController);
// الكود الخاص بنافذة الرسائل السريعة
_showMessageOptions(context, controller);
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
color: AppColor.primaryColor,
size: 32,
),
tooltip: 'Google Maps - Navigate',
// زر الطوارئ (SOS) (يظهر بعد بدء الرحلة)
if (controller.isRideStarted)
_buildSocialButton(
icon: Icons.sos_sharp,
color: AppColor.redColor,
tooltip: 'SOS - Call Emergency',
onPressed: () => _handleSosCall(controller),
),
],
),
),
),
)
: const SizedBox(),
);
},
);
}
// New: ودجت منفصل لبناء أزرار التواصل
Widget _buildSocialButton(
{required IconData icon,
required Color color,
required String tooltip,
required VoidCallback onPressed}) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
),
child: IconButton(
icon: Icon(icon, color: color, size: 28),
tooltip: tooltip,
onPressed: onPressed,
),
);
}
// الكود الخاص بنافذة إدخال رقم الطوارئ
void _handleSosCall(MapDriverController mapDriverController) {
if (box.read(BoxName.sosPhoneDriver) == null) {
Get.defaultDialog(
@@ -107,7 +301,7 @@ class SosConnect extends StatelessWidget {
if (mapDriverController.formKey1.currentState!.validate()) {
box.write(BoxName.sosPhoneDriver,
mapDriverController.sosEmergincyNumberCotroller.text);
Get.back(); // Close the dialog
Get.back();
launchCommunication(
'phone', box.read(BoxName.sosPhoneDriver), '');
}
@@ -119,75 +313,99 @@ class SosConnect extends StatelessWidget {
}
}
void _handleWhatsApp(MapDriverController mapDriverController) {
if (box.read(BoxName.sosPhoneDriver) == null) {
Get.defaultDialog(
title: 'Insert Emergency Number'.tr,
content: Form(
key: mapDriverController.formKey1,
// New: الكود الخاص بنافذة الرسائل السريعة (مستخرج من passenger_info_window.dart)
void _showMessageOptions(
BuildContext context, MapDriverController controller) {
Get.bottomSheet(
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: _buildMessageOptions(controller),
),
);
}
Widget _buildMessageOptions(MapDriverController controller) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Select a quick message'.tr, style: AppStyle.title),
const SizedBox(height: 16),
_buildMessageTile(
text: "Where are you, sir?".tr,
onTap: () {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'message From Driver',
"Where are you, sir?".tr,
controller.tokenPassenger,
[],
'ding.wav');
Get.back();
}),
_buildMessageTile(
text: "I've been trying to reach you but your phone is off.".tr,
onTap: () {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'message From Driver',
"I've been trying to reach you but your phone is off.".tr,
controller.tokenPassenger,
[],
'ding.wav');
Get.back();
}),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Form(
key: controller.formKey2,
child: MyTextForm(
controller: mapDriverController.sosEmergincyNumberCotroller,
label: 'Emergency Number'.tr,
hint: 'Enter phone number'.tr,
type: TextInputType.phone,
controller: controller.messageToPassenger,
label: 'Type something'.tr,
hint: 'Type something'.tr,
type: TextInputType.text,
),
),
confirm: MyElevatedButton(
title: 'Save'.tr,
),
IconButton(
onPressed: () {
if (mapDriverController.formKey1.currentState!.validate()) {
box.write(BoxName.sosPhoneDriver,
mapDriverController.sosEmergincyNumberCotroller.text);
Get.back(); // Close the dialog
_sendWhatsAppMessage(mapDriverController);
}
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'message From Driver',
controller.messageToPassenger.text,
controller.tokenPassenger,
[],
'ding.wav');
controller.messageToPassenger.clear();
Get.back();
},
icon: const Icon(Icons.send),
),
],
),
],
);
}
Widget _buildMessageTile(
{required String text, required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
),
child: Text(text, style: AppStyle.title),
),
);
} else {
_sendWhatsAppMessage(mapDriverController);
}
}
void _handleGoogleMap(MapDriverController mapDriverController) {
() async {
if (Platform.isAndroid) {
Bubble().startBubbleHead(sendAppToBackground: true);
}
var startLat =
Get.find<MapDriverController>().latLngPassengerLocation.latitude;
var startLng =
Get.find<MapDriverController>().latLngPassengerLocation.longitude;
var endLat =
Get.find<MapDriverController>().latLngPassengerDestination.latitude;
var endLng =
Get.find<MapDriverController>().latLngPassengerDestination.longitude;
String url =
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
throw 'Could not launch google maps';
}
}();
}
void _sendWhatsAppMessage(MapDriverController mapDriverController) {
final sosNumber = box.read(BoxName.sosPhoneDriver);
if (sosNumber != null) {
launchCommunication(
'whatsapp',
'+2$sosNumber', // Consider international format
"${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
"${"My current location is:".tr} "
"https://www.google.com/maps/place/"
"${Get.find<LocationController>().myLocation.latitude},"
"${Get.find<LocationController>().myLocation.longitude} "
"${"\nI have a trip on".tr} ${AppInformation.appName} "
"${"app with passenger".tr} ${mapDriverController.passengerName}.",
);
}
}
}

View File

@@ -192,7 +192,7 @@ class WalletCaptainRefactored extends StatelessWidget {
.speakText(
'This amount for all trip I get from Passengers and Collected For me in'
.tr +
' SAFAR Wallet'.tr),
' Intaleq Wallet'.tr),
child: const Icon(Icons.headphones),
),
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',

View File

@@ -36,7 +36,7 @@ class BehaviorPage extends StatelessWidget {
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text("Overall Behavior Score",
Text("Overall Behavior Score".tr,
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
@@ -49,7 +49,7 @@ class BehaviorPage extends StatelessWidget {
),
),
const SizedBox(height: 20),
const Text("Last 10 Trips",
Text("Last 10 Trips".tr,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
ListView.builder(
@@ -70,10 +70,12 @@ class BehaviorPage extends StatelessWidget {
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Behavior Score: ${trip['behavior_score']}"),
Text("Max Speed: ${trip['max_speed']} km/h"),
Text("Hard Brakes: ${trip['hard_brakes']}"),
Text("Distance: ${trip['total_distance']} km"),
Text(
"${'Behavior Score'.tr}: ${trip['behavior_score']}"),
Text("${'Max Speed'.tr}: ${trip['max_speed']} km/h"),
Text("${'Hard Brake'.tr}s: ${trip['hard_brakes']}"),
Text(
"${'Distance'.tr}: ${trip['total_distance']} km"),
],
),
),

View File

@@ -1,373 +1,202 @@
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/controller/profile/captain_profile_controller.dart';
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../my_wallet/walet_captain.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'behavior_page.dart';
import 'captains_cars.dart';
// الصفحة الرئيسية الجديدة
class ProfileCaptain extends StatelessWidget {
ProfileCaptain({super.key});
CaptainWalletController captainWalletController = CaptainWalletController();
const ProfileCaptain({super.key});
@override
Widget build(BuildContext context) {
Get.put(CaptainProfileController());
// Get.put() يجب أن يكون في مكان يتم استدعاؤه مرة واحدة فقط،
// لكن سنبقيه هنا حسب الكود الأصلي
final controller = Get.put(CaptainProfileController());
return MyScafolld(
title: 'My Profile'.tr,
isleading: true,
body: [
GetBuilder<CaptainProfileController>(
builder: (controller) => Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Center(
child: controller.isLoading
? const MyCircularProgressIndicator()
: Column(
builder: (controller) {
if (controller.isLoading) {
return const Center(child: MyCircularProgressIndicator());
}
if (controller.captainProfileData.isEmpty) {
return Center(child: Text('Failed to load profile data.'.tr));
}
return SingleChildScrollView(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Column(
children: [
Material(
elevation: 2,
borderRadius: BorderRadius.circular(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () async {
// addBankCodeEgypt(captainWalletController);
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Add bank Account'.tr,
style: AppStyle.title,
),
),
),
InkWell(
onTap: () async {
Get.to(() => BehaviorPage());
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Show behavior page'.tr,
style: AppStyle.title,
),
),
),
],
),
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Show my Cars'.tr,
onPressed: () async {
Get.to(() => CaptainsCars());
},
),
// const SizedBox(height: 15),
MyElevatedButton(
title: 'Add criminal page'.tr,
onPressed: () async {
Get.to(() => CriminalDocumemtPage());
},
),
],
),
SizedBox(
height: Get.height * .7,
child: DriverProfileCard(
driverId:
controller.captainProfileData['driverID'] ??
'',
// 1. رأس الصفحة: صورة واسم وتقييم
ProfileHeader(
name:
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}',
phoneNumber:
controller.captainProfileData['phone'] ?? '',
email:
controller.captainProfileData['email'] ?? '',
birthdate: controller
.captainProfileData['birthdate'] is String
? controller.captainProfileData['birthdate']
: '',
gender: controller.captainProfileData['gender']
is String
? controller.captainProfileData['gender']
: '',
education: controller
.captainProfileData['education'] is String
? controller.captainProfileData['education']
: '',
carMake:
controller.captainProfileData['make'] ?? '',
carModel:
controller.captainProfileData['model'] ?? '',
carPlate:
controller.captainProfileData['car_plate'] ??
'',
carColor:
controller.captainProfileData['color'] ?? '',
vin: controller.captainProfileData['vin'] ?? '',
registrationDate: controller.captainProfileData[
'registration_date'] ??
'',
expirationDate: controller
.captainProfileData['expiration_date'] ??
'',
'${controller.captainProfileData['first_name'] ?? ''} ${controller.captainProfileData['last_name'] ?? ''}',
rating:
controller.captainProfileData['ratingDriver'] != null
? double.tryParse(controller
.captainProfileData['ratingDriver']
.toString()) ??
0.0
: 0.0,
ratingCount: int.tryParse(controller
.captainProfileData['ratingCount']
.toString()) ??
0,
ratingDriver: controller
.captainProfileData['ratingDriver'] !=
null
? double.tryParse(controller
.captainProfileData['ratingDriver']
.toString()) ??
0
: null,
age: int.tryParse(controller
.captainProfileData['age']
.toString()) ??
0,
),
),
const SizedBox(height: 24),
// 2. قسم الإجراءات السريعة
ActionsGrid(),
const SizedBox(height: 24),
// 3. بطاقة المعلومات الشخصية
PersonalInfoCard(
data: controller.captainProfileData
.cast<String, dynamic>()),
const SizedBox(height: 16),
// 4. بطاقة معلومات المركبة (قابلة للتوسيع)
VehicleInfoCard(
data: controller.captainProfileData
.cast<String, dynamic>()),
],
),
);
},
),
),
),
)
],
isleading: true,
);
}
}
class DriverProfileCard extends StatelessWidget {
final String driverId;
final String name;
final String phoneNumber;
final String email;
final String birthdate;
final String gender;
final String education;
final String carMake;
final String carModel;
final String carPlate;
final String carColor;
final String vin;
final String registrationDate;
final String expirationDate;
final int ratingCount;
final double? ratingDriver;
final int age;
// --- الويدجتس الجديدة المنفصلة لتحسين التصميم ---
DriverProfileCard({
required this.driverId,
/// 1. ويدجت رأس الصفحة
class ProfileHeader extends StatelessWidget {
final String name;
final double rating;
final int ratingCount;
const ProfileHeader({
super.key,
required this.name,
required this.phoneNumber,
required this.email,
required this.birthdate,
required this.gender,
required this.education,
required this.carMake,
required this.carModel,
required this.carPlate,
required this.carColor,
required this.vin,
required this.registrationDate,
required this.expirationDate,
required this.rating,
required this.ratingCount,
required this.ratingDriver,
required this.age,
});
@override
Widget build(BuildContext context) {
return Container(
// elevation: 8,
decoration: AppStyle.boxDecoration1,
margin: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
return Column(
children: [
CircleAvatar(
radius: 50,
backgroundColor: Get.theme.primaryColor.withOpacity(0.1),
child: Icon(Icons.person, size: 60, color: Get.theme.primaryColor),
),
const SizedBox(height: 12),
Text(
style: AppStyle.title,
name,
style: Get.textTheme.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.phone),
const SizedBox(width: 8),
Text(style: AppStyle.title, phoneNumber),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Icon(Icons.star, color: Colors.amber, size: 20),
const SizedBox(width: 4),
Text(
style: AppStyle.title,
'${'birthdate'.tr} : $birthdate',
'${rating.toStringAsFixed(1)} (${'reviews'.tr} $ratingCount)',
style: Get.textTheme.titleMedium
?.copyWith(color: Colors.grey.shade600),
),
],
),
const SizedBox(height: 8),
Row(
],
);
}
}
/// 2. ويدجت شبكة الأزرار
class ActionsGrid extends StatelessWidget {
const ActionsGrid({super.key});
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 2.5, // للتحكم في ارتفاع الأزرار
children: [
const Icon(Icons.wc),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'gender'.tr} : $gender',
_ActionTile(
title: 'My Cars'.tr,
icon: Icons.directions_car_filled,
onTap: () => Get.to(() => CaptainsCars()),
),
_ActionTile(
title: 'Criminal Record'.tr,
icon: Icons.description,
onTap: () => Get.to(() => CriminalDocumemtPage()),
),
_ActionTile(
title: 'Bank Account'.tr,
icon: Icons.account_balance,
onTap: () {
MyDialog().getDialog('Coming Soon'.tr,
'This service will be available soon.'.tr, () => Get.back());
},
),
_ActionTile(
title: 'Behavior Page'.tr,
icon: Icons.checklist_rtl,
onTap: () => Get.to(() => BehaviorPage()),
),
],
),
const SizedBox(height: 8),
Row(
);
}
}
/// ويدجت داخلية لزر في الشبكة
class _ActionTile extends StatelessWidget {
final String title;
final IconData icon;
final VoidCallback onTap;
const _ActionTile(
{required this.title, required this.icon, required this.onTap});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.school),
Icon(icon, color: Get.theme.primaryColor, size: 20),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'education'.tr} : $education',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.car_repair),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'Make'.tr} : $carMake',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.model_training),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_model'.tr} : $carModel',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.drive_eta),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_plate'.tr} : $carPlate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.color_lens),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_color'.tr} : $carColor',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.confirmation_number),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'vin'.tr} : $vin',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'registration_date'.tr} : $registrationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'expiration_date'.tr} : $expirationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'rating_count'.tr} : $ratingCount',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star_rate),
const SizedBox(width: 8),
ratingDriver != null
? Text(
style: AppStyle.title,
'${'rating_driver'.tr} : $ratingDriver',
)
: Text(
style: AppStyle.title,
'${'rating_driver'.tr} : 0',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.person),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'age'.tr} : $age',
),
],
),
Flexible(
child: Text(
title,
style: Get.textTheme.labelLarge,
textAlign: TextAlign.center,
)),
],
),
),
@@ -375,3 +204,147 @@ class DriverProfileCard extends StatelessWidget {
);
}
}
/// 3. بطاقة المعلومات الشخصية
class PersonalInfoCard extends StatelessWidget {
final Map<String, dynamic> data;
PersonalInfoCard({super.key, required this.data});
final controller = Get.find<CaptainProfileController>();
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Personal Information'.tr, style: Get.textTheme.titleLarge),
const Divider(height: 24),
_InfoRow(
icon: Icons.phone,
label: 'Phone Number'.tr,
value: data['phone'] ?? ''),
if (data['email'] != null &&
data['email'].toString().contains('intaleqapp')) ...[
TextFormField(
controller: controller.emailController,
keyboardType:
TextInputType.emailAddress, // ✅ لوحة مفاتيح خاصة بالإيميل
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email'.tr,
hintText: 'Enter your email'.tr,
prefixIcon: Icon(Icons.email),
),
autofillHints: [
AutofillHints.email
], // اختياري لتحسين تجربة المستخدم
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () async {
await controller.updateEmail();
},
child: Text('Update'.tr),
),
] else
_InfoRow(
icon: Icons.email,
label: 'Email'.tr,
value: data['email'] ?? '',
),
_InfoRow(
icon: Icons.cake,
label: 'Age'.tr,
value: data['age']?.toString() ?? 'N/A'),
_InfoRow(
icon: Icons.wc,
label: 'Gender'.tr,
value: data['gender'] ?? 'N/A'),
],
),
),
);
}
}
/// 4. بطاقة معلومات المركبة
class VehicleInfoCard extends StatelessWidget {
final Map<String, dynamic> data;
const VehicleInfoCard({super.key, required this.data});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ExpansionTile(
title: Text('Vehicle Details'.tr, style: Get.textTheme.titleLarge),
leading: Icon(Icons.directions_car, color: Get.theme.primaryColor),
childrenPadding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
children: [
_InfoRow(
icon: Icons.branding_watermark,
label: 'Make'.tr,
value: data['make'] ?? ''),
_InfoRow(
icon: Icons.category,
label: 'Model'.tr,
value: data['model'] ?? ''),
_InfoRow(
icon: Icons.palette,
label: 'Color'.tr,
value: data['color'] ?? ''),
_InfoRow(
icon: Icons.pin,
label: 'Plate Number'.tr,
value: data['car_plate'] ?? ''),
_InfoRow(
icon: Icons.confirmation_number,
label: 'VIN'.tr,
value: data['vin'] ?? ''),
_InfoRow(
icon: Icons.event,
label: 'Expiration Date'.tr,
value: data['expiration_date'] ?? ''),
],
),
);
}
}
/// ويدجت لعرض سطر معلومة (أيقونة + عنوان + قيمة) لتجنب التكرار
class _InfoRow extends StatelessWidget {
final IconData icon;
final String label;
final String value;
const _InfoRow(
{required this.icon, required this.label, required this.value});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
Icon(icon, color: Colors.grey.shade500, size: 20),
const SizedBox(width: 16),
Text(label, style: Get.textTheme.bodyLarge),
const Spacer(),
Flexible(
child: Text(
value,
style: Get.textTheme.bodyLarge?.copyWith(
color: Colors.grey.shade700, fontWeight: FontWeight.w500),
textAlign: TextAlign.end,
),
),
],
),
);
}
}

View File

@@ -105,7 +105,7 @@ class MyDialog extends GetxController {
Get.back();
},
child: Text(
'Cancel',
'Cancel'.tr,
style: TextStyle(
color: AppColor.redColor,
fontWeight: FontWeight.w600,

View File

@@ -836,6 +836,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.2.1"
flutter_staggered_animations:
dependency: "direct main"
description:
name: flutter_staggered_animations
sha256: "81d3c816c9bb0dca9e8a5d5454610e21ffb068aedb2bde49d2f8d04f75538351"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter_stripe:
dependency: "direct main"
description:

View File

@@ -99,6 +99,7 @@ dependencies:
shimmer: ^3.0.0
flutter_svg: ^2.2.0
lottie: ^3.3.1
flutter_staggered_animations: ^1.1.1
dev_dependencies:
flutter_test: