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

View File

@@ -155,11 +155,12 @@ class AuthScreen extends StatelessWidget {
.validate()) { .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,
controller: settingsController,
valueGetter: (ctrl) => (ctrl).isGoogleMapsEnabled,
onChanged: (ctrl) => (ctrl).onChangMapApp(),
), ),
title: Text('Google Map App'.tr, style: AppStyle.headTitle2), _buildSwitchTile(
subtitle: Text( icon: Icons.vibration,
'If you want to make Google Map App run directly when you apply order' title: 'Vibration'.tr,
.tr, subtitle: 'Vibration feedback for buttons'.tr,
style: AppStyle.subtitle, controller: homeController,
), valueGetter: (ctrl) => (ctrl).isVibrate,
trailing: GetBuilder<SettingController>( onChanged: (ctrl) => (ctrl)
builder: (settingController) { .changeVibrateOption(true), // قد تحتاج لتعديل الدالة
return CupertinoSwitch(
value: settingController.isGoogleMapsEnabled,
activeTrackColor: AppColor.primaryColor,
onChanged: (bool value) {
settingController.onChangMapApp();
},
);
},
),
),
CupertinoListTile(
leading: Icon(Icons.vibration),
title: Text('Vibration'.tr, style: AppStyle.headTitle2),
subtitle: Text(
"You can change the vibration feedback for all buttons".tr,
style: AppStyle.subtitle,
),
trailing: GetBuilder<HomePageController>(
builder: (controller) => CupertinoSwitch(
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
activeTrackColor: AppColor.primaryColor,
),
),
onTap: () => print('3'),
), ),
], ],
), ),
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,
answer: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: ListView( child: Image.network(
children: [ 'https://api.tripz-egypt.com/tripz/imageForUsingApp/order_page.jpg',
InkWell(
onTap: () {
MyDialogContent().getDialog(
"What are the order details we provide to you?".tr,
Image.network(
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg',
height: 300,
width: 300,
fit: BoxFit.cover, fit: BoxFit.cover,
), () { // يمكنك إضافة مؤشر تحميل هنا
Get.back(); loadingBuilder: (context, child, loadingProgress) {
}); if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) {
return const Center(
child:
Icon(Icons.error_outline, color: Colors.red, size: 40));
}, },
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"What are the order details we provide to you?".tr,
style: AppStyle.title,
), ),
), ),
), ),
), FaqItem(
const SizedBox( question: "What is the feature of our wallet?".tr,
height: 20, icon: Icons.account_balance_wallet_outlined,
), answer: Text(
InkWell(
onTap: () {
MyDialog().getDialog(
"What are the order details we provide to you?".tr,
'''Intaleq Wallet Features: '''Intaleq Wallet Features:
Transfer money multiple times. - Transfer money multiple times.
Transfer to anyone. - Transfer to anyone.
Make purchases. - Make purchases.
Charge your account. - Charge your account.
Charge a friend's Intaleq account. - Charge a friend's Intaleq account.
Store your money with us and receive it in your bank as a monthly salary.''' - Store your money with us and receive it in your bank as a monthly salary.'''
.tr, () { .tr,
Get.back(); style:
}); TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
},
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,
), ),
), ),
), FaqItem(
), question: "What is Types of Trips in Intaleq?".tr,
const SizedBox( icon: Icons.map_outlined,
height: 20, answer: Text(
),
InkWell(
onTap: () {
MyDialog().getDialog(
"What is Types of Trips in Intaleq?".tr,
'''Types of Trips in Intaleq: '''Types of Trips in Intaleq:
Comfort: For cars newer than 2017 with air conditioning. - Comfort: For cars newer than 2017 with air conditioning.
Lady: For girl drivers. - Lady: For girl drivers.
Speed: For fixed salary and endpoints. - Speed: For fixed salary and endpoints.
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements. - Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
Raih Gai: For same-day return trips longer than 50km. - Raih Gai: For same-day return trips longer than 50km.'''
''' .tr,
.tr, () { style:
Get.back(); TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
});
},
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,
), ),
), ),
), ];
),
], // 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) {
// New: استخدام addPostFrameCallback لضمان أن تحميل البيانات يتم بعد بناء الواجهة
WidgetsBinding.instance.addPostFrameCallback((_) {
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) { if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول
// هذا يعطي GetX وقته لتجهيز كل شيء
WidgetsBinding.instance.addPostFrameCallback((_) {
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
mapDriverController.argumentLoading();
});
} else {
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ
WidgetsBinding.instance.addPostFrameCallback((_) {
Get.snackbar("Error", "No order data found.");
Get.back();
});
}
mapDriverController.argumentLoading(); mapDriverController.argumentLoading();
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver(); mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
} else {
// في حال عدم وجود arguments، يتم التعامل مع هذا الخطأ
Get.snackbar("Error", "No order data found.");
Get.back();
}
});
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,8 +132,13 @@ 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>(
builder: (controller) {
// يظهر زر الإلغاء فقط قبل انتهاء الرحلة
if (controller.isRideFinished) return const SizedBox.shrink();
return GestureDetector(
onTap: () { onTap: () {
Get.defaultDialog( Get.defaultDialog(
title: "Are you sure you want to cancel this trip?".tr, title: "Are you sure you want to cancel this trip?".tr,
@@ -93,36 +160,44 @@ class CancelWidget extends StatelessWidget {
title: 'Ok'.tr, title: 'Ok'.tr,
kolor: AppColor.redColor, kolor: AppColor.redColor,
onPressed: () async { onPressed: () async {
// todo add cancel and inform passenger to get new driver
await mapDriverController await mapDriverController
.cancelTripFromDriverAfterApplied(); .cancelTripFromDriverAfterApplied();
Get.back(); Get.back();
}), }),
cancel: MyElevatedButton( cancel: MyElevatedButton(
title: 'No'.tr, title: 'No'.tr,
// kolor: AppColor.redColor,
onPressed: () { onPressed: () {
Get.back(); Get.back();
})); }));
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.redColor, color: Colors.white,
borderRadius: BorderRadius.circular(15)), shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 5,
),
],
),
child: const Padding( child: const Padding(
padding: EdgeInsets.all(3), padding: EdgeInsets.all(8.0),
child: Icon( child: Icon(
Icons.clear, Icons.clear,
size: 40, size: 30,
color: AppColor.secondaryColor, color: AppColor.redColor,
), ),
), ),
), ),
);
},
), ),
); );
} }
} }
// Changed: تم تعديل تصميم نافذة السعر لتكون أكثر وضوحًا
class PricesWindow extends StatelessWidget { class PricesWindow extends StatelessWidget {
const PricesWindow({ const PricesWindow({
super.key, super.key,
@@ -132,26 +207,30 @@ 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,
right: Get.height * 1,
child: Container( child: Container(
height: Get.height * 3, width: Get.width * 0.8,
decoration: AppStyle.boxDecoration1, padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Text(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(3),
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(
'${mapDriverController.totalPricePassenger} ${'\$'.tr}',
style: AppStyle.headTitle2.copyWith(
color: AppColor.primaryColor, fontSize: 36),
),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
@@ -166,6 +245,7 @@ class PricesWindow extends StatelessWidget {
], ],
), ),
), ),
),
) )
: const SizedBox(); : const SizedBox();
}); });

