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. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23 minSdk = 23
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = 3 versionCode = 7
versionName = '1.0.3' versionName = '1.0.7'
multiDexEnabled =true multiDexEnabled =true
} }

View File

@@ -26,7 +26,7 @@ class AK {
static final String accountSIDTwillo = static final String accountSIDTwillo =
X.r(X.r(X.r(Env.accountSIDTwillo, cn), cC), cs); 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 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 = static final String twilloRecoveryCode =
X.r(X.r(X.r(Env.twilloRecoveryCode, cn), cC), cs); X.r(X.r(X.r(Env.twilloRecoveryCode, cn), cC), cs);
static final String authTokenTwillo = static final String authTokenTwillo =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart'; import 'package:location/location.dart';
import 'package:sefer_driver/constant/table_names.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/box_name.dart';
import '../../constant/links.dart'; import '../../constant/links.dart';
@@ -87,7 +88,7 @@ class LocationController extends GetxController {
Get.find<CaptainWalletController>().totalPoints.toString(); Get.find<CaptainWalletController>().totalPoints.toString();
isActive = Get.find<HomeCaptainController>().isActive; isActive = Get.find<HomeCaptainController>().isActive;
if (isActive && double.parse(totalPoints) > -300) { if (isActive && double.parse(totalPoints) > -30000) {
await getLocation(); await getLocation();
if (myLocation.latitude == 0 && myLocation.longitude == 0) return; if (myLocation.latitude == 0 && myLocation.longitude == 0) return;
@@ -147,13 +148,25 @@ class LocationController extends GetxController {
Get.find<HomeCaptainController>() Get.find<HomeCaptainController>()
.mapHomeCaptainController .mapHomeCaptainController
?.animateCamera( ?.animateCamera(
CameraUpdate.newLatLng( CameraUpdate.newCameraPosition(
LatLng( CameraPosition(
myLocation.latitude, bearing: Get.find<LocationController>().heading,
myLocation.longitude, 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) { } catch (e) {
print('Location update error: $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 'package:url_launcher/url_launcher.dart';
import '../../constant/info.dart'; import '../../constant/info.dart';
import '../../main.dart'; import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart'; import 'encrypt_decrypt.dart';
Future<void> checkForUpdate(BuildContext context) async { Future<void> checkForUpdate(BuildContext context) async {
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.buildNumber; final currentVersion = packageInfo.buildNumber;
final version = packageInfo.version; final version = packageInfo.version;
// print('currentVersion is : $currentVersion'); Log.print('version: ${version}');
print('currentVersion is : $currentVersion');
// Fetch the latest version from your server // Fetch the latest version from your server
String latestVersion = await getPackageInfo(); String latestVersion = await getPackageInfo();
box.write(BoxName.packagInfo, version); box.write(BoxName.packagInfo, version);
@@ -45,8 +47,8 @@ Future<String> getPackageInfo() async {
void showUpdateDialog(BuildContext context) { void showUpdateDialog(BuildContext context) {
final String storeUrl = Platform.isAndroid final String storeUrl = Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=com.sefer_driver' ? 'https://play.google.com/store/apps/details?id=com.intaleq_driver'
: 'https://apps.apple.com/ae/app/sefer-driver/id6502189302'; : 'https://apps.apple.com/ae/app/intaleq-driver/id6502189302';
showGeneralDialog( showGeneralDialog(
context: context, 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/box_name.dart';
import '../../constant/colors.dart'; import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart'; import '../../main.dart';
import '../../print.dart'; import '../../print.dart';
import 'encrypt_decrypt.dart'; import 'encrypt_decrypt.dart';
@@ -310,17 +311,20 @@ class ImageController extends GetxController {
Log.print('request: ${request}'); Log.print('request: ${request}');
var length = await file.length(); var length = await file.length();
var stream = http.ByteStream(file.openRead()); 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( var multipartFile = http.MultipartFile(
'image', 'image',
stream, stream,
length, length,
filename: basename(file.path), filename: basename(file.path),
); );
request.headers.addAll({ request.headers.addAll(headers);
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
// Set the file name to the driverID // Set the file name to the driverID
request.files.add( request.files.add(
http.MultipartFile( http.MultipartFile(
'image', 'image',
@@ -418,14 +422,11 @@ class ImageController extends GetxController {
try { try {
await uploadImage( await uploadImage(
compressedImage, compressedImage,
{ {'driverID': (box.read(BoxName.driverID)), 'imageType': imageType},
'driverID':
(box.read(BoxName.driverID)) ?? (box.read(BoxName.passengerID)),
'imageType': imageType
},
link, link,
); );
} catch (e) { } catch (e) {
Log.print('e: ${e}');
mySnackeBarError('Image Upload Failed'.tr); mySnackeBarError('Image Upload Failed'.tr);
// Get.snackbar('Image Upload Failed'.tr, e.toString(), // Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor); // backgroundColor: AppColor.redColor);

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart'; 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/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart'; import 'package:sefer_driver/views/widgets/mydialoug.dart';
@@ -107,11 +108,16 @@ class MapDriverController extends GetxController {
LatLng latLngPassengerLocation = LatLng(0, 0); LatLng latLngPassengerLocation = LatLng(0, 0);
late LatLng latLngPassengerDestination = LatLng(0, 0); late LatLng latLngPassengerDestination = LatLng(0, 0);
List<Map<String, dynamic>> routeSteps = [];
String currentInstruction = "";
int currentStepIndex = 0;
void onMapCreated(GoogleMapController controller) async { void onMapCreated(GoogleMapController controller) async {
myLocation = Get.find<LocationController>().myLocation; myLocation = Get.find<LocationController>().myLocation;
// myLocation = myLocation; // myLocation = myLocation;
mapController = controller; mapController = controller;
controller.getVisibleRegion(); controller.getVisibleRegion();
// LatLngBounds bounds = await controller.getVisibleRegion();
controller.animateCamera( controller.animateCamera(
CameraUpdate.newLatLng(Get.find<LocationController>().myLocation), CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
); );
@@ -410,20 +416,20 @@ class MapDriverController extends GetxController {
'order_id': (rideId).toString(), 'order_id': (rideId).toString(),
'status': 'Begin' 'status': 'Begin'
}); });
if (AppLink.endPoint != AppLink.seferCairoServer) { // if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: { // CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
'id': (rideId), // 'id': (rideId),
'rideTimeStart': DateTime.now().toString(), // 'rideTimeStart': DateTime.now().toString(),
'status': 'Begin', // 'status': 'Begin',
}); // });
CRUD().post( // CRUD().post(
link: '${AppLink.endPoint}/rides/driver_order/add.php', // link: '${AppLink.endPoint}/rides/driver_order/add.php',
payload: { // payload: {
'driver_id': box.read(BoxName.driverID).toString(), // 'driver_id': box.read(BoxName.driverID).toString(),
'order_id': (rideId).toString(), // 'order_id': (rideId).toString(),
'status': 'Begin' // 'status': 'Begin'
}); // });
} // }
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP( Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
'Trip is Begin'.tr, 'Trip is Begin'.tr,
box.read(BoxName.nameDriver).toString(), box.read(BoxName.nameDriver).toString(),
@@ -591,28 +597,34 @@ class MapDriverController extends GetxController {
} }
Future<void> finishRideFromDriver() async { Future<void> finishRideFromDriver() async {
double distanceToDestination = Geolocator.distanceBetween( // double distanceToDestination = Geolocator.distanceBetween(
latLngPassengerDestination.latitude, // latLngPassengerDestination.latitude,
latLngPassengerDestination.longitude, // latLngPassengerDestination.longitude,
Get.find<LocationController>().myLocation.latitude, // Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude, // Get.find<LocationController>().myLocation.longitude,
); // );
final originalDistanceM = double.parse(distance.toString()) * 1000; final originalDistanceM = double.parse(distance.toString()) * 1000;
// 2. احسب المسافة التي قطعها السائق حتى الآن // 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. عتبة ثلث المسافة // 3. عتبة ثلث المسافة
final oneThirdDistanceM = originalDistanceM / 3; final oneThirdDistanceM = originalDistanceM / 3;
// Logging للتتبع // Logging للتتبع
Log.print('originalDistanceM: $originalDistanceM'); Log.print('originalDistanceM: $originalDistanceM');
Log.print('distanceToDestinationM: $distanceToDestination'); // Log.print('distanceToDestinationM: $distanceToDestination');
Log.print('movedDistanceM: $movedDistanceM'); Log.print('movedDistanceM: $movedDistanceM');
Log.print('oneThirdDistanceM: $oneThirdDistanceM'); Log.print('oneThirdDistanceM: $oneThirdDistanceM');
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد // 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
if (movedDistanceM < oneThirdDistanceM) { if (movedDistanceM > oneThirdDistanceM * 2) {
MyDialog().getDialog( MyDialog().getDialog(
'Are you sure to exit ride?'.tr, '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}'); ('${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: {}); var response = await CRUD().getGoogleApi(link: url, payload: {});
Log.print('response: ${response}');
data = response['routes'][0]['legs']; data = response['routes'][0]['legs'];
distanceBetweenDriverAndPassengerWhenConfirm = distanceBetweenDriverAndPassengerWhenConfirm =
(data[0]['distance']['value']) / 1000; (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 { getMapDestination(String origin, destination) async {
var url = var url =
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}'); ('${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(); double lng = points[i][1].toDouble();
polylineCoordinatesDestination.add(LatLng(lat, lng)); 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) { if (polyLinesDestination.isNotEmpty) {
// clearPolyline(); // clearPolyline();
var polyline = Polyline( var polyline = Polyline(
@@ -1404,6 +1511,7 @@ class MapDriverController extends GetxController {
hours = durationToAdd.inHours; hours = durationToAdd.inHours;
minutes = (durationToAdd.inMinutes % 60).round(); minutes = (durationToAdd.inMinutes % 60).round();
calculateConsumptionFuel(); calculateConsumptionFuel();
updateLocation();
// cancelCheckRidefromPassenger(); // cancelCheckRidefromPassenger();
// checkIsDriverNearPassenger(); // checkIsDriverNearPassenger();
super.onInit(); 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": "البنك التجاري وفا مصر", "ATTIJARIWAFA BANK Egypt": "البنك التجاري وفا مصر",
"Morning Promo": "بونص الصباح", "Morning Promo": "بونص الصباح",
"Show my Cars": "عرض سياراتي", "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": "إضافة سيارة جديدة", "Add new car": "إضافة سيارة جديدة",
"You have gift 300 L.E": "لديك هدية بقيمة 300 جنيه.", "You have gift 300 L.E": "لديك هدية بقيمة 300 جنيه.",
// "VIP Order": "طلب VIP", // "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/controller/functions/crud.dart';
import 'package:sefer_driver/main.dart'; import 'package:sefer_driver/main.dart';
import '../../views/widgets/error_snakbar.dart';
class CaptainProfileController extends GetxController { class CaptainProfileController extends GetxController {
bool isLoading = false; bool isLoading = false;
TextEditingController vin = TextEditingController(); TextEditingController vin = TextEditingController();
@@ -15,6 +17,25 @@ class CaptainProfileController extends GetxController {
TextEditingController model = TextEditingController(); TextEditingController model = TextEditingController();
TextEditingController year = TextEditingController(); TextEditingController year = TextEditingController();
TextEditingController expirationDate = 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 { Future updateFields() async {
var payload = { var payload = {

View File

@@ -1,16 +1,15 @@
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart'; import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart'; import 'package:sefer_driver/views/widgets/my_textField.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart'; import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart'; import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import '../../constant/style.dart'; import '../../constant/style.dart';
import '../../controller/rate/rate_conroller.dart'; import '../../controller/rate/rate_conroller.dart';
// Changed: تم إعادة بناء الصفحة بالكامل لتحسين التصميم وتجربة المستخدم
class RatePassenger extends StatelessWidget { class RatePassenger extends StatelessWidget {
final RateController controller = Get.put(RateController()); final RateController controller = Get.put(RateController());
@@ -18,241 +17,313 @@ class RatePassenger extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MyScafolld( // New: استخدام Scaffold القياسي لهيكل أكثر قوة ومرونة
title: 'Rate Passenger'.tr, return Scaffold(
body: [ appBar: AppBar(
GetBuilder<RateController>(builder: (controller) { title: Text('Rate Passenger'.tr),
return Positioned( centerTitle: true,
top: 40, automaticallyImplyLeading: false, // New: إزالة سهم الرجوع
left: Get.width * .1, backgroundColor: Colors.white,
right: Get.width * .1, elevation: 1,
child: Container( ),
decoration: AppStyle.boxDecoration, // New: استخدام GetBuilder على مستوى الجسم لضمان تحديث الواجهة
child: Column( body: GetBuilder<RateController>(
children: [ builder: (controller) {
Padding( // New: استخدام SingleChildScrollView لتجنب مشاكل الـ overflow عند ظهور لوحة المفاتيح
padding: const EdgeInsets.all(4), return SingleChildScrollView(
child: Container( child: Padding(
height: Get.height * .25, padding: const EdgeInsets.all(16.0),
decoration: AppStyle.boxDecoration1, child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ // New: استدعاء ودجت منفصلة لكل قسم لزيادة التنظيم
Text( _buildPriceSummaryCard(context, controller),
'${'Total price from '.tr}${Get.find<MapDriverController>().passengerName}', const SizedBox(height: 16),
style: AppStyle.title,
), // New: قسم المحفظة يظهر فقط إذا لم يتم التحقق منه
Container( if (controller.walletChecked != 'true')
decoration: BoxDecoration( _buildWalletSection(context, controller),
border: Border.all(
width: 2, const SizedBox(height: 16),
color: AppColor.redColor,
)), _buildRatingSection(context, controller),
child: Padding(
padding: const EdgeInsets.all(4), const SizedBox(height: 24),
child: Text(
(double.parse(controller.price.toString()) - MyElevatedButton(
double.parse(controller.price title: 'Submit rating'.tr,
.toString()) * onPressed: () => controller.addRateToPassenger(),
.12) // New: جعل الزر يأخذ العرض الكامل لمزيد من الوضوح
.toStringAsFixed(2), // isFullWidth: true,
style: AppStyle.number.copyWith( ),
color: AppColor.redColor, ],
textBaseline: TextBaseline.ideographic, ),
decoration: TextDecoration.lineThrough, ),
decorationColor: AppColor.redColor), );
), },
), ),
), );
const SizedBox( }
height: 10,
), // New: ودجت منفصلة لعرض بطاقة ملخص السعر
Container( Widget _buildPriceSummaryCard(
decoration: BoxDecoration( BuildContext context, RateController controller) {
border: Border.all( final MapDriverController mapController = Get.find<MapDriverController>();
width: 2, final double originalPrice =
color: AppColor.greenColor, double.tryParse(controller.price.toString()) ?? 0.0;
)), final double priceAfterDiscount = originalPrice - (originalPrice * 0.12);
child: Padding(
padding: const EdgeInsets.all(4), return Card(
child: Text( elevation: 4,
Get.find<MapDriverController>() shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
.paymentAmount, child: Padding(
style: AppStyle.number, padding: const EdgeInsets.all(16.0),
), child: Column(
), children: [
), Text(
const SizedBox( '${'Trip Summary with'.tr} ${mapController.passengerName}',
height: 10, style: AppStyle.title
), .copyWith(fontSize: 18, fontWeight: FontWeight.bold),
Text( textAlign: TextAlign.center,
'Exclusive offers and discounts always with the Sefer app' ),
.tr, const Divider(height: 24, thickness: 1),
textAlign: TextAlign.center, Row(
style: AppStyle.title mainAxisAlignment: MainAxisAlignment.spaceBetween,
.copyWith(color: AppColor.redColor), children: [
) Text('Original Fare'.tr, style: AppStyle.title),
], Text(
)), priceAfterDiscount.toStringAsFixed(2),
), style: AppStyle.number.copyWith(
controller.walletChecked != 'true' fontSize: 16,
? controller.ispassengerWantWalletFromDriver color: AppColor.redColor,
? Container( decoration: TextDecoration.lineThrough,
decoration: AppStyle.boxDecoration1, ),
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),
),
),
MyElevatedButton(
title: "Press here".tr,
onPressed: () {
controller.addPassengerWallet();
},
)
],
),
),
)
: Container(
width: Get.width * .73,
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
Text(
"Would the passenger like to settle the remaining fare using their wallet?"
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
MyElevatedButton(
title: "Press here".tr,
onPressed: () {
controller.passengerWantPay();
},
)
],
),
)
: const SizedBox(),
const SizedBox(
height: 20,
),
Center(
child: RatingBar.builder(
initialRating: 0,
itemCount: 5,
itemSize: 50,
itemPadding: const EdgeInsets.symmetric(horizontal: 2),
itemBuilder: (context, index) {
switch (index) {
case 0:
return const Icon(
Icons.sentiment_very_dissatisfied,
color: Colors.red,
);
case 1:
return const Icon(
Icons.sentiment_dissatisfied,
color: Colors.redAccent,
);
case 2:
return const Icon(
Icons.sentiment_neutral,
color: Colors.amber,
);
case 3:
return const Icon(
Icons.sentiment_satisfied,
color: Colors.lightGreen,
);
case 4:
return const Icon(
Icons.sentiment_very_satisfied,
color: Colors.green,
);
default:
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(
maxLines: 4,
minLines: 1,
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
),
),
),
),
const SizedBox(
height: 20,
),
MyElevatedButton(
title: 'Submit rating'.tr,
onPressed: () => controller.addRateToPassenger())
],
), ),
)); ],
}), ),
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(
color: AppColor.greenColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.greenColor),
),
child: Text(
mapController.paymentAmount,
style: AppStyle.number
.copyWith(color: AppColor.greenColor, fontSize: 20),
),
),
],
),
const SizedBox(height: 12),
Text(
'Exclusive offers and discounts always with the Sefer app'.tr,
textAlign: TextAlign.center,
style: AppStyle.title
.copyWith(color: AppColor.redColor, fontSize: 13),
)
],
),
),
);
}
// New: ودجت منفصلة لقسم الدفع عبر المحفظة
Widget _buildWalletSection(BuildContext context, RateController controller) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: controller.ispassengerWantWalletFromDriver
? _buildAmountInput(controller)
: _buildWalletQuery(controller),
),
),
);
}
// 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?"
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
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();
},
),
),
],
)
], ],
isleading: false, );
}
// 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();
},
)
],
);
}
// 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: 4),
itemBuilder: (context, index) {
switch (index) {
case 0:
return const Icon(Icons.sentiment_very_dissatisfied,
color: Colors.red);
case 1:
return const Icon(Icons.sentiment_dissatisfied,
color: Colors.redAccent);
case 2:
return const Icon(Icons.sentiment_neutral,
color: Colors.amber);
case 3:
return const Icon(Icons.sentiment_satisfied,
color: Colors.lightGreen);
case 4:
return const Icon(Icons.sentiment_very_satisfied,
color: Colors.green);
default:
return const Icon(Icons.sentiment_neutral,
color: Colors.amber);
}
},
onRatingUpdate: (rating) {
controller.selectRateItem(rating);
},
),
const SizedBox(height: 24),
TextFormField(
maxLines: 4,
minLines: 2,
keyboardType: TextInputType.multiline,
controller: controller.comment,
decoration: InputDecoration(
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)),
),
),
),
],
),
),
); );
} }
} }
// 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

@@ -154,12 +154,13 @@ class AuthScreen extends StatelessWidget {
.formKey.currentState! .formKey.currentState!
.validate()) { .validate()) {
controller.logintest( controller.logintest(
controller controller
.emailController.text .passwordController.text
.trim(), .trim(),
controller controller
.passwordController.text .emailController.text
.trim()); .trim(),
);
} }
}, },
child: Text( 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:flutter/material.dart';
import 'package:get/get.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/lang/languages.dart';
import 'package:sefer_driver/views/widgets/my_scafold.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 '../../../auth/country_widget.dart';
import 'about_us.dart'; import 'about_us.dart';
import 'frequantly_question.dart'; import 'frequantly_question.dart';
@@ -18,140 +18,188 @@ class SettingsCaptain extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(SettingController()); // تحميل الـ Controllers المطلوبة
Get.put(HomePageController()); final settingsController = Get.put(SettingController());
final homeController = Get.put(HomePageController());
return MyScafolld( return MyScafolld(
title: 'Settings'.tr, title: 'Settings'.tr,
isleading: true,
body: [ body: [
ListView( ListView(
physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
children: <Widget>[ children: <Widget>[
// General Section // --- القسم الأول: عام ---
_buildSectionHeader('General'.tr), _buildSectionHeader('General'.tr, context),
CupertinoListSection( _buildSettingsCard(
margin: EdgeInsets.zero,
children: [ children: [
CupertinoListTile( _buildListTile(
leading: const Icon(CupertinoIcons.globe), icon: Icons.language_outlined,
title: Text('Language'.tr, style: AppStyle.headTitle2), title: 'Language'.tr,
subtitle: Text('You can change the language of the app'.tr, subtitle: 'Change the app language'.tr,
style: AppStyle.subtitle), onTap: () => Get.to(() => const Language()),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(const Language()),
), ),
CupertinoListTile( _buildListTile(
leading: const Icon(CupertinoIcons.flag_fill), icon: Icons.flag_outlined,
title: Text('Change Country'.tr, style: AppStyle.headTitle2), title: 'Change Country'.tr,
subtitle: Text( subtitle: 'Get features for your country'.tr,
'You can change the Country to get all features'.tr,
style: AppStyle.subtitle),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to( onTap: () => Get.to(
MyScafolld( () => MyScafolld(
title: 'Change Country'.tr, title: 'Change Country'.tr,
body: [CountryPickerFromSetting()], body: [CountryPickerFromSetting()],
isleading: true, isleading: true,
// isCupertino: true, // Indicate it's a Cupertino style page
), ),
), ),
), ),
], ],
), ),
const SizedBox(height: 20),
// App Preferences Section // --- القسم الثاني: تفضيلات التطبيق ---
_buildSectionHeader('App Preferences'.tr), _buildSectionHeader('App Preferences'.tr, context),
CupertinoListSection( _buildSettingsCard(
margin: EdgeInsets.zero,
children: [ children: [
CupertinoListTile( _buildSwitchTile(
leading: Icon( icon: Icons.map_outlined,
CupertinoIcons.map_pin_ellipse, color: AppColor.redColor,
color: AppColor.redColor, title: 'Google Map App'.tr,
), subtitle: 'Run Google Maps directly'.tr,
title: Text('Google Map App'.tr, style: AppStyle.headTitle2), controller: settingsController,
subtitle: Text( valueGetter: (ctrl) => (ctrl).isGoogleMapsEnabled,
'If you want to make Google Map App run directly when you apply order' onChanged: (ctrl) => (ctrl).onChangMapApp(),
.tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<SettingController>(
builder: (settingController) {
return CupertinoSwitch(
value: settingController.isGoogleMapsEnabled,
activeTrackColor: AppColor.primaryColor,
onChanged: (bool value) {
settingController.onChangMapApp();
},
);
},
),
), ),
CupertinoListTile( _buildSwitchTile(
leading: Icon(Icons.vibration), icon: Icons.vibration,
title: Text('Vibration'.tr, style: AppStyle.headTitle2), title: 'Vibration'.tr,
subtitle: Text( subtitle: 'Vibration feedback for buttons'.tr,
"You can change the vibration feedback for all buttons".tr, controller: homeController,
style: AppStyle.subtitle, valueGetter: (ctrl) => (ctrl).isVibrate,
), onChanged: (ctrl) => (ctrl)
trailing: GetBuilder<HomePageController>( .changeVibrateOption(true), // قد تحتاج لتعديل الدالة
builder: (controller) => CupertinoSwitch(
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
activeTrackColor: AppColor.primaryColor,
),
),
onTap: () => print('3'),
), ),
], ],
), ),
const SizedBox(height: 20),
// Help & Support Section // --- القسم الثالث: المساعدة والدعم ---
_buildSectionHeader('Help & Support'.tr), _buildSectionHeader('Help & Support'.tr, context),
CupertinoListSection( _buildSettingsCard(
margin: EdgeInsets.zero,
children: [ children: [
CupertinoListTile( _buildListTile(
leading: const Icon(CupertinoIcons.question_circle_fill), icon: Icons.quiz_outlined,
title: Text('Frequently Questions'.tr, title: 'Frequently Questions'.tr,
style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const FrequentlyQuestionsPage()), onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
), ),
CupertinoListTile( _buildListTile(
leading: const Icon(CupertinoIcons.hand_raised_fill), icon: Icons.support_agent,
title: title: "How to use App".tr,
Text("How to use Intaleq".tr, style: AppStyle.headTitle2),
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const UsingAppPage()), onTap: () => Get.to(() => const UsingAppPage()),
), ),
CupertinoListTile( _buildListTile(
leading: const Icon(CupertinoIcons.info_circle_fill), icon: Icons.info_outline,
title: Text('About Us'.tr, style: AppStyle.headTitle2), title: 'About Us'.tr,
trailing: const CupertinoListTileChevron(),
onTap: () => Get.to(() => const AboutPage()), 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( 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( child: Text(
title, title,
style: const TextStyle( style: Theme.of(context).textTheme.titleSmall?.copyWith(
fontSize: 17.0, color: Colors.grey.shade600,
fontWeight: FontWeight.w600, fontWeight: FontWeight.bold,
color: CupertinoColors.secondaryLabel, ),
),
), ),
); );
} }
// ويدجت لبناء بطاقة الإعدادات
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:flutter/material.dart';
import 'package:get/get.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 { class UsingAppPage extends StatelessWidget {
const UsingAppPage({super.key}); const UsingAppPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MyScafolld( // 2. تجهيز قائمة البيانات بشكل منظم
title: "How to use Intaleq".tr, final List<FaqItem> faqItems = [
body: [ FaqItem(
SizedBox( question: "What are the order details we provide to you?".tr,
child: Padding( icon: Icons.receipt_long_outlined,
padding: const EdgeInsets.all(8.0), answer: Padding(
child: ListView( padding: const EdgeInsets.all(8.0),
children: [ child: Image.network(
InkWell( 'https://api.tripz-egypt.com/tripz/imageForUsingApp/order_page.jpg',
onTap: () { fit: BoxFit.cover,
MyDialogContent().getDialog( // يمكنك إضافة مؤشر تحميل هنا
"What are the order details we provide to you?".tr, loadingBuilder: (context, child, loadingProgress) {
Image.network( if (loadingProgress == null) return child;
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg', return const Center(child: CircularProgressIndicator());
height: 300, },
width: 300, errorBuilder: (context, error, stackTrace) {
fit: BoxFit.cover, return const Center(
), () { child:
Get.back(); 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,
'''Intaleq Wallet Features:
Transfer money multiple times.
Transfer to anyone.
Make purchases.
Charge your account.
Charge a friend's Intaleq account.
Store your money with us and receive it in your bank as a monthly salary.'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is the feature of our wallet?".tr,
style: AppStyle.title,
),
),
),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What is Types of Trips in Intaleq?".tr,
'''Types of Trips in Intaleq:
Comfort: For cars newer than 2017 with air conditioning.
Lady: For girl drivers.
Speed: For fixed salary and endpoints.
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
Raih Gai: For same-day return trips longer than 50km.
'''
.tr, () {
Get.back();
});
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What is Types of Trips in Intaleq?".tr,
style: AppStyle.title,
),
),
),
),
],
),
), ),
) ),
], ),
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,
style:
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
),
),
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,
style:
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
),
),
];
// 3. بناء الواجهة الرسومية باستخدام البيانات
return MyScafolld(
title: "How to use App".tr, // تم تغيير العنوان ليكون أعم
isleading: true, 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/passenger_info_window.dart';
import 'mapDriverWidgets/sos_connect.dart'; import 'mapDriverWidgets/sos_connect.dart';
// Changed: تم إعادة بناء الصفحة بالكامل لتكون أكثر تنظيمًا
class PassengerLocationMapPage extends StatelessWidget { class PassengerLocationMapPage extends StatelessWidget {
PassengerLocationMapPage({super.key}); PassengerLocationMapPage({super.key});
final LocationController locationController = Get.put(LocationController()); final LocationController locationController = Get.put(LocationController());
@@ -21,36 +22,44 @@ class PassengerLocationMapPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) { // New: استخدام addPostFrameCallback لضمان أن تحميل البيانات يتم بعد بناء الواجهة
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول WidgetsBinding.instance.addPostFrameCallback((_) {
// هذا يعطي GetX وقته لتجهيز كل شيء if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
WidgetsBinding.instance.addPostFrameCallback((_) {
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
mapDriverController.argumentLoading(); mapDriverController.argumentLoading();
}); mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
} else { } else {
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ // في حال عدم وجود arguments، يتم التعامل مع هذا الخطأ
WidgetsBinding.instance.addPostFrameCallback((_) {
Get.snackbar("Error", "No order data found."); Get.snackbar("Error", "No order data found.");
Get.back(); Get.back();
}); }
} });
mapDriverController.argumentLoading();
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
return Scaffold( return Scaffold(
// backgroundColor: AppColor.blueColor,
// title: 'Map Passenger'.tr,
body: SafeArea( body: SafeArea(
child: Stack( child: Stack(
children: [ children: [
// 1. الخريطة في الخلفية
GoogleDriverMap(locationController: locationController), GoogleDriverMap(locationController: locationController),
const PassengerInfoWindow(),
// 2. شريط تعليمات الطريق في الأعلى
const InstructionsOfRoads(),
// 3. زر إلغاء الرحلة في الأعلى يسارًا
CancelWidget(mapDriverController: mapDriverController), CancelWidget(mapDriverController: mapDriverController),
// 4. نافذة معلومات الراكب في الأسفل (تظهر قبل بدء الرحلة)
const PassengerInfoWindow(),
// 5. شريط معلومات وإنهاء الرحلة (يظهر بعد بدء الرحلة)
driverEndRideBar(), driverEndRideBar(),
// 6. أزرار الطوارئ والاتصال
const SosConnect(), const SosConnect(),
// 7. دائرة عرض السرعة
speedCircle(), speedCircle(),
// const GoogleMapApp(),
// 8. نافذة عرض السعر النهائي (تظهر بعد انتهاء الرحلة)
const PricesWindow(), 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 { class CancelWidget extends StatelessWidget {
const CancelWidget({ const CancelWidget({
super.key, super.key,
@@ -70,59 +132,72 @@ class CancelWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Positioned( return Positioned(
top: 10, top: 10,
left: 5, left: 10,
child: GestureDetector( child: GetBuilder<MapDriverController>(
onTap: () { builder: (controller) {
Get.defaultDialog( // يظهر زر الإلغاء فقط قبل انتهاء الرحلة
title: "Are you sure you want to cancel this trip?".tr, if (controller.isRideFinished) return const SizedBox.shrink();
titleStyle: AppStyle.title,
content: Column( return GestureDetector(
children: [ onTap: () {
Text("Why do you want to cancel this trip?".tr), Get.defaultDialog(
Form( title: "Are you sure you want to cancel this trip?".tr,
key: mapDriverController.formKeyCancel, titleStyle: AppStyle.title,
child: MyTextForm( content: Column(
controller: mapDriverController.cancelTripCotroller, children: [
label: "Write the reason for canceling the trip".tr, Text("Why do you want to cancel this trip?".tr),
hint: "Write the reason for canceling the trip".tr, Form(
type: TextInputType.name, key: mapDriverController.formKeyCancel,
)) child: MyTextForm(
controller: mapDriverController.cancelTripCotroller,
label: "Write the reason for canceling the trip".tr,
hint: "Write the reason for canceling the trip".tr,
type: TextInputType.name,
))
],
),
confirm: MyElevatedButton(
title: 'Ok'.tr,
kolor: AppColor.redColor,
onPressed: () async {
await mapDriverController
.cancelTripFromDriverAfterApplied();
Get.back();
}),
cancel: MyElevatedButton(
title: 'No'.tr,
onPressed: () {
Get.back();
}));
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 5,
),
], ],
), ),
confirm: MyElevatedButton( child: const Padding(
title: 'Ok'.tr, padding: EdgeInsets.all(8.0),
kolor: AppColor.redColor, child: Icon(
onPressed: () async { Icons.clear,
// todo add cancel and inform passenger to get new driver size: 30,
await mapDriverController color: AppColor.redColor,
.cancelTripFromDriverAfterApplied(); ),
Get.back(); ),
}),
cancel: MyElevatedButton(
title: 'No'.tr,
// kolor: AppColor.redColor,
onPressed: () {
Get.back();
}));
},
child: Container(
decoration: BoxDecoration(
color: AppColor.redColor,
borderRadius: BorderRadius.circular(15)),
child: const Padding(
padding: EdgeInsets.all(3),
child: Icon(
Icons.clear,
size: 40,
color: AppColor.secondaryColor,
), ),
), );
), },
), ),
); );
} }
} }
// Changed: تم تعديل تصميم نافذة السعر لتكون أكثر وضوحًا
class PricesWindow extends StatelessWidget { class PricesWindow extends StatelessWidget {
const PricesWindow({ const PricesWindow({
super.key, super.key,
@@ -132,38 +207,43 @@ class PricesWindow extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<MapDriverController>(builder: (mapDriverController) { return GetBuilder<MapDriverController>(builder: (mapDriverController) {
return mapDriverController.isPriceWindow return mapDriverController.isPriceWindow
? Positioned( ? Container(
bottom: Get.height * 1.2, color: Colors.black.withOpacity(0.5),
// top: Get.height * 3, child: Center(
left: Get.height * 1, child: Container(
right: Get.height * 1, width: Get.width * 0.8,
child: Container( padding: const EdgeInsets.all(24),
height: Get.height * 3, decoration: BoxDecoration(
decoration: AppStyle.boxDecoration1, color: Colors.white,
child: Column( borderRadius: BorderRadius.circular(20),
children: [ ),
Container( child: Column(
decoration: AppStyle.boxDecoration1, mainAxisSize: MainAxisSize.min,
child: Padding( children: [
padding: const EdgeInsets.all(3), Text(
child: Text( 'Total Price is '.tr,
'Total Price is '.tr, style: AppStyle.headTitle2,
style: AppStyle.headTitle2, textAlign: TextAlign.center,
textAlign: TextAlign.center, ),
), const SizedBox(height: 10),
)), Text(
const SizedBox( '${mapDriverController.totalPricePassenger} ${'\$'.tr}',
height: 20, style: AppStyle.headTitle2.copyWith(
), color: AppColor.primaryColor, fontSize: 36),
MyElevatedButton( ),
title: 'ok'.tr, const SizedBox(
onPressed: () => height: 20,
Get.to(() => RatePassenger(), arguments: { ),
'rideId': mapDriverController.rideId, MyElevatedButton(
'passengerId': mapDriverController.passengerId, title: 'ok'.tr,
'driverId': mapDriverController.driverId onPressed: () =>
})) Get.to(() => RatePassenger(), arguments: {
], 'rideId': mapDriverController.rideId,
'passengerId': mapDriverController.passengerId,
'driverId': mapDriverController.driverId
}))
],
),
), ),
), ),
) )

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,263 +1,491 @@
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/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:slide_to_act/slide_to_act.dart'; import 'package:slide_to_act/slide_to_act.dart';
import 'package:vibration/vibration.dart'; import 'package:vibration/vibration.dart';
import 'dart:io';
import '../../../../constant/colors.dart'; import '../../../../constant/colors.dart';
import '../../../../constant/style.dart'; import '../../../../constant/style.dart';
import '../../../../controller/home/captin/map_driver_controller.dart'; import '../../../../controller/home/captin/map_driver_controller.dart';
import '../../../widgets/elevated_btn.dart'; import '../../../widgets/elevated_btn.dart';
// Changed: إعادة تصميم كاملة للشريط ليصبح شريطًا علويًا عند بدء الرحلة
GetBuilder<MapDriverController> driverEndRideBar() { GetBuilder<MapDriverController> driverEndRideBar() {
return GetBuilder<MapDriverController>( return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted builder: (controller) => AnimatedPositioned(
? Positioned( duration: const Duration(milliseconds: 300),
left: 5, // New: يظهر الشريط من الأعلى عندما تبدأ الرحلة
top: 5, top: controller.isRideStarted ? 0 : -200,
right: 5, left: 0,
child: Container( right: 0,
decoration: AppStyle.boxDecoration1.copyWith( child: Card(
borderRadius: BorderRadius.circular(15), margin: EdgeInsets.zero,
boxShadow: [ elevation: 10,
BoxShadow( shape: const RoundedRectangleBorder(
color: Colors.black.withOpacity(0.1), borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)),
blurRadius: 10, ),
offset: Offset(0, 5), child: Padding(
), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
], child: Column(
), children: [
padding: const EdgeInsets.all(10), // -- معلومات الرحلة --
height: mapDriverController.remainingTimeTimerRideBegin < 60 if (controller.carType != 'Mishwar Vip')
? mapDriverController.driverEndPage = 190 Row(
: mapDriverController.carType == 'Mishwar Vip' mainAxisAlignment: MainAxisAlignment.spaceEvenly,
? 120 children: [
: 170, _buildInfoColumn(
child: Column( icon: Icons.social_distance,
mainAxisAlignment: MainAxisAlignment.spaceAround, text: '${controller.distance} ${'KM'.tr}',
children: [ label: 'Distance'.tr,
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' && _buildInfoColumn(
mapDriverController.carType != 'Awfar Car' && icon: Icons.timelapse,
mapDriverController.carType != 'Scooter') text: controller.hours > 1
Row( ? '${controller.hours}h ${controller.minutes}m'
mainAxisAlignment: MainAxisAlignment.spaceAround, : '${controller.minutes}m',
children: [ label: 'Time'.tr,
_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(), _buildInfoColumn(
Container( icon: Icons.money_sharp,
width: Get.width * 0.8, text: '${controller.paymentAmount} ${'\$'.tr}',
decoration: BoxDecoration( label: 'Price'.tr,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppColor.redColor.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
),
],
), ),
child: SlideAction( ],
height: 50, ),
borderRadius: 15, if (controller.carType != 'Mishwar Vip')
elevation: 4, const Divider(height: 20),
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({ _builtTimerAndCarType(),
super.key,
});
@override const SizedBox(height: 12),
Widget build(BuildContext context) {
final mapDriverController = Get.find<MapDriverController>(); // -- زر إنهاء الرحلة المنزلق --
return Row( SlideAction(
mainAxisAlignment: MainAxisAlignment.spaceAround, height: 55,
children: [ borderRadius: 15,
Container( elevation: 4,
decoration: AppStyle.boxDecoration1.copyWith( text: 'Slide to End Trip'.tr,
boxShadow: [ textStyle: AppStyle.title.copyWith(
BoxShadow( fontSize: 18,
color: AppColor.accentColor.withOpacity(0.2), fontWeight: FontWeight.bold,
blurRadius: 8, color: Colors.white,
offset: Offset(0, 4), ),
outerColor: AppColor.redColor,
innerColor: Colors.white,
sliderButtonIcon: const Icon(
Icons.arrow_forward_ios,
color: AppColor.redColor,
size: 24,
),
sliderRotate: false,
onSubmit: () {
HapticFeedback.mediumImpact();
controller.finishRideFromDriver();
return null; // New: onSubmit now returns null
},
), ),
], ],
), ),
),
),
),
);
}
// 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();
@override
Widget build(BuildContext context) {
final controller = Get.find<MapDriverController>();
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// -- نوع السيارة --
Container(
decoration: AppStyle.boxDecoration1.copyWith(color: Colors.grey[200]),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text( child: Text(
mapDriverController.carType, controller.carType,
style: AppStyle.title, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
), ),
), ),
if (mapDriverController.carType != 'Comfort' && // -- مؤقت الرحلة --
mapDriverController.carType != 'Mishwar Vip' && if (controller.carType != 'Comfort' &&
mapDriverController.carType != 'Lady') controller.carType != 'Mishwar Vip' &&
Container( controller.carType != 'Lady') ...[
width: Get.width * 0.6, const SizedBox(width: 10),
decoration: BoxDecoration( Expanded(
borderRadius: BorderRadius.circular(12), child: Container(
gradient: LinearGradient( height: 40,
colors: [ decoration: BoxDecoration(
mapDriverController.remainingTimeTimerRideBegin < 60 borderRadius: BorderRadius.circular(12),
? AppColor.redColor.withOpacity(0.8) gradient: LinearGradient(
: AppColor.greenColor.withOpacity(0.8), colors: [
mapDriverController.remainingTimeTimerRideBegin < 60 controller.remainingTimeTimerRideBegin < 60
? AppColor.redColor ? AppColor.redColor.withOpacity(0.8)
: AppColor.greenColor, : AppColor.greenColor.withOpacity(0.8),
], controller.remainingTimeTimerRideBegin < 60
begin: Alignment.centerLeft, ? AppColor.redColor
end: Alignment.centerRight, : AppColor.greenColor,
), ],
boxShadow: [
BoxShadow(
color: (mapDriverController.remainingTimeTimerRideBegin < 60
? AppColor.redColor
: AppColor.greenColor)
.withOpacity(0.3),
blurRadius: 8,
offset: Offset(0, 4),
), ),
], ),
), child: ClipRRect(
child: ClipRRect( borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(12), child: Stack(
child: Stack( alignment: Alignment.center,
children: [ children: [
LinearProgressIndicator( LinearProgressIndicator(
backgroundColor: Colors.white.withOpacity(0.2), backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>( valueColor: AlwaysStoppedAnimation<Color>(
Colors.white.withOpacity(0.5), Colors.white.withOpacity(0.2)),
minHeight: 40,
value: controller.progressTimerRideBegin.toDouble(),
), ),
minHeight: 40, Text(
value: controller.stringRemainingTimeRideBegin,
mapDriverController.progressTimerRideBegin.toDouble(),
),
Center(
child: AnimatedDefaultTextStyle(
duration: Duration(milliseconds: 300),
style: AppStyle.title.copyWith( style: AppStyle.title.copyWith(
color: Colors.white, color: Colors.white, fontWeight: FontWeight.bold),
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}) { // Changed: تم تعديل مكان ومظهر دائرة السرعة
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() { GetBuilder<MapDriverController> speedCircle() {
if (Get.find<MapDriverController>().speed > 100) { if (Get.find<MapDriverController>().speed > 100) {
if (Platform.isIOS) { if (Platform.isIOS) {
@@ -278,25 +506,36 @@ GetBuilder<MapDriverController> speedCircle() {
); );
} }
return GetBuilder<MapDriverController>( return GetBuilder<MapDriverController>(
builder: (mapDriverController) { builder: (controller) {
return mapDriverController.isRideStarted return controller.isRideStarted
? Positioned( ? Positioned(
// New: تم وضع دائرة السرعة في الأسفل يمينًا
bottom: 25, bottom: 25,
right: 100, left: 16,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: mapDriverController.speed > 100 color: Colors.white,
? Colors.red boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
: AppColor.secondaryColor, border: Border.all(
border: Border.all(width: 3, color: AppColor.redColor), width: 4,
color: controller.speed > 100
? Colors.red
: AppColor.greenColor,
),
), ),
height: 60, height: 70,
width: 60, width: 70,
child: Center( child: Center(
child: Text( child: Column(
mapDriverController.speed.toStringAsFixed(0), mainAxisAlignment: MainAxisAlignment.center,
style: AppStyle.number, 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:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -15,93 +129,88 @@ class GoogleDriverMap extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(MapDriverController()); final MapDriverController controller = Get.put(MapDriverController());
return Padding(
padding: const EdgeInsets.all(8.0), // New: تحديد قيمة الـ padding لتحريك مركز الخريطة للأعلى
child: GetBuilder<MapDriverController>( final double mapPaddingBottom = MediaQuery.of(context).size.height * 0.3;
builder: (controller) => Column(
children: [ return GetBuilder<MapDriverController>(
SizedBox( builder: (controller) => GoogleMap(
height: Get.height * .92, onMapCreated: (mapController) {
child: GoogleMap( controller.onMapCreated(mapController);
onMapCreated: controller.onMapCreated, // New: تطبيق الـ padding بعد إنشاء الخريطة مباشرة
zoomControlsEnabled: true, mapController.setMapStyle('[]'); // يمكنك إضافة تصميم مخصص للخريطة هنا
initialCameraPosition: CameraPosition( // يمكنك استخدام CameraUpdate.scrollBy لتحريك الكاميرا إذا رغبت بذلك:
target: locationController.myLocation, // controller.mapController!.animateCamera(CameraUpdate.scrollBy(0, mapPaddingBottom));
zoom: 13, },
bearing: locationController.heading, // New: إضافة padding لتحريك مركز الخريطة للأعلى، مما يجعل أيقونة السائق تظهر في الأسفل
tilt: 40,
), zoomControlsEnabled: false, // Changed: تم إخفاء أزرار الزوم الافتراضية
cameraTargetBounds: initialCameraPosition: CameraPosition(
CameraTargetBounds.unbounded, // Allow unrestricted movement target: locationController.myLocation,
onCameraMove: (position) { zoom: 17,
CameraPosition( bearing: locationController.heading, // استخدام اتجاه السائق
target: locationController.myLocation, tilt: 60, // زاوية ميل
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,
),
},
),
),
],
), ),
onCameraMove: (position) {
CameraPosition(
target: locationController.myLocation,
zoom: 17.5,
tilt: 50.0,
bearing: locationController.heading,
);
},
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,
trafficEnabled: true, // Changed: تفعيل عرض حركة المرور
buildingsEnabled: true,
polylines: {
Polyline(
zIndex: 2,
polylineId: const PolylineId('route1'),
points: controller.polylineCoordinates,
color: const Color.fromARGB(255, 163, 81, 246),
width: 6, // Changed: زيادة عرض الخط
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
Polyline(
zIndex: 2,
polylineId: const PolylineId('route'),
points: controller.polylineCoordinatesDestination,
color: const Color.fromARGB(255, 10, 29, 126),
width: 6, // Changed: زيادة عرض الخط
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
},
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: locationController.myLocation,
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,
icon: controller.startIcon,
),
Marker(
markerId: MarkerId('end'.tr),
position: controller.latLngPassengerDestination,
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:sefer_driver/views/widgets/mydialoug.dart';
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.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:get/get.dart';
import 'package:sefer_driver/constant/colors.dart'; import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/info.dart'; import 'package:sefer_driver/constant/info.dart';
@@ -15,373 +9,97 @@ import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/box_name.dart'; import '../../../../constant/box_name.dart';
import '../../../../constant/style.dart'; import '../../../../constant/style.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../main.dart'; import '../../../../main.dart';
import '../../../../print.dart';
// Changed: إعادة تصميم كاملة لتصبح شريط معلومات علوي مدمج
class PassengerInfoWindow extends StatelessWidget { class PassengerInfoWindow extends StatelessWidget {
const PassengerInfoWindow({super.key}); const PassengerInfoWindow({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<MapDriverController>( return GetBuilder<MapDriverController>(
builder: (controller) => controller.isPassengerInfoWindow == true builder: (controller) => AnimatedPositioned(
? Positioned( duration: const Duration(milliseconds: 400),
bottom: 10, curve: Curves.easeInOut,
left: 10, // Changed: تم تغيير الموضع من الأسفل إلى الأعلى
right: 10, top: controller.isPassengerInfoWindow ? 15.0 : -200.0,
child: Card( left: 15.0,
elevation: 5, right: 15.0,
shape: RoundedRectangleBorder( child: Card(
borderRadius: BorderRadius.circular(15), elevation: 8,
), shadowColor: Colors.black.withOpacity(0.3),
child: Padding( shape: RoundedRectangleBorder(
padding: const EdgeInsets.all(16.0), borderRadius: BorderRadius.circular(16),
child: Column( ),
mainAxisSize: MainAxisSize.min, child: Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding:
children: [ const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
Row( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( // New: صف علوي للمعلومات الأساسية
'Go to passenger Location'.tr, _buildTopInfoRow(controller),
style: AppStyle.title.copyWith( const Divider(height: 16),
color: AppColor.greenColor,
fontWeight: FontWeight.bold, // Changed: الأزرار الآن في صف أفقي ومدمج
), if (!controller.isRideBegin) _buildActionButtons(controller),
),
if (!controller.isRideBegin) // New: مؤشر انتظار الراكب المدمج
Wrap( if (controller.remainingTimeInPassengerLocatioWait < 300 &&
spacing: 16.0, controller.remainingTimeInPassengerLocatioWait != 0 &&
children: [ !controller.isRideBegin) ...[
IconButton( const SizedBox(height: 10),
onPressed: () async { _buildWaitingIndicator(controller),
controller.isSocialPressed = true; ],
await controller.driverCallPassenger();
makePhoneCall( // زر الإلغاء بعد انتهاء وقت الانتظار
controller.passengerPhone.toString()); if (controller.isdriverWaitTimeEnd &&
}, !controller.isRideBegin) ...[
icon: const Icon( const SizedBox(height: 10),
Icons.phone, _buildCancelAfterWaitButton(controller),
color: AppColor.blueColor, ]
), ],
tooltip: 'Call Passenger', ),
), ),
IconButton( ),
onPressed: () { ),
Get.bottomSheet( );
backgroundColor: Colors.white, }
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical( // New: ودجت لعرض المعلومات العلوية بشكل مدمج
top: Radius.circular(20)), Widget _buildTopInfoRow(MapDriverController controller) {
), return Row(
Padding( mainAxisAlignment: MainAxisAlignment.spaceBetween,
padding: const EdgeInsets.all(16.0), children: [
child: _buildMessageOptions(controller), // معلومات الراكب
), Expanded(
); child: Column(
}, crossAxisAlignment: CrossAxisAlignment.start,
icon: const Icon( children: [
Icons.message, Text(
color: AppColor.redColor, 'Go to passenger:'.tr,
), style: AppStyle.title
tooltip: 'Send Message', .copyWith(color: Colors.grey[600], fontSize: 13),
),
IconButton(
onPressed: () async {
if (Platform.isAndroid) {
Bubble().startBubbleHead(
sendAppToBackground: true);
}
await controller
.openGoogleMapFromDriverToPassenger();
},
icon: const Icon(
MaterialCommunityIcons.map_marker_radius,
size: 28,
color: AppColor.blueColor,
),
tooltip: 'Open in Maps',
),
],
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.timer,
text: controller.hours > 1
? '${controller.hours}h ${controller.minutes}m'
: '${controller.minutes}m',
label: 'Duration',
),
_buildInfoTile(
icon: Icons.map,
text: '${controller.distance} km',
label: 'Distance',
),
_buildInfoTile(
icon: Icons.person,
text: controller.passengerName,
label: 'Passenger',
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoTile(
icon: Icons.attach_money,
text: controller.totalPricePassenger,
label: 'Cost',
),
_buildInfoTile(
icon: Icons.directions_car,
text: controller.carType.tr,
label: 'Car Type',
),
],
),
const SizedBox(height: 16),
if (!controller.isRideBegin)
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: MyElevatedButton(
title: 'Start the Ride'.tr,
kolor: AppColor.greenColor,
onPressed: () {
MyDialog().getDialog(
"Is the Passenger in your Car?".tr,
"Don't start trip if passenger not in your car"
.tr,
() async {
await controller
.startRideFromDriver();
Get.back();
},
);
},
),
),
const SizedBox(width: 8),
if (controller.isArrivedSend)
Expanded(
child: MyElevatedButton(
title: 'I Arrive'.tr,
kolor: AppColor.yellowColor,
onPressed: () async {
if (await controller
.calculateDistanceBetweenDriverAndPassengerLocation() <
140) {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'Hi ,I Arrive your site',
'I Arrive at your site'.tr,
controller.tokenPassenger,
[],
'ding.wav',
);
controller
.startTimerToShowDriverWaitPassengerDuration();
controller.isArrivedSend = false;
} else {
MyDialog().getDialog(
'You are not near the passenger location'
.tr,
'Please go to the pickup location exactly'
.tr, () {
Get.back();
});
}
},
),
),
],
),
const SizedBox(height: 12),
if (controller.remainingTimeInPassengerLocatioWait <
300 &&
controller
.remainingTimeInPassengerLocatioWait !=
0)
Stack(
alignment: Alignment.center,
children: [
LinearProgressIndicator(
backgroundColor: AppColor.greyColor,
color: controller
.remainingTimeInPassengerLocatioWait <
60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 20,
borderRadius: BorderRadius.circular(10),
value: controller
.progressInPassengerLocationFromDriver
.toDouble(),
),
Text(
controller
.stringRemainingTimeWaitingPassenger,
style: AppStyle.title,
),
],
),
const SizedBox(height: 12),
if (controller.isdriverWaitTimeEnd)
MyElevatedButton(
title:
'You Can Cancel the Trip and get Cost From '
.tr +
AppInformation.appName.tr,
kolor: AppColor.deepPurpleAccent,
onPressed: () {
MyDialog().getDialog(
'Are you sure to cancel?'.tr, '',
() async {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'Driver Cancelled Your Trip',
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
.tr,
controller.tokenPassenger,
[],
'cancel.wav',
);
Log.print(
'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}');
box.write(BoxName.rideStatus, 'Cancel');
await controller
.addWaitingTimeCostFromPassengerToDriverWallet();
controller.isdriverWaitTimeEnd = false;
Get.back();
});
},
),
],
),
],
),
),
), ),
) Text(
: const SizedBox(), controller.passengerName,
); style: AppStyle.title
} .copyWith(fontWeight: FontWeight.bold, fontSize: 18),
overflow: TextOverflow.ellipsis,
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( Row(
children: [ children: [
Expanded( _buildInfoChip(Icons.map_outlined, '${controller.distance} km'),
child: Form( const SizedBox(width: 8),
key: controller.formKey2, _buildInfoChip(
child: MyTextForm( Icons.timer_outlined,
controller: controller.messageToPassenger, controller.hours > 1
label: 'Type something'.tr, ? '${controller.hours}h ${controller.minutes}m'
hint: 'Type something'.tr, : '${controller.minutes}m',
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),
), ),
], ],
), ),
@@ -389,19 +107,145 @@ class PassengerInfoWindow extends StatelessWidget {
); );
} }
Widget _buildMessageTile( // New: ودجت مخصص لعرض المعلومات بشكل أنيق
{required String text, required VoidCallback onTap}) { Widget _buildInfoChip(IconData icon, String text) {
return InkWell( return Container(
onTap: onTap, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Container( decoration: BoxDecoration(
padding: const EdgeInsets.all(12), color: AppColor.primaryColor.withOpacity(0.1),
margin: const EdgeInsets.symmetric(vertical: 4), borderRadius: BorderRadius.circular(20),
decoration: BoxDecoration( ),
borderRadius: BorderRadius.circular(8), child: Row(
color: Colors.grey[100], mainAxisSize: MainAxisSize.min,
), children: [
child: Text(text, style: AppStyle.title), Icon(icon, color: AppColor.primaryColor, size: 16),
const SizedBox(width: 4),
Text(text,
style: TextStyle(
color: AppColor.primaryColor, fontWeight: FontWeight.bold)),
],
), ),
); );
} }
// Changed: إعادة تصميم أزرار الإجراءات لتكون أكثر دمجًا
Widget _buildActionButtons(MapDriverController controller) {
return Row(
children: [
if (controller.isArrivedSend)
Expanded(
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() <
140) {
Get.find<FirebaseMessagesController>()
.sendNotificationToDriverMAP(
'Hi ,I Arrive your site',
'I Arrive at your site'.tr,
controller.tokenPassenger,
[],
'ding.wav',
);
controller.startTimerToShowDriverWaitPassengerDuration();
controller.isArrivedSend = false;
} else {
MyDialog().getDialog(
'You are not near the passenger location'.tr,
'Please go to the pickup location exactly'.tr,
() => Get.back());
}
},
),
),
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)),
),
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.withOpacity(0.3),
color: controller.remainingTimeInPassengerLocatioWait < 60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 25,
value: controller.progressInPassengerLocationFromDriver.toDouble(),
),
Text(
controller.stringRemainingTimeWaitingPassenger,
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13,
shadows: [
Shadow(color: Colors.black.withOpacity(0.5), blurRadius: 2)
]),
),
],
),
);
}
// 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(
'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');
box.write(BoxName.rideStatus, 'Cancel');
await controller.addWaitingTimeCostFromPassengerToDriverWallet();
controller.isdriverWaitTimeEnd = false;
Get.back();
});
},
);
}
} }

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

View File

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

View File

@@ -36,7 +36,7 @@ class BehaviorPage extends StatelessWidget {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
children: [ children: [
const Text("Overall Behavior Score", Text("Overall Behavior Score".tr,
style: TextStyle( style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)), fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 10), const SizedBox(height: 10),
@@ -49,7 +49,7 @@ class BehaviorPage extends StatelessWidget {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text("Last 10 Trips", Text("Last 10 Trips".tr,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 10), const SizedBox(height: 10),
ListView.builder( ListView.builder(
@@ -70,10 +70,12 @@ class BehaviorPage extends StatelessWidget {
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Behavior Score: ${trip['behavior_score']}"), Text(
Text("Max Speed: ${trip['max_speed']} km/h"), "${'Behavior Score'.tr}: ${trip['behavior_score']}"),
Text("Hard Brakes: ${trip['hard_brakes']}"), Text("${'Max Speed'.tr}: ${trip['max_speed']} km/h"),
Text("Distance: ${trip['total_distance']} km"), 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:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:sefer_driver/controller/profile/captain_profile_controller.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 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../my_wallet/walet_captain.dart'; import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'behavior_page.dart'; import 'behavior_page.dart';
import 'captains_cars.dart'; import 'captains_cars.dart';
// الصفحة الرئيسية الجديدة
class ProfileCaptain extends StatelessWidget { class ProfileCaptain extends StatelessWidget {
ProfileCaptain({super.key}); const ProfileCaptain({super.key});
CaptainWalletController captainWalletController = CaptainWalletController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(CaptainProfileController()); // Get.put() يجب أن يكون في مكان يتم استدعاؤه مرة واحدة فقط،
// لكن سنبقيه هنا حسب الكود الأصلي
final controller = Get.put(CaptainProfileController());
return MyScafolld( return MyScafolld(
title: 'My Profile'.tr, title: 'My Profile'.tr,
isleading: true,
body: [ body: [
GetBuilder<CaptainProfileController>( GetBuilder<CaptainProfileController>(
builder: (controller) => Padding( builder: (controller) {
padding: const EdgeInsets.all(16.0), if (controller.isLoading) {
child: SingleChildScrollView( return const Center(child: MyCircularProgressIndicator());
child: Center( }
child: controller.isLoading if (controller.captainProfileData.isEmpty) {
? const MyCircularProgressIndicator() return Center(child: Text('Failed to load profile data.'.tr));
: Column( }
children: [ return SingleChildScrollView(
Material( padding:
elevation: 2, const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
borderRadius: BorderRadius.circular(8), child: Column(
child: Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceAround, // 1. رأس الصفحة: صورة واسم وتقييم
children: [ ProfileHeader(
InkWell( name:
onTap: () async { '${controller.captainProfileData['first_name'] ?? ''} ${controller.captainProfileData['last_name'] ?? ''}',
// addBankCodeEgypt(captainWalletController); rating:
}, controller.captainProfileData['ratingDriver'] != null
borderRadius: BorderRadius.circular(8), ? double.tryParse(controller
child: Padding( .captainProfileData['ratingDriver']
padding: const EdgeInsets.symmetric( .toString()) ??
vertical: 16, horizontal: 16), 0.0
child: Text( : 0.0,
'Add bank Account'.tr, ratingCount: int.tryParse(controller
style: AppStyle.title, .captainProfileData['ratingCount']
), .toString()) ??
), 0,
), ),
InkWell( const SizedBox(height: 24),
onTap: () async {
Get.to(() => BehaviorPage()); // 2. قسم الإجراءات السريعة
}, ActionsGrid(),
borderRadius: BorderRadius.circular(8), const SizedBox(height: 24),
child: Padding(
padding: const EdgeInsets.symmetric( // 3. بطاقة المعلومات الشخصية
vertical: 16, horizontal: 16), PersonalInfoCard(
child: Text( data: controller.captainProfileData
'Show behavior page'.tr, .cast<String, dynamic>()),
style: AppStyle.title, const SizedBox(height: 16),
),
), // 4. بطاقة معلومات المركبة (قابلة للتوسيع)
), VehicleInfoCard(
], data: controller.captainProfileData
), .cast<String, dynamic>()),
), ],
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Show my Cars'.tr,
onPressed: () async {
Get.to(() => CaptainsCars());
},
),
// const SizedBox(height: 15),
MyElevatedButton(
title: 'Add criminal page'.tr,
onPressed: () async {
Get.to(() => CriminalDocumemtPage());
},
),
],
),
SizedBox(
height: Get.height * .7,
child: DriverProfileCard(
driverId:
controller.captainProfileData['driverID'] ??
'',
name:
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}',
phoneNumber:
controller.captainProfileData['phone'] ?? '',
email:
controller.captainProfileData['email'] ?? '',
birthdate: controller
.captainProfileData['birthdate'] is String
? controller.captainProfileData['birthdate']
: '',
gender: controller.captainProfileData['gender']
is String
? controller.captainProfileData['gender']
: '',
education: controller
.captainProfileData['education'] is String
? controller.captainProfileData['education']
: '',
carMake:
controller.captainProfileData['make'] ?? '',
carModel:
controller.captainProfileData['model'] ?? '',
carPlate:
controller.captainProfileData['car_plate'] ??
'',
carColor:
controller.captainProfileData['color'] ?? '',
vin: controller.captainProfileData['vin'] ?? '',
registrationDate: controller.captainProfileData[
'registration_date'] ??
'',
expirationDate: controller
.captainProfileData['expiration_date'] ??
'',
ratingCount: int.tryParse(controller
.captainProfileData['ratingCount']
.toString()) ??
0,
ratingDriver: controller
.captainProfileData['ratingDriver'] !=
null
? double.tryParse(controller
.captainProfileData['ratingDriver']
.toString()) ??
0
: null,
age: int.tryParse(controller
.captainProfileData['age']
.toString()) ??
0,
),
),
],
),
), ),
), );
), },
) ),
], ],
isleading: true,
); );
} }
} }
class DriverProfileCard extends StatelessWidget { // --- الويدجتس الجديدة المنفصلة لتحسين التصميم ---
final String driverId;
final String name;
final String phoneNumber;
final String email;
final String birthdate;
final String gender;
final String education;
final String carMake;
final String carModel;
final String carPlate;
final String carColor;
final String vin;
final String registrationDate;
final String expirationDate;
final int ratingCount;
final double? ratingDriver;
final int age;
DriverProfileCard({ /// 1. ويدجت رأس الصفحة
required this.driverId, class ProfileHeader extends StatelessWidget {
final String name;
final double rating;
final int ratingCount;
const ProfileHeader({
super.key,
required this.name, required this.name,
required this.phoneNumber, required this.rating,
required this.email,
required this.birthdate,
required this.gender,
required this.education,
required this.carMake,
required this.carModel,
required this.carPlate,
required this.carColor,
required this.vin,
required this.registrationDate,
required this.expirationDate,
required this.ratingCount, required this.ratingCount,
required this.ratingDriver,
required this.age,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Column(
// elevation: 8, children: [
decoration: AppStyle.boxDecoration1, CircleAvatar(
margin: const EdgeInsets.all(16), radius: 50,
child: SingleChildScrollView( backgroundColor: Get.theme.primaryColor.withOpacity(0.1),
child: Icon(Icons.person, size: 60, color: Get.theme.primaryColor),
),
const SizedBox(height: 12),
Text(
name,
style: Get.textTheme.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.star, color: Colors.amber, size: 20),
const SizedBox(width: 4),
Text(
'${rating.toStringAsFixed(1)} (${'reviews'.tr} $ratingCount)',
style: Get.textTheme.titleMedium
?.copyWith(color: Colors.grey.shade600),
),
],
),
],
);
}
}
/// 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: [
_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()),
),
],
);
}
}
/// ويدجت داخلية لزر في الشبكة
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( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(8.0),
child: Column( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Icon(icon, color: Get.theme.primaryColor, size: 20),
style: AppStyle.title, const SizedBox(width: 8),
name, Flexible(
), child: Text(
const SizedBox(height: 8), title,
Row( style: Get.textTheme.labelLarge,
children: [ textAlign: TextAlign.center,
const Icon(Icons.phone), )),
const SizedBox(width: 8),
Text(style: AppStyle.title, phoneNumber),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'birthdate'.tr} : $birthdate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.wc),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'gender'.tr} : $gender',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.school),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'education'.tr} : $education',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.car_repair),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'Make'.tr} : $carMake',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.model_training),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_model'.tr} : $carModel',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.drive_eta),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_plate'.tr} : $carPlate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.color_lens),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'car_color'.tr} : $carColor',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.confirmation_number),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'vin'.tr} : $vin',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'registration_date'.tr} : $registrationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'expiration_date'.tr} : $expirationDate',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'rating_count'.tr} : $ratingCount',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star_rate),
const SizedBox(width: 8),
ratingDriver != null
? Text(
style: AppStyle.title,
'${'rating_driver'.tr} : $ratingDriver',
)
: Text(
style: AppStyle.title,
'${'rating_driver'.tr} : 0',
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.person),
const SizedBox(width: 8),
Text(
style: AppStyle.title,
'${'age'.tr} : $age',
),
],
),
], ],
), ),
), ),
@@ -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(); Get.back();
}, },
child: Text( child: Text(
'Cancel', 'Cancel'.tr,
style: TextStyle( style: TextStyle(
color: AppColor.redColor, color: AppColor.redColor,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

View File

@@ -836,6 +836,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.2.1" 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: flutter_stripe:
dependency: "direct main" dependency: "direct main"
description: description:

View File

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