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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -154,12 +154,13 @@ class AuthScreen extends StatelessWidget {
.formKey.currentState!
.validate()) {
controller.logintest(
controller
.emailController.text
.trim(),
controller
.passwordController.text
.trim());
controller
.passwordController.text
.trim(),
controller
.emailController.text
.trim(),
);
}
},
child: Text(

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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