View File

@@ -1,434 +1,170 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.dart'; import 'package:sefer_driver/constant/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());
@override // 2. تعريف بيانات القائمة بشكل مركزي ومنظم
Widget build(BuildContext context) { final List<DrawerItem> drawerItems = [
return CupertinoPageScaffold( DrawerItem(
backgroundColor: CupertinoColors.systemGroupedBackground, title: 'Wallet'.tr,
navigationBar: CupertinoNavigationBar( icon: Icons.account_balance_wallet,
middle: Text('Menu'.tr, color: Colors.green,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)), onTap: () => Get.to(() => WalletCaptainRefactored())),
backgroundColor: Colors.transparent, DrawerItem(
border: null, title: 'Profile'.tr,
), icon: Icons.person,
child: SafeArea( color: Colors.blue,
child: CustomScrollView( onTap: () => Get.to(() => ProfileCaptain())),
slivers: [ DrawerItem(
const SliverToBoxAdapter(child: UserAccountHeader()), title: 'History of Trip'.tr,
SliverList( icon: Icons.history,
delegate: SliverChildListDelegate([ color: Colors.orange,
const SizedBox(height: 10), onTap: () => Get.to(() => const HistoryCaptain())),
_buildSectionHeader('Account'.tr), DrawerItem(
_buildDrawerItem( title: 'Available for rides'.tr,
icon: CupertinoIcons.money_dollar_circle_fill, icon: Icons.drive_eta,
text: 'Wallet'.tr, color: Colors.teal,
onTap: () => Get.to(() => WalletCaptainRefactored()), onTap: () => Get.to(() => const AvailableRidesPage())),
), DrawerItem(
_buildDrawerItem( title: 'Notifications'.tr,
icon: CupertinoIcons.person_fill, icon: Icons.notifications,
text: 'Profile'.tr, color: Colors.purple,
onTap: () => Get.to(() => ProfileCaptain()), onTap: () => Get.to(() => const NotificationCaptain())),
), DrawerItem(
_buildSectionHeader('Activities'.tr), title: 'Helping Center'.tr,
_buildDrawerItem( icon: Icons.help_center,
icon: CupertinoIcons.clock_fill, color: Colors.cyan,
text: 'History of Trip'.tr, onTap: () => Get.to(() => HelpCaptain())),
onTap: () => Get.to(() => const HistoryCaptain()), DrawerItem(
), title: 'Share App'.tr,
_buildDrawerItem( icon: Icons.share,
icon: CupertinoIcons.car_fill, color: Colors.indigo,
text: 'Available for rides'.tr, onTap: () => Get.to(() => InviteScreen())),
onTap: () => Get.to(() => const AvailableRidesPage()), DrawerItem(
), title: 'Maintenance Center'.tr,
_buildSectionHeader('Support'.tr), icon: Icons.build,
_buildDrawerItem( color: Colors.brown,
icon: CupertinoIcons.bell_fill, onTap: () => Get.to(() => MaintainCenterPage())),
text: 'Notifications'.tr, DrawerItem(
onTap: () => Get.to(() => const NotificationCaptain()), title: 'Health Insurance'.tr,
), icon: Icons.favorite,
_buildDrawerItem( color: Colors.pink,
icon: CupertinoIcons.question_circle_fill, onTap: () => Get.to(() => AssuranceHealthPage())),
text: 'Helping Center'.tr, DrawerItem(
onTap: () => Get.to(() => HelpCaptain()), title: 'Contact Us'.tr,
), icon: Icons.email,
_buildSectionHeader('More'.tr), color: Colors.blueGrey,
...moreMenuItems(), 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,
List<Widget> moreMenuItems() => [ color: Colors.amber,
_buildDrawerItem( onTap: () => Get.to(() => RatingScreen())),
icon: CupertinoIcons.share_up, DrawerItem(
text: 'Share App'.tr, title: 'Settings'.tr,
onTap: () => Get.to(() => InviteScreen()), icon: Icons.settings,
), color: Colors.grey.shade600,
_buildDrawerItem( onTap: () => Get.to(() => const SettingsCaptain())),
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) { @override
return Padding( Widget build(BuildContext context) {
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), return Drawer(
child: Text( child: Container(
title, color: Theme.of(context).scaffoldBackgroundColor,
style: const TextStyle( child: Column(
color: CupertinoColors.systemGrey, children: [
fontSize: 13, // --- الجزء العلوي من القائمة (بيانات المستخدم) ---
fontWeight: FontWeight.w600, UserHeader(), // استخدمنا الويدجت المحسنة بالأسفل
// --- قائمة العناصر المتحركة ---
Expanded(
child: AnimationLimiter(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: drawerItems.length + 1, // +1 لزر تسجيل الخروج
itemBuilder: (BuildContext context, int index) {
// --- زر تسجيل الخروج في النهاية ---
if (index == drawerItems.length) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _DrawerItemTile(
item: DrawerItem(
title: 'Sign Out'.tr,
icon: Icons.logout,
color: Colors.red,
onTap: () =>
Get.to(() => const LogoutCaptain())),
),
), ),
), ),
); );
} }
Widget _buildDrawerItem({ // --- بقية العناصر ---
required IconData icon, final item = drawerItems[index];
required String text, return AnimationConfiguration.staggeredList(
required VoidCallback onTap, position: index,
bool isDestructive = false, duration: const Duration(milliseconds: 375),
}) { child: SlideAnimation(
return Container( verticalOffset: 50.0,
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), child: FadeInAnimation(
decoration: BoxDecoration( child: _DrawerItemTile(item: item),
color: CupertinoColors.white,
borderRadius: BorderRadius.circular(10),
), ),
child: CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onPressed: onTap,
child: Row(
children: [
Icon(
icon,
color: isDestructive
? CupertinoColors.destructiveRed
: CupertinoColors.activeBlue,
size: 22,
), ),
const SizedBox(width: 12), );
Text( },
text, ),
style: AppStyle.title,
// TextStyle(
// color: isDestructive
// ? CupertinoColors.destructiveRed
// : CupertinoColors.label,
// fontSize: 16,
// ),
), ),
const Spacer(),
Icon(
CupertinoIcons.chevron_right,
color: CupertinoColors.systemGrey3,
size: 18,
), ),
], ],
), ),
@@ -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( decoration: BoxDecoration(
gradient: const LinearGradient( color: item.color.withOpacity(0.1),
colors: [AppColor.blueColor, Color(0xFF2196F3)], shape: BoxShape.circle,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
), ),
boxShadow: [ child: Icon(item.icon, color: item.color, size: 24),
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 10,
spreadRadius: 2,
), ),
], title: Text(
item.title,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.w500),
), ),
child: Column( onTap: () {
crossAxisAlignment: CrossAxisAlignment.start, Get.back(); // لإغلاق القائمة عند الضغط
children: [ Future.delayed(const Duration(milliseconds: 250), () {
GetBuilder<ImageController>( item.onTap(); // الانتقال للصفحة بعد تأخير بسيط لإظهار الأنيميشن
builder: (imageController) { });
return Stack( },
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: [ children: [
Container( Container(
width: 100,
height: 100,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3), border: Border.all(color: Colors.white, width: 2),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 5)
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
spreadRadius: 2,
),
], ],
), ),
child: imageController.isloading child: controller.isloading
? const CupertinoActivityIndicator() ? const CircularProgressIndicator(color: Colors.white)
: ClipRRect( : CircleAvatar(
borderRadius: BorderRadius.circular(50), backgroundImage: NetworkImage(
child: Image.network( '${AppLink.server}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg'),
'${AppLink.seferCairoServer}/portrate_captain_image/${(box.read(BoxName.driverID))}.jpg',
fit: BoxFit.cover,
),
), ),
), ),
Positioned( Positioned(
right: 0, bottom: -5,
bottom: 0, right: -5,
child: CupertinoButton( child: InkWell(
padding: EdgeInsets.zero, onTap: () => controller.choosImagePicture(
onPressed: () { AppLink.uploadImagePortrate, 'portrait'),
imageController.choosImagePicture(
AppLink.uploadImagePortrate, 'portrait');
},
child: Container( child: Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(4),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon( child: Icon(Icons.camera_alt,
CupertinoIcons.camera_fill, color: Theme.of(context).primaryColor, size: 18),
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), otherAccountsPictures: [
Text( Row(
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, mainAxisSize: MainAxisSize.min,
children: [ children: [
// const Icon(
// CupertinoIcons.star_fill,
// color: CupertinoColors.systemYellow,
// size: 18,
// ),
// const SizedBox(width: 4),
Text( Text(
Get.find<HomeCaptainController>().rating.toString(), homeCaptainController.rating.toString(),
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontWeight: FontWeight.bold,
fontWeight: FontWeight.w600, fontSize: 16),
),
),
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) {},
), ),
const SizedBox(width: 4),
const Icon(Icons.star, color: Colors.amber, size: 20),
], ],
), ),
),
], ],
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Theme.of(context).primaryColor, Colors.blue.shade700],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
), ),
); );
} }

