25-8-4-1
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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/';
|
||||
|
||||
@@ -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============
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
86
lib/controller/home/captin/model.dart
Normal file
86
lib/controller/home/captin/model.dart
Normal 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(' ', ' ');
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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
@@ -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: () {
|
||||
|
||||
@@ -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, '', '');
|
||||
|
||||
@@ -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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}',
|
||||
|
||||
@@ -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"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class MyDialog extends GetxController {
|
||||
Get.back();
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
'Cancel'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.redColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user