View File

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

View File

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

View File

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

View File

@@ -192,7 +192,7 @@ class WalletCaptainRefactored extends StatelessWidget {
.speakText( .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( }
return SingleChildScrollView(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Column(
children: [ children: [
Material( // 1. رأس الصفحة: صورة واسم وتقييم
elevation: 2, ProfileHeader(
borderRadius: BorderRadius.circular(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () async {
// addBankCodeEgypt(captainWalletController);
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Add bank Account'.tr,
style: AppStyle.title,
),
),
),
InkWell(
onTap: () async {
Get.to(() => BehaviorPage());
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 16),
child: Text(
'Show behavior page'.tr,
style: AppStyle.title,
),
),
),
],
),
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Show my Cars'.tr,
onPressed: () async {
Get.to(() => CaptainsCars());
},
),
// const SizedBox(height: 15),
MyElevatedButton(
title: 'Add criminal page'.tr,
onPressed: () async {
Get.to(() => CriminalDocumemtPage());
},
),
],
),
SizedBox(
height: Get.height * .7,
child: DriverProfileCard(
driverId:
controller.captainProfileData['driverID'] ??
'',
name: name:
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}', '${controller.captainProfileData['first_name'] ?? ''} ${controller.captainProfileData['last_name'] ?? ''}',
phoneNumber: rating:
controller.captainProfileData['phone'] ?? '', controller.captainProfileData['ratingDriver'] != null
email: ? double.tryParse(controller
controller.captainProfileData['email'] ?? '', .captainProfileData['ratingDriver']
birthdate: controller .toString()) ??
.captainProfileData['birthdate'] is String 0.0
? controller.captainProfileData['birthdate'] : 0.0,
: '',
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 ratingCount: int.tryParse(controller
.captainProfileData['ratingCount'] .captainProfileData['ratingCount']
.toString()) ?? .toString()) ??
0, 0,
ratingDriver: controller
.captainProfileData['ratingDriver'] !=
null
? double.tryParse(controller
.captainProfileData['ratingDriver']
.toString()) ??
0
: null,
age: int.tryParse(controller
.captainProfileData['age']
.toString()) ??
0,
),
), ),
const SizedBox(height: 24),
// 2. قسم الإجراءات السريعة
ActionsGrid(),
const SizedBox(height: 24),
// 3. بطاقة المعلومات الشخصية
PersonalInfoCard(
data: controller.captainProfileData
.cast<String, dynamic>()),
const SizedBox(height: 16),
// 4. بطاقة معلومات المركبة (قابلة للتوسيع)
VehicleInfoCard(
data: controller.captainProfileData
.cast<String, dynamic>()),
], ],
), ),
);
},
), ),
),
),
)
], ],
isleading: true,
); );
} }
} }
class DriverProfileCard extends StatelessWidget { // --- الويدجتس الجديدة المنفصلة لتحسين التصميم ---
final String driverId;
final String name;
final String phoneNumber;
final String email;
final String birthdate;
final String gender;
final String education;
final String carMake;
final String carModel;
final String carPlate;
final String carColor;
final String vin;
final String registrationDate;
final String expirationDate;
final int ratingCount;
final double? ratingDriver;
final int age;
DriverProfileCard({ /// 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,
decoration: AppStyle.boxDecoration1,
margin: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
CircleAvatar(
radius: 50,
backgroundColor: Get.theme.primaryColor.withOpacity(0.1),
child: Icon(Icons.person, size: 60, color: Get.theme.primaryColor),
),
const SizedBox(height: 12),
Text( Text(
style: AppStyle.title,
name, name,
style: Get.textTheme.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.phone), Icon(Icons.star, color: Colors.amber, size: 20),
const SizedBox(width: 8), const SizedBox(width: 4),
Text(style: AppStyle.title, phoneNumber),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.calendar_today),
const SizedBox(width: 8),
Text( Text(
style: AppStyle.title, '${rating.toStringAsFixed(1)} (${'reviews'.tr} $ratingCount)',
'${'birthdate'.tr} : $birthdate', style: Get.textTheme.titleMedium
?.copyWith(color: Colors.grey.shade600),
), ),
], ],
), ),
const SizedBox(height: 8), ],
Row( );
}
}
/// 2. ويدجت شبكة الأزرار
class ActionsGrid extends StatelessWidget {
const ActionsGrid({super.key});
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 2.5, // للتحكم في ارتفاع الأزرار
children: [ children: [
const Icon(Icons.wc), _ActionTile(
const SizedBox(width: 8), title: 'My Cars'.tr,
Text( icon: Icons.directions_car_filled,
style: AppStyle.title, onTap: () => Get.to(() => CaptainsCars()),
'${'gender'.tr} : $gender', ),
_ActionTile(
title: 'Criminal Record'.tr,
icon: Icons.description,
onTap: () => Get.to(() => CriminalDocumemtPage()),
),
_ActionTile(
title: 'Bank Account'.tr,
icon: Icons.account_balance,
onTap: () {
MyDialog().getDialog('Coming Soon'.tr,
'This service will be available soon.'.tr, () => Get.back());
},
),
_ActionTile(
title: 'Behavior Page'.tr,
icon: Icons.checklist_rtl,
onTap: () => Get.to(() => BehaviorPage()),
), ),
], ],
), );
const SizedBox(height: 8), }
Row( }
/// ويدجت داخلية لزر في الشبكة
class _ActionTile extends StatelessWidget {
final String title;
final IconData icon;
final VoidCallback onTap;
const _ActionTile(
{required this.title, required this.icon, required this.onTap});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.school), Icon(icon, color: Get.theme.primaryColor, size: 20),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Flexible(
style: AppStyle.title, child: Text(
'${'education'.tr} : $education', title,
), style: Get.textTheme.labelLarge,
], textAlign: TextAlign.center,
), )),
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: