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.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = 3
|
versionCode = 7
|
||||||
versionName = '1.0.3'
|
versionName = '1.0.7'
|
||||||
multiDexEnabled =true
|
multiDexEnabled =true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class AK {
|
|||||||
static final String accountSIDTwillo =
|
static final String accountSIDTwillo =
|
||||||
X.r(X.r(X.r(Env.accountSIDTwillo, cn), cC), cs);
|
X.r(X.r(X.r(Env.accountSIDTwillo, cn), cC), cs);
|
||||||
static final String serverAPI = X.r(X.r(X.r(Env.serverAPI, cn), cC), cs);
|
static final String serverAPI = X.r(X.r(X.r(Env.serverAPI, cn), cC), cs);
|
||||||
static final String mapAPIKEY = X.r(X.r(X.r(Env.mapAPIKEY, cn), cC), cs);
|
static final String mapAPIKEY = Env.mapAPIKEY;
|
||||||
static final String twilloRecoveryCode =
|
static final String twilloRecoveryCode =
|
||||||
X.r(X.r(X.r(Env.twilloRecoveryCode, cn), cC), cs);
|
X.r(X.r(X.r(Env.twilloRecoveryCode, cn), cC), cs);
|
||||||
static final String authTokenTwillo =
|
static final String authTokenTwillo =
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class AppInformation {
|
class AppInformation {
|
||||||
static const String companyName = 'Intaleq';
|
static const String companyName = 'Intaleq';
|
||||||
static const String appName = 'Intaleq DRIVER';
|
static const String appName = 'Intaleq DRIVER';
|
||||||
static const String appVersion = 'Intaleq Captain';
|
static const String appVersion = 'Intaleq DRIVER';
|
||||||
static const String phoneNumber = '962798583052';
|
static const String phoneNumber = '962798583052';
|
||||||
static const String linkedInProfile =
|
static const String linkedInProfile =
|
||||||
'https://www.linkedin.com/in/hamza-ayed/';
|
'https://www.linkedin.com/in/hamza-ayed/';
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ class AppLink {
|
|||||||
static String addprofile = "$profile/add.php";
|
static String addprofile = "$profile/add.php";
|
||||||
static String deleteprofile = "$profile/delete.php";
|
static String deleteprofile = "$profile/delete.php";
|
||||||
static String updateprofile = "$profile/update.php";
|
static String updateprofile = "$profile/update.php";
|
||||||
|
static String updateDriverEmail = "$profile/updateDriverEmail.php";
|
||||||
|
|
||||||
//===================Auth============
|
//===================Auth============
|
||||||
|
|
||||||
|
|||||||
@@ -69,10 +69,8 @@ class LoginDriverController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() async {
|
||||||
box.write(BoxName.countryCode, 'Syria');
|
box.write(BoxName.countryCode, 'Syria');
|
||||||
box.read(BoxName.isTest) == null ||
|
// box.write(BoxName.driverID, '34feffd3fa72d6bee56b');
|
||||||
box.read(BoxName.isTest).toString() == '0'
|
await getAppTester();
|
||||||
? await getAppTester()
|
|
||||||
: null;
|
|
||||||
|
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
@@ -83,10 +81,15 @@ class LoginDriverController extends GetxController {
|
|||||||
payload: {'appPlatform': AppInformation.appName});
|
payload: {'appPlatform': AppInformation.appName});
|
||||||
if (res != 'failure') {
|
if (res != 'failure') {
|
||||||
var d = jsonDecode(res);
|
var d = jsonDecode(res);
|
||||||
|
|
||||||
isTest = d['message'][0]['isTest'];
|
isTest = d['message'][0]['isTest'];
|
||||||
|
box.write(BoxName.isTest, isTest);
|
||||||
|
|
||||||
|
Log.print('isTest: ${box.read(BoxName.isTest)}');
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
|
isTest = 0;
|
||||||
|
box.write(BoxName.isTest, isTest);
|
||||||
|
update();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class OtpVerificationController extends GetxController {
|
|||||||
try {
|
try {
|
||||||
final response = await CRUD().post(
|
final response = await CRUD().post(
|
||||||
link:
|
link:
|
||||||
'${AppLink.server}/auth/token_passenger/driver/verify_otp_driver/.php',
|
'${AppLink.server}/auth/token_passenger/driver/verify_otp_driver.php',
|
||||||
payload: {
|
payload: {
|
||||||
'phone_number': phone,
|
'phone_number': phone,
|
||||||
'otp': otpCode.value,
|
'otp': otpCode.value,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import 'package:encrypt/encrypt.dart' as encrypt;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:secure_string_operations/secure_string_operations.dart';
|
import 'package:secure_string_operations/secure_string_operations.dart';
|
||||||
|
|
||||||
import '../../constant/box_name.dart';
|
|
||||||
import '../../constant/char_map.dart';
|
import '../../constant/char_map.dart';
|
||||||
import '../../env/env.dart';
|
import '../../env/env.dart';
|
||||||
import '../../main.dart';
|
import '../../main.dart';
|
||||||
|
import '../../print.dart';
|
||||||
|
|
||||||
class EncryptionHelper {
|
class EncryptionHelper {
|
||||||
static EncryptionHelper? _instance;
|
static EncryptionHelper? _instance;
|
||||||
@@ -29,16 +29,14 @@ class EncryptionHelper {
|
|||||||
return; // Prevent re-initialization
|
return; // Prevent re-initialization
|
||||||
}
|
}
|
||||||
debugPrint("Initializing EncryptionHelper...");
|
debugPrint("Initializing EncryptionHelper...");
|
||||||
// Read stored keys
|
|
||||||
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
|
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
|
||||||
var initializationVector =
|
var initializationVector =
|
||||||
r(Env.initializationVector).toString().split(Env.addd)[0];
|
r(Env.initializationVector).toString().split(Env.addd)[0];
|
||||||
|
|
||||||
// Log.print('initializationVector: ${initializationVector}');
|
|
||||||
// Set the global instance
|
// Set the global instance
|
||||||
_instance = EncryptionHelper._(
|
_instance = EncryptionHelper._(
|
||||||
encrypt.Key.fromUtf8(keyOfApp),
|
encrypt.Key.fromUtf8(keyOfApp!),
|
||||||
encrypt.IV.fromUtf8(initializationVector),
|
encrypt.IV.fromUtf8(initializationVector!),
|
||||||
);
|
);
|
||||||
debugPrint("EncryptionHelper initialized successfully.");
|
debugPrint("EncryptionHelper initialized successfully.");
|
||||||
}
|
}
|
||||||
@@ -46,8 +44,8 @@ class EncryptionHelper {
|
|||||||
/// Encrypts a string
|
/// Encrypts a string
|
||||||
String encryptData(String plainText) {
|
String encryptData(String plainText) {
|
||||||
try {
|
try {
|
||||||
final encrypter = encrypt.Encrypter(
|
final encrypter =
|
||||||
encrypt.AES(key, mode: encrypt.AESMode.cbc)); // AES-GCM
|
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
|
||||||
final encrypted = encrypter.encrypt(plainText, iv: iv);
|
final encrypted = encrypter.encrypt(plainText, iv: iv);
|
||||||
return encrypted.base64;
|
return encrypted.base64;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
import 'package:location/location.dart';
|
import 'package:location/location.dart';
|
||||||
import 'package:sefer_driver/constant/table_names.dart';
|
import 'package:sefer_driver/constant/table_names.dart';
|
||||||
|
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||||
|
|
||||||
import '../../constant/box_name.dart';
|
import '../../constant/box_name.dart';
|
||||||
import '../../constant/links.dart';
|
import '../../constant/links.dart';
|
||||||
@@ -87,7 +88,7 @@ class LocationController extends GetxController {
|
|||||||
Get.find<CaptainWalletController>().totalPoints.toString();
|
Get.find<CaptainWalletController>().totalPoints.toString();
|
||||||
isActive = Get.find<HomeCaptainController>().isActive;
|
isActive = Get.find<HomeCaptainController>().isActive;
|
||||||
|
|
||||||
if (isActive && double.parse(totalPoints) > -300) {
|
if (isActive && double.parse(totalPoints) > -30000) {
|
||||||
await getLocation();
|
await getLocation();
|
||||||
if (myLocation.latitude == 0 && myLocation.longitude == 0) return;
|
if (myLocation.latitude == 0 && myLocation.longitude == 0) return;
|
||||||
|
|
||||||
@@ -147,13 +148,25 @@ class LocationController extends GetxController {
|
|||||||
Get.find<HomeCaptainController>()
|
Get.find<HomeCaptainController>()
|
||||||
.mapHomeCaptainController
|
.mapHomeCaptainController
|
||||||
?.animateCamera(
|
?.animateCamera(
|
||||||
CameraUpdate.newLatLng(
|
CameraUpdate.newCameraPosition(
|
||||||
LatLng(
|
CameraPosition(
|
||||||
myLocation.latitude,
|
bearing: Get.find<LocationController>().heading,
|
||||||
myLocation.longitude,
|
target: myLocation,
|
||||||
|
zoom: 17, // Adjust zoom level as needed
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// if (Get.isRegistered()) {
|
||||||
|
// Get.find<MapDriverController>().mapController?.animateCamera(
|
||||||
|
// CameraUpdate.newCameraPosition(
|
||||||
|
// CameraPosition(
|
||||||
|
// bearing: Get.find<LocationController>().heading,
|
||||||
|
// target: myLocation,
|
||||||
|
// zoom: 17, // Adjust zoom level as needed
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Location update error: $e');
|
print('Location update error: $e');
|
||||||
|
|||||||
@@ -15,13 +15,15 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../../constant/info.dart';
|
import '../../constant/info.dart';
|
||||||
import '../../main.dart';
|
import '../../main.dart';
|
||||||
|
import '../../print.dart';
|
||||||
import 'encrypt_decrypt.dart';
|
import 'encrypt_decrypt.dart';
|
||||||
|
|
||||||
Future<void> checkForUpdate(BuildContext context) async {
|
Future<void> checkForUpdate(BuildContext context) async {
|
||||||
final packageInfo = await PackageInfo.fromPlatform();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
final currentVersion = packageInfo.buildNumber;
|
final currentVersion = packageInfo.buildNumber;
|
||||||
final version = packageInfo.version;
|
final version = packageInfo.version;
|
||||||
// print('currentVersion is : $currentVersion');
|
Log.print('version: ${version}');
|
||||||
|
print('currentVersion is : $currentVersion');
|
||||||
// Fetch the latest version from your server
|
// Fetch the latest version from your server
|
||||||
String latestVersion = await getPackageInfo();
|
String latestVersion = await getPackageInfo();
|
||||||
box.write(BoxName.packagInfo, version);
|
box.write(BoxName.packagInfo, version);
|
||||||
@@ -45,8 +47,8 @@ Future<String> getPackageInfo() async {
|
|||||||
|
|
||||||
void showUpdateDialog(BuildContext context) {
|
void showUpdateDialog(BuildContext context) {
|
||||||
final String storeUrl = Platform.isAndroid
|
final String storeUrl = Platform.isAndroid
|
||||||
? 'https://play.google.com/store/apps/details?id=com.sefer_driver'
|
? 'https://play.google.com/store/apps/details?id=com.intaleq_driver'
|
||||||
: 'https://apps.apple.com/ae/app/sefer-driver/id6502189302';
|
: 'https://apps.apple.com/ae/app/intaleq-driver/id6502189302';
|
||||||
|
|
||||||
showGeneralDialog(
|
showGeneralDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart' as path_provider;
|
|||||||
|
|
||||||
import '../../constant/box_name.dart';
|
import '../../constant/box_name.dart';
|
||||||
import '../../constant/colors.dart';
|
import '../../constant/colors.dart';
|
||||||
|
import '../../constant/info.dart';
|
||||||
import '../../main.dart';
|
import '../../main.dart';
|
||||||
import '../../print.dart';
|
import '../../print.dart';
|
||||||
import 'encrypt_decrypt.dart';
|
import 'encrypt_decrypt.dart';
|
||||||
@@ -310,17 +311,20 @@ class ImageController extends GetxController {
|
|||||||
Log.print('request: ${request}');
|
Log.print('request: ${request}');
|
||||||
var length = await file.length();
|
var length = await file.length();
|
||||||
var stream = http.ByteStream(file.openRead());
|
var stream = http.ByteStream(file.openRead());
|
||||||
|
final headers = {
|
||||||
|
'Authorization':
|
||||||
|
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||||
|
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||||
|
};
|
||||||
var multipartFile = http.MultipartFile(
|
var multipartFile = http.MultipartFile(
|
||||||
'image',
|
'image',
|
||||||
stream,
|
stream,
|
||||||
length,
|
length,
|
||||||
filename: basename(file.path),
|
filename: basename(file.path),
|
||||||
);
|
);
|
||||||
request.headers.addAll({
|
request.headers.addAll(headers);
|
||||||
'Authorization':
|
|
||||||
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
|
|
||||||
});
|
|
||||||
// Set the file name to the driverID
|
// Set the file name to the driverID
|
||||||
|
|
||||||
request.files.add(
|
request.files.add(
|
||||||
http.MultipartFile(
|
http.MultipartFile(
|
||||||
'image',
|
'image',
|
||||||
@@ -418,14 +422,11 @@ class ImageController extends GetxController {
|
|||||||
try {
|
try {
|
||||||
await uploadImage(
|
await uploadImage(
|
||||||
compressedImage,
|
compressedImage,
|
||||||
{
|
{'driverID': (box.read(BoxName.driverID)), 'imageType': imageType},
|
||||||
'driverID':
|
|
||||||
(box.read(BoxName.driverID)) ?? (box.read(BoxName.passengerID)),
|
|
||||||
'imageType': imageType
|
|
||||||
},
|
|
||||||
link,
|
link,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
Log.print('e: ${e}');
|
||||||
mySnackeBarError('Image Upload Failed'.tr);
|
mySnackeBarError('Image Upload Failed'.tr);
|
||||||
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
|
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
|
||||||
// backgroundColor: AppColor.redColor);
|
// backgroundColor: AppColor.redColor);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:sefer_driver/controller/functions/crud.dart';
|
|||||||
|
|
||||||
import '../../../constant/table_names.dart';
|
import '../../../constant/table_names.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../../../print.dart';
|
||||||
|
|
||||||
class DriverBehaviorController extends GetxController {
|
class DriverBehaviorController extends GetxController {
|
||||||
Future<List<Map<String, dynamic>>> getAllData() async {
|
Future<List<Map<String, dynamic>>> getAllData() async {
|
||||||
@@ -27,16 +28,17 @@ class DriverBehaviorController extends GetxController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response != 'failure') {
|
if (response != 'failure') {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response);
|
||||||
|
|
||||||
overallScore.value =
|
overallScore.value =
|
||||||
double.parse(json['data']['overall_behavior_score'].toString());
|
double.parse(json['message']['overall_behavior_score'].toString());
|
||||||
lastTrips.value = json['data']['last_10_trips'];
|
lastTrips.value = json['message']['last_10_trips'];
|
||||||
} else {
|
} else {
|
||||||
// Get.snackbar("Error", json['message'] ?? "Unknown error");
|
// Get.snackbar("Error", json['message'] ?? "Unknown error");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Get.snackbar("Error", "Exception: $e");
|
// Get.snackbar("Error", "Exception: $e");
|
||||||
|
Log.print('e: ${e}');
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ class HomeCaptainController extends GetxController {
|
|||||||
onMapCreated(mapHomeCaptainController!);
|
onMapCreated(mapHomeCaptainController!);
|
||||||
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
||||||
getRefusedOrderByCaptain();
|
getRefusedOrderByCaptain();
|
||||||
|
box.write(BoxName.statusDriverLocation, 'off');
|
||||||
// LocationController().getLocation();
|
// LocationController().getLocation();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart';
|
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart';
|
||||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
@@ -107,11 +108,16 @@ class MapDriverController extends GetxController {
|
|||||||
LatLng latLngPassengerLocation = LatLng(0, 0);
|
LatLng latLngPassengerLocation = LatLng(0, 0);
|
||||||
late LatLng latLngPassengerDestination = LatLng(0, 0);
|
late LatLng latLngPassengerDestination = LatLng(0, 0);
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> routeSteps = [];
|
||||||
|
String currentInstruction = "";
|
||||||
|
int currentStepIndex = 0;
|
||||||
|
|
||||||
void onMapCreated(GoogleMapController controller) async {
|
void onMapCreated(GoogleMapController controller) async {
|
||||||
myLocation = Get.find<LocationController>().myLocation;
|
myLocation = Get.find<LocationController>().myLocation;
|
||||||
// myLocation = myLocation;
|
// myLocation = myLocation;
|
||||||
mapController = controller;
|
mapController = controller;
|
||||||
controller.getVisibleRegion();
|
controller.getVisibleRegion();
|
||||||
|
// LatLngBounds bounds = await controller.getVisibleRegion();
|
||||||
controller.animateCamera(
|
controller.animateCamera(
|
||||||
CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
|
CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
|
||||||
);
|
);
|
||||||
@@ -410,20 +416,20 @@ class MapDriverController extends GetxController {
|
|||||||
'order_id': (rideId).toString(),
|
'order_id': (rideId).toString(),
|
||||||
'status': 'Begin'
|
'status': 'Begin'
|
||||||
});
|
});
|
||||||
if (AppLink.endPoint != AppLink.seferCairoServer) {
|
// if (AppLink.endPoint != AppLink.seferCairoServer) {
|
||||||
CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
|
// CRUD().post(link: "${AppLink.endPoint}/rides/update.php", payload: {
|
||||||
'id': (rideId),
|
// 'id': (rideId),
|
||||||
'rideTimeStart': DateTime.now().toString(),
|
// 'rideTimeStart': DateTime.now().toString(),
|
||||||
'status': 'Begin',
|
// 'status': 'Begin',
|
||||||
});
|
// });
|
||||||
CRUD().post(
|
// CRUD().post(
|
||||||
link: '${AppLink.endPoint}/rides/driver_order/add.php',
|
// link: '${AppLink.endPoint}/rides/driver_order/add.php',
|
||||||
payload: {
|
// payload: {
|
||||||
'driver_id': box.read(BoxName.driverID).toString(),
|
// 'driver_id': box.read(BoxName.driverID).toString(),
|
||||||
'order_id': (rideId).toString(),
|
// 'order_id': (rideId).toString(),
|
||||||
'status': 'Begin'
|
// 'status': 'Begin'
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||||
'Trip is Begin'.tr,
|
'Trip is Begin'.tr,
|
||||||
box.read(BoxName.nameDriver).toString(),
|
box.read(BoxName.nameDriver).toString(),
|
||||||
@@ -591,28 +597,34 @@ class MapDriverController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> finishRideFromDriver() async {
|
Future<void> finishRideFromDriver() async {
|
||||||
double distanceToDestination = Geolocator.distanceBetween(
|
// double distanceToDestination = Geolocator.distanceBetween(
|
||||||
latLngPassengerDestination.latitude,
|
// latLngPassengerDestination.latitude,
|
||||||
latLngPassengerDestination.longitude,
|
// latLngPassengerDestination.longitude,
|
||||||
Get.find<LocationController>().myLocation.latitude,
|
// Get.find<LocationController>().myLocation.latitude,
|
||||||
Get.find<LocationController>().myLocation.longitude,
|
// Get.find<LocationController>().myLocation.longitude,
|
||||||
);
|
// );
|
||||||
final originalDistanceM = double.parse(distance.toString()) * 1000;
|
final originalDistanceM = double.parse(distance.toString()) * 1000;
|
||||||
|
|
||||||
// 2. احسب المسافة التي قطعها السائق حتى الآن
|
// 2. احسب المسافة التي قطعها السائق حتى الآن
|
||||||
final movedDistanceM = originalDistanceM - distanceToDestination;
|
final movedDistanceM = Geolocator.distanceBetween(
|
||||||
|
Get.find<LocationController>().myLocation.latitude,
|
||||||
|
Get.find<LocationController>().myLocation.longitude,
|
||||||
|
latLngPassengerDestination.latitude,
|
||||||
|
latLngPassengerDestination.longitude,
|
||||||
|
);
|
||||||
|
// originalDistanceM - distanceToDestination;
|
||||||
|
|
||||||
// 3. عتبة ثلث المسافة
|
// 3. عتبة ثلث المسافة
|
||||||
final oneThirdDistanceM = originalDistanceM / 3;
|
final oneThirdDistanceM = originalDistanceM / 3;
|
||||||
|
|
||||||
// Logging للتتبع
|
// Logging للتتبع
|
||||||
Log.print('originalDistanceM: $originalDistanceM');
|
Log.print('originalDistanceM: $originalDistanceM');
|
||||||
Log.print('distanceToDestinationM: $distanceToDestination');
|
// Log.print('distanceToDestinationM: $distanceToDestination');
|
||||||
Log.print('movedDistanceM: $movedDistanceM');
|
Log.print('movedDistanceM: $movedDistanceM');
|
||||||
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
|
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
|
||||||
|
|
||||||
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
|
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
|
||||||
if (movedDistanceM < oneThirdDistanceM) {
|
if (movedDistanceM > oneThirdDistanceM * 2) {
|
||||||
MyDialog().getDialog(
|
MyDialog().getDialog(
|
||||||
'Are you sure to exit ride?'.tr,
|
'Are you sure to exit ride?'.tr,
|
||||||
'',
|
'',
|
||||||
@@ -1177,6 +1189,7 @@ class MapDriverController extends GetxController {
|
|||||||
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
|
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
|
||||||
|
|
||||||
var response = await CRUD().getGoogleApi(link: url, payload: {});
|
var response = await CRUD().getGoogleApi(link: url, payload: {});
|
||||||
|
Log.print('response: ${response}');
|
||||||
data = response['routes'][0]['legs'];
|
data = response['routes'][0]['legs'];
|
||||||
distanceBetweenDriverAndPassengerWhenConfirm =
|
distanceBetweenDriverAndPassengerWhenConfirm =
|
||||||
(data[0]['distance']['value']) / 1000;
|
(data[0]['distance']['value']) / 1000;
|
||||||
@@ -1230,6 +1243,84 @@ class MapDriverController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkForNextStep(LatLng currentPosition) {
|
||||||
|
if (currentStepIndex >= routeSteps.length) return;
|
||||||
|
|
||||||
|
final step = routeSteps[currentStepIndex];
|
||||||
|
final endLocation = step['end_location'];
|
||||||
|
final endLatLng = LatLng(endLocation['lat'], endLocation['lng']);
|
||||||
|
|
||||||
|
final distance = calculateDistance(
|
||||||
|
currentPosition.latitude,
|
||||||
|
currentPosition.longitude,
|
||||||
|
endLatLng.latitude,
|
||||||
|
endLatLng.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance < 50) {
|
||||||
|
// 50 متر قبل النقطة
|
||||||
|
currentStepIndex++;
|
||||||
|
if (currentStepIndex < routeSteps.length) {
|
||||||
|
currentInstruction = _parseInstruction(
|
||||||
|
routeSteps[currentStepIndex]['html_instructions']);
|
||||||
|
Get.isRegistered<TextToSpeechController>()
|
||||||
|
? Get.find<TextToSpeechController>().speakText(currentInstruction)
|
||||||
|
: Get.put(TextToSpeechController()).speakText(currentInstruction);
|
||||||
|
Log.print('Current Instruction: $currentInstruction');
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the distance in meters between two latitude/longitude points.
|
||||||
|
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
||||||
|
const double earthRadius = 6371000; // meters
|
||||||
|
double dLat = _degreesToRadians(lat2 - lat1);
|
||||||
|
double dLon = _degreesToRadians(lon2 - lon1);
|
||||||
|
|
||||||
|
double a = (sin(dLat / 2) * sin(dLat / 2)) +
|
||||||
|
cos(_degreesToRadians(lat1)) *
|
||||||
|
cos(_degreesToRadians(lat2)) *
|
||||||
|
(sin(dLon / 2) * sin(dLon / 2));
|
||||||
|
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
|
||||||
|
double distance = earthRadius * c;
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _degreesToRadians(double degrees) {
|
||||||
|
return degrees * (3.1415926535897932 / 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _parseInstruction(String htmlInstruction) {
|
||||||
|
return htmlInstruction.replaceAll(RegExp(r'<[^>]*>'), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkDestinationProximity() {
|
||||||
|
final distance = calculateDistance(
|
||||||
|
myLocation.latitude,
|
||||||
|
myLocation.longitude,
|
||||||
|
latLngPassengerDestination.latitude,
|
||||||
|
latLngPassengerDestination.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance < 300) {
|
||||||
|
// 300 متر قبل الوجهة
|
||||||
|
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||||
|
"You are near the destination".tr,
|
||||||
|
"You are near the destination".tr,
|
||||||
|
tokenPassenger,
|
||||||
|
[
|
||||||
|
box.read(BoxName.driverID),
|
||||||
|
rideId,
|
||||||
|
box.read(BoxName.tokenDriver),
|
||||||
|
paymentAmount.toString()
|
||||||
|
],
|
||||||
|
'ding.wav',
|
||||||
|
);
|
||||||
|
// يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getMapDestination(String origin, destination) async {
|
getMapDestination(String origin, destination) async {
|
||||||
var url =
|
var url =
|
||||||
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
|
('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
|
||||||
@@ -1243,6 +1334,22 @@ class MapDriverController extends GetxController {
|
|||||||
double lng = points[i][1].toDouble();
|
double lng = points[i][1].toDouble();
|
||||||
polylineCoordinatesDestination.add(LatLng(lat, lng));
|
polylineCoordinatesDestination.add(LatLng(lat, lng));
|
||||||
}
|
}
|
||||||
|
// استخراج الخطوات
|
||||||
|
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
|
||||||
|
Log.print('routeSteps: ${routeSteps}');
|
||||||
|
currentStepIndex = 0;
|
||||||
|
if (routeSteps.isNotEmpty) {
|
||||||
|
currentInstruction =
|
||||||
|
_parseInstruction(routeSteps[0]['html_instructions']);
|
||||||
|
Log.print('currentInstruction: ${currentInstruction}');
|
||||||
|
Get.isRegistered<TextToSpeechController>()
|
||||||
|
? Get.find<TextToSpeechController>().speakText(currentInstruction)
|
||||||
|
: Get.put(TextToSpeechController()).speakText(currentInstruction);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
|
||||||
|
// دالة مساعدة لتنظيف التعليمات
|
||||||
|
|
||||||
if (polyLinesDestination.isNotEmpty) {
|
if (polyLinesDestination.isNotEmpty) {
|
||||||
// clearPolyline();
|
// clearPolyline();
|
||||||
var polyline = Polyline(
|
var polyline = Polyline(
|
||||||
@@ -1404,6 +1511,7 @@ class MapDriverController extends GetxController {
|
|||||||
hours = durationToAdd.inHours;
|
hours = durationToAdd.inHours;
|
||||||
minutes = (durationToAdd.inMinutes % 60).round();
|
minutes = (durationToAdd.inMinutes % 60).round();
|
||||||
calculateConsumptionFuel();
|
calculateConsumptionFuel();
|
||||||
|
updateLocation();
|
||||||
// cancelCheckRidefromPassenger();
|
// cancelCheckRidefromPassenger();
|
||||||
// checkIsDriverNearPassenger();
|
// checkIsDriverNearPassenger();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|||||||
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": "البنك التجاري وفا مصر",
|
"ATTIJARIWAFA BANK Egypt": "البنك التجاري وفا مصر",
|
||||||
"Morning Promo": "بونص الصباح",
|
"Morning Promo": "بونص الصباح",
|
||||||
"Show my Cars": "عرض سياراتي",
|
"Show my Cars": "عرض سياراتي",
|
||||||
"Add criminal page": "إضافة الفيش الجنائي",
|
'Behavior Score': "درجة السلوك",
|
||||||
|
"Driver Behavior": "سلوك السائق",
|
||||||
|
"Last 10 Trips": "آخر 10 رحلات",
|
||||||
|
"Trip ID": "رقم الرحلة", 'Vehicle Details': "تفاصيل المركبة",
|
||||||
|
'Hard Brake': "فرملة قوية",
|
||||||
|
'Show behavior page': "عرض صفحة السلوك",
|
||||||
|
'Coming Soon': "قريبًا",
|
||||||
|
'Logout': "تسجيل الخروج",
|
||||||
|
'Contact Support to Recharge': "تواصل مع الدعم لإعادة الشحن",
|
||||||
|
'Are you sure you want to logout?':
|
||||||
|
"هل أنت متأكد أنك تريد تسجيل الخروج؟",
|
||||||
|
"How to use App": "كيفية استخدام التطبيق",
|
||||||
|
'This service will be available soon.':
|
||||||
|
"هذه الخدمة ستكون متاحة قريبًا.",
|
||||||
|
'Change the app language': "تغيير لغة التطبيق",
|
||||||
|
'Get features for your country': "احصل على ميزات لبلدك",
|
||||||
|
'Vibration feedback for buttons': "ردود فعل الاهتزاز للأزرار",
|
||||||
|
'Run Google Maps directly': "تشغيل خرائط جوجل مباشرة",
|
||||||
|
'reviews': "المراجعات",
|
||||||
|
"Trip Summary with": "ملخص الرحلة مع",
|
||||||
|
"Original Fare": "الأجرة الأصلية",
|
||||||
|
"Your Earnings": "أرباحك",
|
||||||
|
"Exclusive offers and discounts always with the Sefer app":
|
||||||
|
"عروض وخصومات حصرية دائماً مع تطبيق سفر",
|
||||||
|
"Would the passenger like to settle the remaining fare using their wallet?":
|
||||||
|
"هل يرغب الراكب بتسوية الأجرة المتبقية من محفظته؟",
|
||||||
|
"Yes, Pay": "نعم، ادفع",
|
||||||
|
"How much Passenger pay?": "كم دفع الراكب؟",
|
||||||
|
"Passenger paid amount": "المبلغ الذي دفعه الراكب",
|
||||||
|
"Add to Passenger Wallet": "أضف إلى محفظة الراكب",
|
||||||
|
"How was the passenger?": "كيف كان الراكب؟",
|
||||||
|
"Add a comment (optional)": "أضف تعليقاً (اختياري)",
|
||||||
|
"Type something...": "اكتب شيئاً...",
|
||||||
|
"Submit rating": "إرسال التقييم",
|
||||||
|
'Trip Summary with': "ملخص الرحلة مع",
|
||||||
|
'Original Fare': "الأجرة الأصلية",
|
||||||
|
'Your Earnings': "أرباحك",
|
||||||
|
'Exclusive offers and discounts always with the Intaleq app':
|
||||||
|
"عروض وخصومات حصرية دائماً مع تطبيق انطلق",
|
||||||
|
'Enter your email'
|
||||||
|
'''Types of Trips in Intaleq:
|
||||||
|
|
||||||
|
- Comfort: For cars newer than 2017 with air conditioning.
|
||||||
|
- Lady: For girl drivers.
|
||||||
|
- Speed: For fixed salary and endpoints.
|
||||||
|
- Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
|
||||||
|
- Raih Gai: For same-day return trips longer than 50km.''':
|
||||||
|
'''أنواع الرحلات في Intaleq:
|
||||||
|
- راحة: للسيارات الأحدث من 2017 مع تكييف الهواء.
|
||||||
|
- للسائقات الإناث.
|
||||||
|
- سبيد: براتب ثابت ونقاط نهاية محددة.
|
||||||
|
- مشاوير: للرحلات المرنة حيث يختار الركاب السيارة والسائق باتفاق مسبق.
|
||||||
|
- رحّي غاي: للرحلات ذات العودة في نفس اليوم التي تزيد عن 50 كم.''',
|
||||||
|
|
||||||
|
'''Intaleq Wallet Features:
|
||||||
|
|
||||||
|
- Transfer money multiple times.
|
||||||
|
- Transfer to anyone.
|
||||||
|
- Make purchases.
|
||||||
|
- Charge your account.
|
||||||
|
- Charge a friend's Intaleq account.
|
||||||
|
- Store your money with us and receive it in your bank as a monthly salary.''':
|
||||||
|
'''ميزات محفظة Intaleq:
|
||||||
|
- تحويل الأموال عدة مرات.
|
||||||
|
- التحويل إلى أي شخص.
|
||||||
|
- إجراء عمليات شراء.
|
||||||
|
- شحن حسابك.
|
||||||
|
- شحن حساب Intaleq لصديق.
|
||||||
|
- قم بتخزين أموالك معنا واستلامها في بنكك كراتب شهري.''',
|
||||||
|
'Are you sure you want to logout?':
|
||||||
|
"هل أنت متأكد أنك تريد تسجيل الخروج؟",
|
||||||
|
'My Cars': "سياراتي",
|
||||||
|
'Bank Account': "الحساب البنكي",
|
||||||
|
'Behavior Page': "صفحة السلوك",
|
||||||
|
'Personal Information': "المعلومات الشخصية",
|
||||||
|
"Add criminal page": "إضافة صفحة جنائية",
|
||||||
|
"Overall Behavior Score": "درجة السلوك العامة",
|
||||||
"Add new car": "إضافة سيارة جديدة",
|
"Add new car": "إضافة سيارة جديدة",
|
||||||
"You have gift 300 L.E": "لديك هدية بقيمة 300 جنيه.",
|
"You have gift 300 L.E": "لديك هدية بقيمة 300 جنيه.",
|
||||||
// "VIP Order": "طلب VIP",
|
// "VIP Order": "طلب VIP",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:sefer_driver/constant/links.dart';
|
|||||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||||
import 'package:sefer_driver/main.dart';
|
import 'package:sefer_driver/main.dart';
|
||||||
|
|
||||||
|
import '../../views/widgets/error_snakbar.dart';
|
||||||
|
|
||||||
class CaptainProfileController extends GetxController {
|
class CaptainProfileController extends GetxController {
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
TextEditingController vin = TextEditingController();
|
TextEditingController vin = TextEditingController();
|
||||||
@@ -15,6 +17,25 @@ class CaptainProfileController extends GetxController {
|
|||||||
TextEditingController model = TextEditingController();
|
TextEditingController model = TextEditingController();
|
||||||
TextEditingController year = TextEditingController();
|
TextEditingController year = TextEditingController();
|
||||||
TextEditingController expirationDate = TextEditingController();
|
TextEditingController expirationDate = TextEditingController();
|
||||||
|
final TextEditingController emailController = TextEditingController();
|
||||||
|
|
||||||
|
updateEmail() async {
|
||||||
|
var payload = {
|
||||||
|
'id': box.read(BoxName.driverID).toString(),
|
||||||
|
'email': emailController.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
var res =
|
||||||
|
await CRUD().post(link: AppLink.updateDriverEmail, payload: payload);
|
||||||
|
|
||||||
|
if ((res)['status'] == 'success') {
|
||||||
|
box.write(BoxName.email, emailController.text);
|
||||||
|
update();
|
||||||
|
Get.back();
|
||||||
|
} else {
|
||||||
|
mySnackeBarError((res)['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future updateFields() async {
|
Future updateFields() async {
|
||||||
var payload = {
|
var payload = {
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sefer_driver/constant/colors.dart';
|
import 'package:sefer_driver/constant/colors.dart';
|
||||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
|
||||||
|
|
||||||
import '../../constant/style.dart';
|
import '../../constant/style.dart';
|
||||||
import '../../controller/rate/rate_conroller.dart';
|
import '../../controller/rate/rate_conroller.dart';
|
||||||
|
|
||||||
|
// Changed: تم إعادة بناء الصفحة بالكامل لتحسين التصميم وتجربة المستخدم
|
||||||
class RatePassenger extends StatelessWidget {
|
class RatePassenger extends StatelessWidget {
|
||||||
final RateController controller = Get.put(RateController());
|
final RateController controller = Get.put(RateController());
|
||||||
|
|
||||||
@@ -18,241 +17,313 @@ class RatePassenger extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MyScafolld(
|
// New: استخدام Scaffold القياسي لهيكل أكثر قوة ومرونة
|
||||||
title: 'Rate Passenger'.tr,
|
return Scaffold(
|
||||||
body: [
|
appBar: AppBar(
|
||||||
GetBuilder<RateController>(builder: (controller) {
|
title: Text('Rate Passenger'.tr),
|
||||||
return Positioned(
|
centerTitle: true,
|
||||||
top: 40,
|
automaticallyImplyLeading: false, // New: إزالة سهم الرجوع
|
||||||
left: Get.width * .1,
|
backgroundColor: Colors.white,
|
||||||
right: Get.width * .1,
|
elevation: 1,
|
||||||
child: Container(
|
),
|
||||||
decoration: AppStyle.boxDecoration,
|
// New: استخدام GetBuilder على مستوى الجسم لضمان تحديث الواجهة
|
||||||
child: Column(
|
body: GetBuilder<RateController>(
|
||||||
children: [
|
builder: (controller) {
|
||||||
Padding(
|
// New: استخدام SingleChildScrollView لتجنب مشاكل الـ overflow عند ظهور لوحة المفاتيح
|
||||||
padding: const EdgeInsets.all(4),
|
return SingleChildScrollView(
|
||||||
child: Container(
|
child: Padding(
|
||||||
height: Get.height * .25,
|
padding: const EdgeInsets.all(16.0),
|
||||||
decoration: AppStyle.boxDecoration1,
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
// New: استدعاء ودجت منفصلة لكل قسم لزيادة التنظيم
|
||||||
Text(
|
_buildPriceSummaryCard(context, controller),
|
||||||
'${'Total price from '.tr}${Get.find<MapDriverController>().passengerName}',
|
const SizedBox(height: 16),
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
// New: قسم المحفظة يظهر فقط إذا لم يتم التحقق منه
|
||||||
Container(
|
if (controller.walletChecked != 'true')
|
||||||
decoration: BoxDecoration(
|
_buildWalletSection(context, controller),
|
||||||
border: Border.all(
|
|
||||||
width: 2,
|
const SizedBox(height: 16),
|
||||||
color: AppColor.redColor,
|
|
||||||
)),
|
_buildRatingSection(context, controller),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(4),
|
const SizedBox(height: 24),
|
||||||
child: Text(
|
|
||||||
(double.parse(controller.price.toString()) -
|
MyElevatedButton(
|
||||||
double.parse(controller.price
|
title: 'Submit rating'.tr,
|
||||||
.toString()) *
|
onPressed: () => controller.addRateToPassenger(),
|
||||||
.12)
|
// New: جعل الزر يأخذ العرض الكامل لمزيد من الوضوح
|
||||||
.toStringAsFixed(2),
|
// isFullWidth: true,
|
||||||
style: AppStyle.number.copyWith(
|
),
|
||||||
color: AppColor.redColor,
|
],
|
||||||
textBaseline: TextBaseline.ideographic,
|
),
|
||||||
decoration: TextDecoration.lineThrough,
|
),
|
||||||
decorationColor: AppColor.redColor),
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
const SizedBox(
|
}
|
||||||
height: 10,
|
|
||||||
),
|
// New: ودجت منفصلة لعرض بطاقة ملخص السعر
|
||||||
Container(
|
Widget _buildPriceSummaryCard(
|
||||||
decoration: BoxDecoration(
|
BuildContext context, RateController controller) {
|
||||||
border: Border.all(
|
final MapDriverController mapController = Get.find<MapDriverController>();
|
||||||
width: 2,
|
final double originalPrice =
|
||||||
color: AppColor.greenColor,
|
double.tryParse(controller.price.toString()) ?? 0.0;
|
||||||
)),
|
final double priceAfterDiscount = originalPrice - (originalPrice * 0.12);
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(4),
|
return Card(
|
||||||
child: Text(
|
elevation: 4,
|
||||||
Get.find<MapDriverController>()
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
.paymentAmount,
|
child: Padding(
|
||||||
style: AppStyle.number,
|
padding: const EdgeInsets.all(16.0),
|
||||||
),
|
child: Column(
|
||||||
),
|
children: [
|
||||||
),
|
Text(
|
||||||
const SizedBox(
|
'${'Trip Summary with'.tr} ${mapController.passengerName}',
|
||||||
height: 10,
|
style: AppStyle.title
|
||||||
),
|
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
Text(
|
textAlign: TextAlign.center,
|
||||||
'Exclusive offers and discounts always with the Sefer app'
|
),
|
||||||
.tr,
|
const Divider(height: 24, thickness: 1),
|
||||||
textAlign: TextAlign.center,
|
Row(
|
||||||
style: AppStyle.title
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
.copyWith(color: AppColor.redColor),
|
children: [
|
||||||
)
|
Text('Original Fare'.tr, style: AppStyle.title),
|
||||||
],
|
Text(
|
||||||
)),
|
priceAfterDiscount.toStringAsFixed(2),
|
||||||
),
|
style: AppStyle.number.copyWith(
|
||||||
controller.walletChecked != 'true'
|
fontSize: 16,
|
||||||
? controller.ispassengerWantWalletFromDriver
|
color: AppColor.redColor,
|
||||||
? Container(
|
decoration: TextDecoration.lineThrough,
|
||||||
decoration: AppStyle.boxDecoration1,
|
),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"How much Passenger pay?".tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
Form(
|
|
||||||
key: controller.formKey,
|
|
||||||
child: MyTextForm(
|
|
||||||
controller:
|
|
||||||
controller.passengerPayAmount,
|
|
||||||
label: "passenger amount to me".tr,
|
|
||||||
hint: "passenger amount to me".tr,
|
|
||||||
type: const TextInputType
|
|
||||||
.numberWithOptions(decimal: true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MyElevatedButton(
|
|
||||||
title: "Press here".tr,
|
|
||||||
onPressed: () {
|
|
||||||
controller.addPassengerWallet();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
width: Get.width * .73,
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Would the passenger like to settle the remaining fare using their wallet?"
|
|
||||||
.tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
MyElevatedButton(
|
|
||||||
title: "Press here".tr,
|
|
||||||
onPressed: () {
|
|
||||||
controller.passengerWantPay();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: RatingBar.builder(
|
|
||||||
initialRating: 0,
|
|
||||||
itemCount: 5,
|
|
||||||
itemSize: 50,
|
|
||||||
itemPadding: const EdgeInsets.symmetric(horizontal: 2),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_very_dissatisfied,
|
|
||||||
color: Colors.red,
|
|
||||||
);
|
|
||||||
case 1:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_dissatisfied,
|
|
||||||
color: Colors.redAccent,
|
|
||||||
);
|
|
||||||
case 2:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_neutral,
|
|
||||||
color: Colors.amber,
|
|
||||||
);
|
|
||||||
case 3:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_satisfied,
|
|
||||||
color: Colors.lightGreen,
|
|
||||||
);
|
|
||||||
case 4:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_very_satisfied,
|
|
||||||
color: Colors.green,
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return const Icon(
|
|
||||||
Icons.sentiment_neutral,
|
|
||||||
color: Colors.amber,
|
|
||||||
);
|
|
||||||
} //
|
|
||||||
},
|
|
||||||
onRatingUpdate: (rating) {
|
|
||||||
controller.selectRateItem(rating);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: Get.width * .75,
|
|
||||||
child: TextFormField(
|
|
||||||
maxLines: 4,
|
|
||||||
minLines: 1,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
controller: controller.comment,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Enter your Note'.tr,
|
|
||||||
hintText: 'Type something...',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
Icons.rate_review), // Add an icon as a prefix
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.clear,
|
|
||||||
color: AppColor.redColor,
|
|
||||||
), // Add an icon as a suffix
|
|
||||||
onPressed: () {
|
|
||||||
controller.comment.clear();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
border:
|
|
||||||
const OutlineInputBorder(), // Add a border around the input field
|
|
||||||
enabledBorder: const OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color:
|
|
||||||
Colors.blue), // Customize the border color
|
|
||||||
),
|
|
||||||
focusedBorder: const OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Colors
|
|
||||||
.green), // Customize the border color when focused
|
|
||||||
),
|
|
||||||
errorBorder: const OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Colors
|
|
||||||
.red), // Customize the border color when there's an error
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
MyElevatedButton(
|
|
||||||
title: 'Submit rating'.tr,
|
|
||||||
onPressed: () => controller.addRateToPassenger())
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
));
|
],
|
||||||
}),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Your Earnings'.tr,
|
||||||
|
style:
|
||||||
|
AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.greenColor.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: AppColor.greenColor),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
mapController.paymentAmount,
|
||||||
|
style: AppStyle.number
|
||||||
|
.copyWith(color: AppColor.greenColor, fontSize: 20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
'Exclusive offers and discounts always with the Sefer app'.tr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: AppStyle.title
|
||||||
|
.copyWith(color: AppColor.redColor, fontSize: 13),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: ودجت منفصلة لقسم الدفع عبر المحفظة
|
||||||
|
Widget _buildWalletSection(BuildContext context, RateController controller) {
|
||||||
|
return Card(
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: controller.ispassengerWantWalletFromDriver
|
||||||
|
? _buildAmountInput(controller)
|
||||||
|
: _buildWalletQuery(controller),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: واجهة سؤال استخدام المحفظة
|
||||||
|
Widget _buildWalletQuery(RateController controller) {
|
||||||
|
return Column(
|
||||||
|
key: const ValueKey('walletQuery'),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Would the passenger like to settle the remaining fare using their wallet?"
|
||||||
|
.tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: MyElevatedButton(
|
||||||
|
title: 'No'.tr,
|
||||||
|
onPressed: () {
|
||||||
|
// يمكنك هنا تحديد ما يحدث عند الضغط على "لا"
|
||||||
|
// حاليًا، ستبقى الواجهة كما هي أو يمكنك إخفاؤها
|
||||||
|
},
|
||||||
|
kolor: AppColor.redColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: MyElevatedButton(
|
||||||
|
title: 'Yes, Pay'.tr,
|
||||||
|
onPressed: () {
|
||||||
|
controller.passengerWantPay();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
isleading: false,
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: واجهة إدخال المبلغ المدفوع
|
||||||
|
Widget _buildAmountInput(RateController controller) {
|
||||||
|
return Column(
|
||||||
|
key: const ValueKey('amountInput'),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"How much Passenger pay?".tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Form(
|
||||||
|
key: controller.formKey,
|
||||||
|
child: MyTextForm(
|
||||||
|
controller: controller.passengerPayAmount,
|
||||||
|
label: "Passenger paid amount".tr,
|
||||||
|
hint: "0.00",
|
||||||
|
type: const TextInputType.numberWithOptions(decimal: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
MyElevatedButton(
|
||||||
|
title: "Add to Passenger Wallet".tr,
|
||||||
|
// isFullWidth: true,
|
||||||
|
onPressed: () {
|
||||||
|
controller.addPassengerWallet();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: ودجت منفصلة لقسم التقييم وكتابة الملاحظات
|
||||||
|
Widget _buildRatingSection(BuildContext context, RateController controller) {
|
||||||
|
return Card(
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text('How was the passenger?'.tr,
|
||||||
|
style: AppStyle.title
|
||||||
|
.copyWith(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
RatingBar.builder(
|
||||||
|
initialRating: 0,
|
||||||
|
itemCount: 5,
|
||||||
|
itemSize: 50,
|
||||||
|
itemPadding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return const Icon(Icons.sentiment_very_dissatisfied,
|
||||||
|
color: Colors.red);
|
||||||
|
case 1:
|
||||||
|
return const Icon(Icons.sentiment_dissatisfied,
|
||||||
|
color: Colors.redAccent);
|
||||||
|
case 2:
|
||||||
|
return const Icon(Icons.sentiment_neutral,
|
||||||
|
color: Colors.amber);
|
||||||
|
case 3:
|
||||||
|
return const Icon(Icons.sentiment_satisfied,
|
||||||
|
color: Colors.lightGreen);
|
||||||
|
case 4:
|
||||||
|
return const Icon(Icons.sentiment_very_satisfied,
|
||||||
|
color: Colors.green);
|
||||||
|
default:
|
||||||
|
return const Icon(Icons.sentiment_neutral,
|
||||||
|
color: Colors.amber);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRatingUpdate: (rating) {
|
||||||
|
controller.selectRateItem(rating);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
TextFormField(
|
||||||
|
maxLines: 4,
|
||||||
|
minLines: 2,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
controller: controller.comment,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Add a comment (optional)'.tr,
|
||||||
|
hintText: 'Type something...'.tr,
|
||||||
|
prefixIcon: const Icon(Icons.rate_review_outlined),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New: إضافة isFullWidth إلى MyElevatedButton لتسهيل التحكم في العرض
|
||||||
|
// تأكد من تحديث ملف elevated_btn.dart بهذا التغيير
|
||||||
|
/*
|
||||||
|
class MyElevatedButton extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final Color? kolor;
|
||||||
|
final bool isFullWidth; // New property
|
||||||
|
|
||||||
|
const MyElevatedButton({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
required this.onPressed,
|
||||||
|
this.kolor,
|
||||||
|
this.isFullWidth = false, // Default value
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: isFullWidth ? double.infinity : null, // Apply width
|
||||||
|
height: 50, // Standard height
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: kolor ?? AppColor.primaryColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
textStyle: AppStyle.title.copyWith(color: Colors.white),
|
||||||
|
),
|
||||||
|
child: Text(title, style: const TextStyle(color: Colors.white)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -154,12 +154,13 @@ class AuthScreen extends StatelessWidget {
|
|||||||
.formKey.currentState!
|
.formKey.currentState!
|
||||||
.validate()) {
|
.validate()) {
|
||||||
controller.logintest(
|
controller.logintest(
|
||||||
controller
|
controller
|
||||||
.emailController.text
|
.passwordController.text
|
||||||
.trim(),
|
.trim(),
|
||||||
controller
|
controller
|
||||||
.passwordController.text
|
.emailController.text
|
||||||
.trim());
|
.trim(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'package:sefer_driver/constant/colors.dart';
|
|
||||||
import 'package:sefer_driver/controller/profile/setting_controller.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sefer_driver/constant/style.dart';
|
import 'package:sefer_driver/constant/colors.dart';
|
||||||
|
import 'package:sefer_driver/controller/profile/setting_controller.dart';
|
||||||
import 'package:sefer_driver/views/lang/languages.dart';
|
import 'package:sefer_driver/views/lang/languages.dart';
|
||||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||||
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
|
|
||||||
import '../../../../controller/functions/vibrate.dart';
|
// تأكد من صحة مسارات الاستيراد هذه
|
||||||
|
import '../../../../controller/functions/vibrate.dart'; // Controller with isVibrate
|
||||||
import '../../../auth/country_widget.dart';
|
import '../../../auth/country_widget.dart';
|
||||||
import 'about_us.dart';
|
import 'about_us.dart';
|
||||||
import 'frequantly_question.dart';
|
import 'frequantly_question.dart';
|
||||||
@@ -18,140 +18,188 @@ class SettingsCaptain extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(SettingController());
|
// تحميل الـ Controllers المطلوبة
|
||||||
Get.put(HomePageController());
|
final settingsController = Get.put(SettingController());
|
||||||
|
final homeController = Get.put(HomePageController());
|
||||||
|
|
||||||
return MyScafolld(
|
return MyScafolld(
|
||||||
title: 'Settings'.tr,
|
title: 'Settings'.tr,
|
||||||
|
isleading: true,
|
||||||
body: [
|
body: [
|
||||||
ListView(
|
ListView(
|
||||||
physics: const BouncingScrollPhysics(),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// General Section
|
// --- القسم الأول: عام ---
|
||||||
_buildSectionHeader('General'.tr),
|
_buildSectionHeader('General'.tr, context),
|
||||||
CupertinoListSection(
|
_buildSettingsCard(
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
children: [
|
children: [
|
||||||
CupertinoListTile(
|
_buildListTile(
|
||||||
leading: const Icon(CupertinoIcons.globe),
|
icon: Icons.language_outlined,
|
||||||
title: Text('Language'.tr, style: AppStyle.headTitle2),
|
title: 'Language'.tr,
|
||||||
subtitle: Text('You can change the language of the app'.tr,
|
subtitle: 'Change the app language'.tr,
|
||||||
style: AppStyle.subtitle),
|
onTap: () => Get.to(() => const Language()),
|
||||||
trailing: const CupertinoListTileChevron(),
|
|
||||||
onTap: () => Get.to(const Language()),
|
|
||||||
),
|
),
|
||||||
CupertinoListTile(
|
_buildListTile(
|
||||||
leading: const Icon(CupertinoIcons.flag_fill),
|
icon: Icons.flag_outlined,
|
||||||
title: Text('Change Country'.tr, style: AppStyle.headTitle2),
|
title: 'Change Country'.tr,
|
||||||
subtitle: Text(
|
subtitle: 'Get features for your country'.tr,
|
||||||
'You can change the Country to get all features'.tr,
|
|
||||||
style: AppStyle.subtitle),
|
|
||||||
trailing: const CupertinoListTileChevron(),
|
|
||||||
onTap: () => Get.to(
|
onTap: () => Get.to(
|
||||||
MyScafolld(
|
() => MyScafolld(
|
||||||
title: 'Change Country'.tr,
|
title: 'Change Country'.tr,
|
||||||
body: [CountryPickerFromSetting()],
|
body: [CountryPickerFromSetting()],
|
||||||
isleading: true,
|
isleading: true,
|
||||||
// isCupertino: true, // Indicate it's a Cupertino style page
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// App Preferences Section
|
// --- القسم الثاني: تفضيلات التطبيق ---
|
||||||
_buildSectionHeader('App Preferences'.tr),
|
_buildSectionHeader('App Preferences'.tr, context),
|
||||||
CupertinoListSection(
|
_buildSettingsCard(
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
children: [
|
children: [
|
||||||
CupertinoListTile(
|
_buildSwitchTile(
|
||||||
leading: Icon(
|
icon: Icons.map_outlined,
|
||||||
CupertinoIcons.map_pin_ellipse,
|
color: AppColor.redColor,
|
||||||
color: AppColor.redColor,
|
title: 'Google Map App'.tr,
|
||||||
),
|
subtitle: 'Run Google Maps directly'.tr,
|
||||||
title: Text('Google Map App'.tr, style: AppStyle.headTitle2),
|
controller: settingsController,
|
||||||
subtitle: Text(
|
valueGetter: (ctrl) => (ctrl).isGoogleMapsEnabled,
|
||||||
'If you want to make Google Map App run directly when you apply order'
|
onChanged: (ctrl) => (ctrl).onChangMapApp(),
|
||||||
.tr,
|
|
||||||
style: AppStyle.subtitle,
|
|
||||||
),
|
|
||||||
trailing: GetBuilder<SettingController>(
|
|
||||||
builder: (settingController) {
|
|
||||||
return CupertinoSwitch(
|
|
||||||
value: settingController.isGoogleMapsEnabled,
|
|
||||||
activeTrackColor: AppColor.primaryColor,
|
|
||||||
onChanged: (bool value) {
|
|
||||||
settingController.onChangMapApp();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
CupertinoListTile(
|
_buildSwitchTile(
|
||||||
leading: Icon(Icons.vibration),
|
icon: Icons.vibration,
|
||||||
title: Text('Vibration'.tr, style: AppStyle.headTitle2),
|
title: 'Vibration'.tr,
|
||||||
subtitle: Text(
|
subtitle: 'Vibration feedback for buttons'.tr,
|
||||||
"You can change the vibration feedback for all buttons".tr,
|
controller: homeController,
|
||||||
style: AppStyle.subtitle,
|
valueGetter: (ctrl) => (ctrl).isVibrate,
|
||||||
),
|
onChanged: (ctrl) => (ctrl)
|
||||||
trailing: GetBuilder<HomePageController>(
|
.changeVibrateOption(true), // قد تحتاج لتعديل الدالة
|
||||||
builder: (controller) => CupertinoSwitch(
|
|
||||||
value: controller.isVibrate,
|
|
||||||
onChanged: controller.changeVibrateOption,
|
|
||||||
activeTrackColor: AppColor.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () => print('3'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// Help & Support Section
|
// --- القسم الثالث: المساعدة والدعم ---
|
||||||
_buildSectionHeader('Help & Support'.tr),
|
_buildSectionHeader('Help & Support'.tr, context),
|
||||||
CupertinoListSection(
|
_buildSettingsCard(
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
children: [
|
children: [
|
||||||
CupertinoListTile(
|
_buildListTile(
|
||||||
leading: const Icon(CupertinoIcons.question_circle_fill),
|
icon: Icons.quiz_outlined,
|
||||||
title: Text('Frequently Questions'.tr,
|
title: 'Frequently Questions'.tr,
|
||||||
style: AppStyle.headTitle2),
|
|
||||||
trailing: const CupertinoListTileChevron(),
|
|
||||||
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
|
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
|
||||||
),
|
),
|
||||||
CupertinoListTile(
|
_buildListTile(
|
||||||
leading: const Icon(CupertinoIcons.hand_raised_fill),
|
icon: Icons.support_agent,
|
||||||
title:
|
title: "How to use App".tr,
|
||||||
Text("How to use Intaleq".tr, style: AppStyle.headTitle2),
|
|
||||||
trailing: const CupertinoListTileChevron(),
|
|
||||||
onTap: () => Get.to(() => const UsingAppPage()),
|
onTap: () => Get.to(() => const UsingAppPage()),
|
||||||
),
|
),
|
||||||
CupertinoListTile(
|
_buildListTile(
|
||||||
leading: const Icon(CupertinoIcons.info_circle_fill),
|
icon: Icons.info_outline,
|
||||||
title: Text('About Us'.tr, style: AppStyle.headTitle2),
|
title: 'About Us'.tr,
|
||||||
trailing: const CupertinoListTileChevron(),
|
|
||||||
onTap: () => Get.to(() => const AboutPage()),
|
onTap: () => Get.to(() => const AboutPage()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// --- القسم الرابع: تسجيل الخروج ---
|
||||||
|
_buildSectionHeader('Account'.tr, context),
|
||||||
|
_buildSettingsCard(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.logout, color: Colors.red),
|
||||||
|
title: Text(
|
||||||
|
'Logout'.tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.red, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
'Logout'.tr,
|
||||||
|
'Are you sure you want to logout?'.tr,
|
||||||
|
() {
|
||||||
|
// أضف دالة تسجيل الخروج هنا
|
||||||
|
Get.back(); // لإغلاق مربع الحوار
|
||||||
|
},
|
||||||
|
// isConfirmation: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
isleading: true,
|
|
||||||
// isCupertino: true, // Indicate this screen is generally Cupertino style
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSectionHeader(String title) {
|
// ويدجت لبناء عنوان كل قسم
|
||||||
|
Widget _buildSectionHeader(String title, BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0, top: 20.0, bottom: 10.0),
|
padding: const EdgeInsets.only(left: 8.0, bottom: 12.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: const TextStyle(
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||||
fontSize: 17.0,
|
color: Colors.grey.shade600,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.bold,
|
||||||
color: CupertinoColors.secondaryLabel,
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ويدجت لبناء بطاقة الإعدادات
|
||||||
|
Widget _buildSettingsCard({required List<Widget> children}) {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shadowColor: Colors.black.withOpacity(0.1),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
clipBehavior: Clip.antiAlias, // مهم لجعل splash effect داخل حدود البطاقة
|
||||||
|
child: Column(children: children),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ويدجت لبناء عنصر قابل للضغط (مثل اللغة، عن التطبيق)
|
||||||
|
Widget _buildListTile({
|
||||||
|
required IconData icon,
|
||||||
|
required String title,
|
||||||
|
String? subtitle,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Icon(icon, color: Colors.grey.shade700),
|
||||||
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||||
|
subtitle: subtitle != null ? Text(subtitle) : null,
|
||||||
|
trailing:
|
||||||
|
const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey),
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ويدجت لبناء عنصر يحتوي على مفتاح تفعيل/إلغاء (Switch)
|
||||||
|
Widget _buildSwitchTile<T extends GetxController>({
|
||||||
|
required IconData icon,
|
||||||
|
Color? color,
|
||||||
|
required String title,
|
||||||
|
required String subtitle,
|
||||||
|
required T controller,
|
||||||
|
required bool Function(T) valueGetter,
|
||||||
|
required Function(T) onChanged,
|
||||||
|
}) {
|
||||||
|
return GetBuilder<T>(
|
||||||
|
init: controller,
|
||||||
|
builder: (ctrl) {
|
||||||
|
return SwitchListTile(
|
||||||
|
secondary: Icon(icon, color: color ?? Colors.grey.shade700),
|
||||||
|
title:
|
||||||
|
Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||||
|
subtitle: Text(subtitle),
|
||||||
|
value: valueGetter(ctrl),
|
||||||
|
onChanged: (value) => onChanged(ctrl),
|
||||||
|
activeColor: AppColor.primaryColor,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,112 +1,112 @@
|
|||||||
import 'package:sefer_driver/constant/style.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||||
|
|
||||||
|
// 1. إنشاء Class لتمثيل بيانات كل سؤال وجواب
|
||||||
|
class FaqItem {
|
||||||
|
final String question;
|
||||||
|
final Widget answer; // استخدام Widget يسمح بوضع نصوص أو صور
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
FaqItem({required this.question, required this.answer, required this.icon});
|
||||||
|
}
|
||||||
|
|
||||||
class UsingAppPage extends StatelessWidget {
|
class UsingAppPage extends StatelessWidget {
|
||||||
const UsingAppPage({super.key});
|
const UsingAppPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MyScafolld(
|
// 2. تجهيز قائمة البيانات بشكل منظم
|
||||||
title: "How to use Intaleq".tr,
|
final List<FaqItem> faqItems = [
|
||||||
body: [
|
FaqItem(
|
||||||
SizedBox(
|
question: "What are the order details we provide to you?".tr,
|
||||||
child: Padding(
|
icon: Icons.receipt_long_outlined,
|
||||||
padding: const EdgeInsets.all(8.0),
|
answer: Padding(
|
||||||
child: ListView(
|
padding: const EdgeInsets.all(8.0),
|
||||||
children: [
|
child: Image.network(
|
||||||
InkWell(
|
'https://api.tripz-egypt.com/tripz/imageForUsingApp/order_page.jpg',
|
||||||
onTap: () {
|
fit: BoxFit.cover,
|
||||||
MyDialogContent().getDialog(
|
// يمكنك إضافة مؤشر تحميل هنا
|
||||||
"What are the order details we provide to you?".tr,
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
Image.network(
|
if (loadingProgress == null) return child;
|
||||||
'https://api.Intaleq-egypt.com/Intaleq/imageForUsingApp/order_page.jpg',
|
return const Center(child: CircularProgressIndicator());
|
||||||
height: 300,
|
},
|
||||||
width: 300,
|
errorBuilder: (context, error, stackTrace) {
|
||||||
fit: BoxFit.cover,
|
return const Center(
|
||||||
), () {
|
child:
|
||||||
Get.back();
|
Icon(Icons.error_outline, color: Colors.red, size: 40));
|
||||||
});
|
},
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"What are the order details we provide to you?".tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
MyDialog().getDialog(
|
|
||||||
"What are the order details we provide to you?".tr,
|
|
||||||
'''Intaleq Wallet Features:
|
|
||||||
|
|
||||||
Transfer money multiple times.
|
|
||||||
Transfer to anyone.
|
|
||||||
Make purchases.
|
|
||||||
Charge your account.
|
|
||||||
Charge a friend's Intaleq account.
|
|
||||||
Store your money with us and receive it in your bank as a monthly salary.'''
|
|
||||||
.tr, () {
|
|
||||||
Get.back();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"What is the feature of our wallet?".tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
MyDialog().getDialog(
|
|
||||||
"What is Types of Trips in Intaleq?".tr,
|
|
||||||
'''Types of Trips in Intaleq:
|
|
||||||
|
|
||||||
Comfort: For cars newer than 2017 with air conditioning.
|
|
||||||
Lady: For girl drivers.
|
|
||||||
Speed: For fixed salary and endpoints.
|
|
||||||
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
|
|
||||||
Raih Gai: For same-day return trips longer than 50km.
|
|
||||||
'''
|
|
||||||
.tr, () {
|
|
||||||
Get.back();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"What is Types of Trips in Intaleq?".tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
|
FaqItem(
|
||||||
|
question: "What is the feature of our wallet?".tr,
|
||||||
|
icon: Icons.account_balance_wallet_outlined,
|
||||||
|
answer: Text(
|
||||||
|
'''Intaleq Wallet Features:
|
||||||
|
|
||||||
|
- Transfer money multiple times.
|
||||||
|
- Transfer to anyone.
|
||||||
|
- Make purchases.
|
||||||
|
- Charge your account.
|
||||||
|
- Charge a friend's Intaleq account.
|
||||||
|
- Store your money with us and receive it in your bank as a monthly salary.'''
|
||||||
|
.tr,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FaqItem(
|
||||||
|
question: "What is Types of Trips in Intaleq?".tr,
|
||||||
|
icon: Icons.map_outlined,
|
||||||
|
answer: Text(
|
||||||
|
'''Types of Trips in Intaleq:
|
||||||
|
|
||||||
|
- Comfort: For cars newer than 2017 with air conditioning.
|
||||||
|
- Lady: For girl drivers.
|
||||||
|
- Speed: For fixed salary and endpoints.
|
||||||
|
- Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
|
||||||
|
- Raih Gai: For same-day return trips longer than 50km.'''
|
||||||
|
.tr,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 15, height: 1.5, color: Colors.grey.shade700),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// 3. بناء الواجهة الرسومية باستخدام البيانات
|
||||||
|
return MyScafolld(
|
||||||
|
title: "How to use App".tr, // تم تغيير العنوان ليكون أعم
|
||||||
isleading: true,
|
isleading: true,
|
||||||
|
body: [
|
||||||
|
ListView.separated(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
|
||||||
|
itemCount: faqItems.length,
|
||||||
|
separatorBuilder: (context, index) => const SizedBox(height: 12),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = faqItems[index];
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shadowColor: Colors.black.withOpacity(0.1),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16)),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: ExpansionTile(
|
||||||
|
leading: Icon(item.icon, color: Theme.of(context).primaryColor),
|
||||||
|
title: Text(item.question,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||||
|
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Divider(height: 1),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
item.answer,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'mapDriverWidgets/google_driver_map_page.dart';
|
|||||||
import 'mapDriverWidgets/passenger_info_window.dart';
|
import 'mapDriverWidgets/passenger_info_window.dart';
|
||||||
import 'mapDriverWidgets/sos_connect.dart';
|
import 'mapDriverWidgets/sos_connect.dart';
|
||||||
|
|
||||||
|
// Changed: تم إعادة بناء الصفحة بالكامل لتكون أكثر تنظيمًا
|
||||||
class PassengerLocationMapPage extends StatelessWidget {
|
class PassengerLocationMapPage extends StatelessWidget {
|
||||||
PassengerLocationMapPage({super.key});
|
PassengerLocationMapPage({super.key});
|
||||||
final LocationController locationController = Get.put(LocationController());
|
final LocationController locationController = Get.put(LocationController());
|
||||||
@@ -21,36 +22,44 @@ class PassengerLocationMapPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
|
// New: استخدام addPostFrameCallback لضمان أن تحميل البيانات يتم بعد بناء الواجهة
|
||||||
// نستخدم addPostFrameCallback لضمان أن هذا الكود يعمل بعد اكتمال بناء الإطار الأول
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// هذا يعطي GetX وقته لتجهيز كل شيء
|
if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
// نستدعي دالة التهيئة الجديدة ونمرر لها البيانات
|
|
||||||
mapDriverController.argumentLoading();
|
mapDriverController.argumentLoading();
|
||||||
});
|
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
|
||||||
} else {
|
} else {
|
||||||
// في حال عدم وجود arguments، يجب التعامل مع هذا الخطأ
|
// في حال عدم وجود arguments، يتم التعامل مع هذا الخطأ
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
Get.snackbar("Error", "No order data found.");
|
Get.snackbar("Error", "No order data found.");
|
||||||
Get.back();
|
Get.back();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
mapDriverController.argumentLoading();
|
|
||||||
mapDriverController.startTimerToShowPassengerInfoWindowFromDriver();
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// backgroundColor: AppColor.blueColor,
|
|
||||||
// title: 'Map Passenger'.tr,
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
// 1. الخريطة في الخلفية
|
||||||
GoogleDriverMap(locationController: locationController),
|
GoogleDriverMap(locationController: locationController),
|
||||||
const PassengerInfoWindow(),
|
|
||||||
|
// 2. شريط تعليمات الطريق في الأعلى
|
||||||
|
const InstructionsOfRoads(),
|
||||||
|
|
||||||
|
// 3. زر إلغاء الرحلة في الأعلى يسارًا
|
||||||
CancelWidget(mapDriverController: mapDriverController),
|
CancelWidget(mapDriverController: mapDriverController),
|
||||||
|
|
||||||
|
// 4. نافذة معلومات الراكب في الأسفل (تظهر قبل بدء الرحلة)
|
||||||
|
const PassengerInfoWindow(),
|
||||||
|
|
||||||
|
// 5. شريط معلومات وإنهاء الرحلة (يظهر بعد بدء الرحلة)
|
||||||
driverEndRideBar(),
|
driverEndRideBar(),
|
||||||
|
|
||||||
|
// 6. أزرار الطوارئ والاتصال
|
||||||
const SosConnect(),
|
const SosConnect(),
|
||||||
|
|
||||||
|
// 7. دائرة عرض السرعة
|
||||||
speedCircle(),
|
speedCircle(),
|
||||||
// const GoogleMapApp(),
|
|
||||||
|
// 8. نافذة عرض السعر النهائي (تظهر بعد انتهاء الرحلة)
|
||||||
const PricesWindow(),
|
const PricesWindow(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -58,6 +67,59 @@ class PassengerLocationMapPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New: تصميم جديد لشريط تعليمات الطريق في أعلى الشاشة
|
||||||
|
class InstructionsOfRoads extends StatelessWidget {
|
||||||
|
const InstructionsOfRoads({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GetBuilder<MapDriverController>(
|
||||||
|
builder: (controller) =>
|
||||||
|
// يتم إظهار التعليمات فقط إذا كانت متوفرة
|
||||||
|
controller.currentInstruction.isNotEmpty
|
||||||
|
? Positioned(
|
||||||
|
bottom: 10,
|
||||||
|
left: MediaQuery.of(context).size.width * 0.15,
|
||||||
|
right: MediaQuery.of(context).size.width * 0.15,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.2),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.directions,
|
||||||
|
color: AppColor.primaryColor),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
controller.currentInstruction,
|
||||||
|
style: AppStyle.title.copyWith(fontSize: 16),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox(), // في حالة عدم وجود تعليمات، لا يظهر شيء
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed: تم تعديل تصميم وموضع زر الإلغاء ليكون أيقونة بسيطة في الأعلى
|
||||||
class CancelWidget extends StatelessWidget {
|
class CancelWidget extends StatelessWidget {
|
||||||
const CancelWidget({
|
const CancelWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -70,59 +132,72 @@ class CancelWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
top: 10,
|
top: 10,
|
||||||
left: 5,
|
left: 10,
|
||||||
child: GestureDetector(
|
child: GetBuilder<MapDriverController>(
|
||||||
onTap: () {
|
builder: (controller) {
|
||||||
Get.defaultDialog(
|
// يظهر زر الإلغاء فقط قبل انتهاء الرحلة
|
||||||
title: "Are you sure you want to cancel this trip?".tr,
|
if (controller.isRideFinished) return const SizedBox.shrink();
|
||||||
titleStyle: AppStyle.title,
|
|
||||||
content: Column(
|
return GestureDetector(
|
||||||
children: [
|
onTap: () {
|
||||||
Text("Why do you want to cancel this trip?".tr),
|
Get.defaultDialog(
|
||||||
Form(
|
title: "Are you sure you want to cancel this trip?".tr,
|
||||||
key: mapDriverController.formKeyCancel,
|
titleStyle: AppStyle.title,
|
||||||
child: MyTextForm(
|
content: Column(
|
||||||
controller: mapDriverController.cancelTripCotroller,
|
children: [
|
||||||
label: "Write the reason for canceling the trip".tr,
|
Text("Why do you want to cancel this trip?".tr),
|
||||||
hint: "Write the reason for canceling the trip".tr,
|
Form(
|
||||||
type: TextInputType.name,
|
key: mapDriverController.formKeyCancel,
|
||||||
))
|
child: MyTextForm(
|
||||||
|
controller: mapDriverController.cancelTripCotroller,
|
||||||
|
label: "Write the reason for canceling the trip".tr,
|
||||||
|
hint: "Write the reason for canceling the trip".tr,
|
||||||
|
type: TextInputType.name,
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
confirm: MyElevatedButton(
|
||||||
|
title: 'Ok'.tr,
|
||||||
|
kolor: AppColor.redColor,
|
||||||
|
onPressed: () async {
|
||||||
|
await mapDriverController
|
||||||
|
.cancelTripFromDriverAfterApplied();
|
||||||
|
Get.back();
|
||||||
|
}),
|
||||||
|
cancel: MyElevatedButton(
|
||||||
|
title: 'No'.tr,
|
||||||
|
onPressed: () {
|
||||||
|
Get.back();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.2),
|
||||||
|
blurRadius: 5,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
confirm: MyElevatedButton(
|
child: const Padding(
|
||||||
title: 'Ok'.tr,
|
padding: EdgeInsets.all(8.0),
|
||||||
kolor: AppColor.redColor,
|
child: Icon(
|
||||||
onPressed: () async {
|
Icons.clear,
|
||||||
// todo add cancel and inform passenger to get new driver
|
size: 30,
|
||||||
await mapDriverController
|
color: AppColor.redColor,
|
||||||
.cancelTripFromDriverAfterApplied();
|
),
|
||||||
Get.back();
|
),
|
||||||
}),
|
|
||||||
cancel: MyElevatedButton(
|
|
||||||
title: 'No'.tr,
|
|
||||||
// kolor: AppColor.redColor,
|
|
||||||
onPressed: () {
|
|
||||||
Get.back();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.redColor,
|
|
||||||
borderRadius: BorderRadius.circular(15)),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(3),
|
|
||||||
child: Icon(
|
|
||||||
Icons.clear,
|
|
||||||
size: 40,
|
|
||||||
color: AppColor.secondaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changed: تم تعديل تصميم نافذة السعر لتكون أكثر وضوحًا
|
||||||
class PricesWindow extends StatelessWidget {
|
class PricesWindow extends StatelessWidget {
|
||||||
const PricesWindow({
|
const PricesWindow({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -132,38 +207,43 @@ class PricesWindow extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<MapDriverController>(builder: (mapDriverController) {
|
return GetBuilder<MapDriverController>(builder: (mapDriverController) {
|
||||||
return mapDriverController.isPriceWindow
|
return mapDriverController.isPriceWindow
|
||||||
? Positioned(
|
? Container(
|
||||||
bottom: Get.height * 1.2,
|
color: Colors.black.withOpacity(0.5),
|
||||||
// top: Get.height * 3,
|
child: Center(
|
||||||
left: Get.height * 1,
|
child: Container(
|
||||||
right: Get.height * 1,
|
width: Get.width * 0.8,
|
||||||
child: Container(
|
padding: const EdgeInsets.all(24),
|
||||||
height: Get.height * 3,
|
decoration: BoxDecoration(
|
||||||
decoration: AppStyle.boxDecoration1,
|
color: Colors.white,
|
||||||
child: Column(
|
borderRadius: BorderRadius.circular(20),
|
||||||
children: [
|
),
|
||||||
Container(
|
child: Column(
|
||||||
decoration: AppStyle.boxDecoration1,
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(3),
|
Text(
|
||||||
child: Text(
|
'Total Price is '.tr,
|
||||||
'Total Price is '.tr,
|
style: AppStyle.headTitle2,
|
||||||
style: AppStyle.headTitle2,
|
textAlign: TextAlign.center,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
const SizedBox(height: 10),
|
||||||
)),
|
Text(
|
||||||
const SizedBox(
|
'${mapDriverController.totalPricePassenger} ${'\$'.tr}',
|
||||||
height: 20,
|
style: AppStyle.headTitle2.copyWith(
|
||||||
),
|
color: AppColor.primaryColor, fontSize: 36),
|
||||||
MyElevatedButton(
|
),
|
||||||
title: 'ok'.tr,
|
const SizedBox(
|
||||||
onPressed: () =>
|
height: 20,
|
||||||
Get.to(() => RatePassenger(), arguments: {
|
),
|
||||||
'rideId': mapDriverController.rideId,
|
MyElevatedButton(
|
||||||
'passengerId': mapDriverController.passengerId,
|
title: 'ok'.tr,
|
||||||
'driverId': mapDriverController.driverId
|
onPressed: () =>
|
||||||
}))
|
Get.to(() => RatePassenger(), arguments: {
|
||||||
],
|
'rideId': mapDriverController.rideId,
|
||||||
|
'passengerId': mapDriverController.passengerId,
|
||||||
|
'driverId': mapDriverController.driverId
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,434 +1,170 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
|
import 'package:sefer_driver/constant/box_name.dart';
|
||||||
import 'package:sefer_driver/constant/links.dart';
|
import 'package:sefer_driver/constant/links.dart';
|
||||||
import 'package:sefer_driver/constant/style.dart';
|
import 'package:sefer_driver/controller/functions/upload_image.dart';
|
||||||
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
|
||||||
|
import 'package:sefer_driver/main.dart';
|
||||||
|
|
||||||
|
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
|
||||||
import 'package:sefer_driver/views/Rate/rate_app_page.dart';
|
import 'package:sefer_driver/views/Rate/rate_app_page.dart';
|
||||||
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
|
import 'package:sefer_driver/views/auth/captin/contact_us_page.dart';
|
||||||
import 'package:sefer_driver/views/auth/captin/invite_driver_screen.dart';
|
import 'package:sefer_driver/views/auth/captin/invite_driver_screen.dart';
|
||||||
import 'package:sefer_driver/views/notification/available_rides_page.dart';
|
import 'package:sefer_driver/views/notification/available_rides_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:sefer_driver/constant/box_name.dart';
|
|
||||||
import 'package:sefer_driver/main.dart';
|
|
||||||
import 'package:sefer_driver/views/auth/captin/logout_captain.dart';
|
import 'package:sefer_driver/views/auth/captin/logout_captain.dart';
|
||||||
import 'package:sefer_driver/views/home/Captin/history/history_captain.dart';
|
import 'package:sefer_driver/views/home/Captin/history/history_captain.dart';
|
||||||
import 'package:sefer_driver/views/home/Captin/home_captain/help_captain.dart';
|
import 'package:sefer_driver/views/home/Captin/home_captain/help_captain.dart';
|
||||||
import 'package:sefer_driver/views/home/Captin/About%20Us/settings_captain.dart';
|
import 'package:sefer_driver/views/home/Captin/About Us/settings_captain.dart';
|
||||||
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
|
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
|
||||||
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
|
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
|
||||||
import 'package:sefer_driver/views/notification/notification_captain.dart';
|
import 'package:sefer_driver/views/notification/notification_captain.dart';
|
||||||
|
|
||||||
import '../../../../constant/colors.dart';
|
|
||||||
import '../../../../controller/functions/upload_image.dart';
|
|
||||||
import '../About Us/video_page.dart';
|
import '../About Us/video_page.dart';
|
||||||
import '../assurance_health_page.dart';
|
import '../assurance_health_page.dart';
|
||||||
import '../maintain_center_page.dart';
|
import '../maintain_center_page.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
|
|
||||||
// class CupertinoDrawerCaptain extends StatelessWidget {
|
// 1. إنشاء Class لتعريف بيانات كل عنصر في القائمة
|
||||||
// final ImageController imageController = Get.put(ImageController());
|
class DrawerItem {
|
||||||
|
final String title;
|
||||||
|
final IconData icon;
|
||||||
|
final Color color;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
// @override
|
DrawerItem(
|
||||||
// Widget build(BuildContext context) {
|
{required this.title,
|
||||||
// return CupertinoPageScaffold(
|
required this.icon,
|
||||||
// navigationBar: CupertinoNavigationBar(
|
required this.color,
|
||||||
// middle: Text('Menu'.tr),
|
required this.onTap});
|
||||||
// ),
|
}
|
||||||
// child: SafeArea(
|
|
||||||
// child: CustomScrollView(
|
|
||||||
// slivers: [
|
|
||||||
// const SliverToBoxAdapter(child: const UserAccountHeader()),
|
|
||||||
// SliverList(
|
|
||||||
// delegate: SliverChildListDelegate([
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.money_dollar,
|
|
||||||
// text: 'Wallet'.tr,
|
|
||||||
// onTap: () => Get.to(() => WalletCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.person,
|
|
||||||
// text: 'Profile'.tr,
|
|
||||||
// onTap: () => Get.to(() => ProfileCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.clock,
|
|
||||||
// text: 'History of Trip'.tr,
|
|
||||||
// onTap: () => Get.to(() => const HistoryCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.car_detailed,
|
|
||||||
// text: 'Available for rides'.tr,
|
|
||||||
// onTap: () => Get.to(() => const AvailableRidesPage()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.bell,
|
|
||||||
// text: 'Notifications'.tr,
|
|
||||||
// onTap: () => Get.to(() => const NotificationCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.question_circle,
|
|
||||||
// text: 'Helping Center'.tr,
|
|
||||||
// onTap: () => Get.to(() => HelpCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.share,
|
|
||||||
// text: 'Share App'.tr,
|
|
||||||
// onTap: () => Get.to(() => InviteScreen()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.wrench,
|
|
||||||
// text: "Maintenance Center".tr,
|
|
||||||
// onTap: () => Get.to(() => MaintainCenterPage()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon:
|
|
||||||
// CupertinoIcons.heart, // Updated icon to represent health
|
|
||||||
// text: "Health Insurance".tr, // Updated English text
|
|
||||||
// onTap: () => Get.to(() => AssuranceHealthPage()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.mail,
|
|
||||||
// text: "Contact Us".tr,
|
|
||||||
// onTap: () => Get.to(() => ContactUsPage()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon:
|
|
||||||
// Icons.play_circle_filled, // Icon representing video play
|
|
||||||
// text: 'Videos Tutorials'.tr,
|
|
||||||
// onTap: () => Get.to(() => VideoListPage()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: Icons.star, // Another option with a filled star icon
|
|
||||||
// text: "Rate Our App".tr,
|
|
||||||
// onTap: () => Get.to(() => RatingScreen()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.settings,
|
|
||||||
// text: 'Settings'.tr,
|
|
||||||
// onTap: () => Get.to(() => const SettingsCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// _buildDrawerItem(
|
|
||||||
// icon: CupertinoIcons.square_arrow_right,
|
|
||||||
// text: 'Sign Out'.tr,
|
|
||||||
// onTap: () => Get.to(() => const LogoutCaptain()),
|
|
||||||
// ),
|
|
||||||
// _buildDivider(),
|
|
||||||
// ]),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Widget _buildDivider() {
|
// --- الويدجت الرئيسية للقائمة الجانبية ---
|
||||||
// return const Divider(
|
class AppDrawer extends StatelessWidget {
|
||||||
// thickness: 1,
|
AppDrawer({super.key});
|
||||||
// height: 1,
|
|
||||||
// color: CupertinoColors.systemGrey4,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Widget _buildDrawerItem({
|
|
||||||
// required IconData icon,
|
|
||||||
// required String text,
|
|
||||||
// required VoidCallback onTap,
|
|
||||||
// }) {
|
|
||||||
// return CupertinoButton(
|
|
||||||
// onPressed: onTap,
|
|
||||||
// child: Row(
|
|
||||||
// children: [
|
|
||||||
// Icon(icon, color: CupertinoColors.activeBlue),
|
|
||||||
// const SizedBox(width: 10),
|
|
||||||
// Text(text, style: const TextStyle(color: CupertinoColors.label)),
|
|
||||||
// const Spacer(),
|
|
||||||
// const Icon(CupertinoIcons.right_chevron,
|
|
||||||
// color: CupertinoColors.systemGrey),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class UserAccountHeader extends StatelessWidget {
|
|
||||||
// const UserAccountHeader({super.key});
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// return Container(
|
|
||||||
// padding: const EdgeInsets.all(16),
|
|
||||||
// decoration: const BoxDecoration(
|
|
||||||
// gradient: LinearGradient(
|
|
||||||
// colors: [AppColor.blueColor, AppColor.twitterColor],
|
|
||||||
// begin: Alignment.topLeft,
|
|
||||||
// end: Alignment.bottomRight,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// child: Column(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// GetBuilder<ImageController>(
|
|
||||||
// builder: (imageController) {
|
|
||||||
// return Stack(
|
|
||||||
// children: [
|
|
||||||
// imageController.isloading
|
|
||||||
// ? const CupertinoActivityIndicator()
|
|
||||||
// : Container(
|
|
||||||
// width: 100,
|
|
||||||
// height: 100,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// shape: BoxShape.circle,
|
|
||||||
// image: DecorationImage(
|
|
||||||
// fit: BoxFit.cover,
|
|
||||||
// image: NetworkImage(
|
|
||||||
// '${AppLink.seferCairoServer}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg',
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Positioned(
|
|
||||||
// right: 0,
|
|
||||||
// top: 0,
|
|
||||||
// child: CupertinoButton(
|
|
||||||
// onPressed: () {
|
|
||||||
// imageController.choosImagePicture(
|
|
||||||
// AppLink.uploadImage1, 'portrait');
|
|
||||||
// },
|
|
||||||
// child: const Icon(CupertinoIcons.pencil_circle_fill,
|
|
||||||
// color: CupertinoColors.white),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 10),
|
|
||||||
// Text(
|
|
||||||
// '${box.read(BoxName.nameDriver).toString().split(' ')[0]} ${box.read(BoxName.nameDriver).toString().split(' ')[1]}',
|
|
||||||
// style: const TextStyle(
|
|
||||||
// color: CupertinoColors.white,
|
|
||||||
// fontSize: 18,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 5),
|
|
||||||
// Text(
|
|
||||||
// box.read(BoxName.emailDriver),
|
|
||||||
// style: const TextStyle(color: CupertinoColors.white, fontSize: 14),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 10),
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// Get.find<HomeCaptainController>().rating.toString(),
|
|
||||||
// style: const TextStyle(
|
|
||||||
// color: CupertinoColors.systemYellow, fontSize: 16),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 5),
|
|
||||||
// // You might want to replace this with a Cupertino-style rating widget
|
|
||||||
// // For now, we'll use text to represent stars
|
|
||||||
// // const Text('★★★★★',
|
|
||||||
// // style: TextStyle(color: CupertinoColors.systemYellow)),
|
|
||||||
|
|
||||||
// Container(
|
|
||||||
// padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
|
||||||
// color: AppColor.greenColor,
|
|
||||||
// child: RatingBar.builder(
|
|
||||||
// initialRating: double.parse(
|
|
||||||
// Get.find<HomeCaptainController>().rating.toString()),
|
|
||||||
// minRating: 1,
|
|
||||||
// direction: Axis.horizontal,
|
|
||||||
// itemCount: 5,
|
|
||||||
// itemSize: 20,
|
|
||||||
// itemPadding: const EdgeInsets.symmetric(horizontal: 2),
|
|
||||||
// itemBuilder: (context, _) => const Icon(
|
|
||||||
// Icons.star,
|
|
||||||
// color: Colors.amber,
|
|
||||||
// ),
|
|
||||||
// onRatingUpdate: (rating) {},
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
class CupertinoDrawerCaptain extends StatelessWidget {
|
|
||||||
final ImageController imageController = Get.put(ImageController());
|
final ImageController imageController = Get.put(ImageController());
|
||||||
|
|
||||||
|
// 2. تعريف بيانات القائمة بشكل مركزي ومنظم
|
||||||
|
final List<DrawerItem> drawerItems = [
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Wallet'.tr,
|
||||||
|
icon: Icons.account_balance_wallet,
|
||||||
|
color: Colors.green,
|
||||||
|
onTap: () => Get.to(() => WalletCaptainRefactored())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Profile'.tr,
|
||||||
|
icon: Icons.person,
|
||||||
|
color: Colors.blue,
|
||||||
|
onTap: () => Get.to(() => ProfileCaptain())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'History of Trip'.tr,
|
||||||
|
icon: Icons.history,
|
||||||
|
color: Colors.orange,
|
||||||
|
onTap: () => Get.to(() => const HistoryCaptain())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Available for rides'.tr,
|
||||||
|
icon: Icons.drive_eta,
|
||||||
|
color: Colors.teal,
|
||||||
|
onTap: () => Get.to(() => const AvailableRidesPage())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Notifications'.tr,
|
||||||
|
icon: Icons.notifications,
|
||||||
|
color: Colors.purple,
|
||||||
|
onTap: () => Get.to(() => const NotificationCaptain())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Helping Center'.tr,
|
||||||
|
icon: Icons.help_center,
|
||||||
|
color: Colors.cyan,
|
||||||
|
onTap: () => Get.to(() => HelpCaptain())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Share App'.tr,
|
||||||
|
icon: Icons.share,
|
||||||
|
color: Colors.indigo,
|
||||||
|
onTap: () => Get.to(() => InviteScreen())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Maintenance Center'.tr,
|
||||||
|
icon: Icons.build,
|
||||||
|
color: Colors.brown,
|
||||||
|
onTap: () => Get.to(() => MaintainCenterPage())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Health Insurance'.tr,
|
||||||
|
icon: Icons.favorite,
|
||||||
|
color: Colors.pink,
|
||||||
|
onTap: () => Get.to(() => AssuranceHealthPage())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Contact Us'.tr,
|
||||||
|
icon: Icons.email,
|
||||||
|
color: Colors.blueGrey,
|
||||||
|
onTap: () => Get.to(() => ContactUsPage())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Videos Tutorials'.tr,
|
||||||
|
icon: Icons.video_library,
|
||||||
|
color: Colors.redAccent,
|
||||||
|
onTap: () => Get.to(() => VideoListPage())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Rate Our App'.tr,
|
||||||
|
icon: Icons.star,
|
||||||
|
color: Colors.amber,
|
||||||
|
onTap: () => Get.to(() => RatingScreen())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Settings'.tr,
|
||||||
|
icon: Icons.settings,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
onTap: () => Get.to(() => const SettingsCaptain())),
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CupertinoPageScaffold(
|
return Drawer(
|
||||||
backgroundColor: CupertinoColors.systemGroupedBackground,
|
child: Container(
|
||||||
navigationBar: CupertinoNavigationBar(
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
middle: Text('Menu'.tr,
|
child: Column(
|
||||||
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
border: null,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
child: CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
const SliverToBoxAdapter(child: UserAccountHeader()),
|
|
||||||
SliverList(
|
|
||||||
delegate: SliverChildListDelegate([
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
_buildSectionHeader('Account'.tr),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.money_dollar_circle_fill,
|
|
||||||
text: 'Wallet'.tr,
|
|
||||||
onTap: () => Get.to(() => WalletCaptainRefactored()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.person_fill,
|
|
||||||
text: 'Profile'.tr,
|
|
||||||
onTap: () => Get.to(() => ProfileCaptain()),
|
|
||||||
),
|
|
||||||
_buildSectionHeader('Activities'.tr),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.clock_fill,
|
|
||||||
text: 'History of Trip'.tr,
|
|
||||||
onTap: () => Get.to(() => const HistoryCaptain()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.car_fill,
|
|
||||||
text: 'Available for rides'.tr,
|
|
||||||
onTap: () => Get.to(() => const AvailableRidesPage()),
|
|
||||||
),
|
|
||||||
_buildSectionHeader('Support'.tr),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.bell_fill,
|
|
||||||
text: 'Notifications'.tr,
|
|
||||||
onTap: () => Get.to(() => const NotificationCaptain()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.question_circle_fill,
|
|
||||||
text: 'Helping Center'.tr,
|
|
||||||
onTap: () => Get.to(() => HelpCaptain()),
|
|
||||||
),
|
|
||||||
_buildSectionHeader('More'.tr),
|
|
||||||
...moreMenuItems(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> moreMenuItems() => [
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.share_up,
|
|
||||||
text: 'Share App'.tr,
|
|
||||||
onTap: () => Get.to(() => InviteScreen()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.wrench_fill,
|
|
||||||
text: "Maintenance Center".tr,
|
|
||||||
onTap: () => Get.to(() => MaintainCenterPage()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.heart_fill,
|
|
||||||
text: "Health Insurance".tr,
|
|
||||||
onTap: () => Get.to(() => AssuranceHealthPage()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.mail_solid,
|
|
||||||
text: "Contact Us".tr,
|
|
||||||
onTap: () => Get.to(() => ContactUsPage()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.play_circle_fill,
|
|
||||||
text: 'Videos Tutorials'.tr,
|
|
||||||
onTap: () => Get.to(() => VideoListPage()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.star_fill,
|
|
||||||
text: "Rate Our App".tr,
|
|
||||||
onTap: () => Get.to(() => RatingScreen()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.settings_solid,
|
|
||||||
text: 'Settings'.tr,
|
|
||||||
onTap: () => Get.to(() => const SettingsCaptain()),
|
|
||||||
),
|
|
||||||
_buildDrawerItem(
|
|
||||||
icon: CupertinoIcons.square_arrow_right,
|
|
||||||
text: 'Sign Out'.tr,
|
|
||||||
onTap: () => Get.to(() => const LogoutCaptain()),
|
|
||||||
isDestructive: true,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildSectionHeader(String title) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
||||||
child: Text(
|
|
||||||
title,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: CupertinoColors.systemGrey,
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDrawerItem({
|
|
||||||
required IconData icon,
|
|
||||||
required String text,
|
|
||||||
required VoidCallback onTap,
|
|
||||||
bool isDestructive = false,
|
|
||||||
}) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: CupertinoColors.white,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: CupertinoButton(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
||||||
onPressed: onTap,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
// --- الجزء العلوي من القائمة (بيانات المستخدم) ---
|
||||||
icon,
|
UserHeader(), // استخدمنا الويدجت المحسنة بالأسفل
|
||||||
color: isDestructive
|
|
||||||
? CupertinoColors.destructiveRed
|
// --- قائمة العناصر المتحركة ---
|
||||||
: CupertinoColors.activeBlue,
|
Expanded(
|
||||||
size: 22,
|
child: AnimationLimiter(
|
||||||
),
|
child: ListView.builder(
|
||||||
const SizedBox(width: 12),
|
padding: const EdgeInsets.all(8.0),
|
||||||
Text(
|
itemCount: drawerItems.length + 1, // +1 لزر تسجيل الخروج
|
||||||
text,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
style: AppStyle.title,
|
// --- زر تسجيل الخروج في النهاية ---
|
||||||
// TextStyle(
|
if (index == drawerItems.length) {
|
||||||
// color: isDestructive
|
return AnimationConfiguration.staggeredList(
|
||||||
// ? CupertinoColors.destructiveRed
|
position: index,
|
||||||
// : CupertinoColors.label,
|
duration: const Duration(milliseconds: 375),
|
||||||
// fontSize: 16,
|
child: SlideAnimation(
|
||||||
// ),
|
verticalOffset: 50.0,
|
||||||
),
|
child: FadeInAnimation(
|
||||||
const Spacer(),
|
child: _DrawerItemTile(
|
||||||
Icon(
|
item: DrawerItem(
|
||||||
CupertinoIcons.chevron_right,
|
title: 'Sign Out'.tr,
|
||||||
color: CupertinoColors.systemGrey3,
|
icon: Icons.logout,
|
||||||
size: 18,
|
color: Colors.red,
|
||||||
|
onTap: () =>
|
||||||
|
Get.to(() => const LogoutCaptain())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- بقية العناصر ---
|
||||||
|
final item = drawerItems[index];
|
||||||
|
return AnimationConfiguration.staggeredList(
|
||||||
|
position: index,
|
||||||
|
duration: const Duration(milliseconds: 375),
|
||||||
|
child: SlideAnimation(
|
||||||
|
verticalOffset: 50.0,
|
||||||
|
child: FadeInAnimation(
|
||||||
|
child: _DrawerItemTile(item: item),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -437,152 +173,123 @@ class CupertinoDrawerCaptain extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserAccountHeader extends StatelessWidget {
|
// --- ويدجت خاصة بكل عنصر في القائمة ---
|
||||||
const UserAccountHeader({super.key});
|
class _DrawerItemTile extends StatelessWidget {
|
||||||
|
final DrawerItem item;
|
||||||
|
const _DrawerItemTile({required this.item});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.color.withOpacity(0.1),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(item.icon, color: item.color, size: 24),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item.title,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium
|
||||||
|
?.copyWith(fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Get.back(); // لإغلاق القائمة عند الضغط
|
||||||
|
Future.delayed(const Duration(milliseconds: 250), () {
|
||||||
|
item.onTap(); // الانتقال للصفحة بعد تأخير بسيط لإظهار الأنيميشن
|
||||||
|
});
|
||||||
|
},
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
splashColor: item.color.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ويدجت محسنة للجزء العلوي من القائمة ---
|
||||||
|
class UserHeader extends StatelessWidget {
|
||||||
|
UserHeader({super.key});
|
||||||
|
final ImageController imageController = Get.find<ImageController>();
|
||||||
|
final HomeCaptainController homeCaptainController =
|
||||||
|
Get.find<HomeCaptainController>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return UserAccountsDrawerHeader(
|
||||||
|
accountName: Text(
|
||||||
|
box.read(BoxName.nameDriver).toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
shadows: [Shadow(blurRadius: 2, color: Colors.black26)]),
|
||||||
|
),
|
||||||
|
accountEmail: Text(box.read(BoxName.emailDriver)),
|
||||||
|
currentAccountPicture: GetBuilder<ImageController>(
|
||||||
|
builder: (controller) => Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 5)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: controller.isloading
|
||||||
|
? const CircularProgressIndicator(color: Colors.white)
|
||||||
|
: CircleAvatar(
|
||||||
|
backgroundImage: NetworkImage(
|
||||||
|
'${AppLink.server}/portrate_captain_image/${box.read(BoxName.driverID)}.jpg'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: -5,
|
||||||
|
right: -5,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => controller.choosImagePicture(
|
||||||
|
AppLink.uploadImagePortrate, 'portrait'),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(Icons.camera_alt,
|
||||||
|
color: Theme.of(context).primaryColor, size: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
otherAccountsPictures: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
homeCaptainController.rating.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
const Icon(Icons.star, color: Colors.amber, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [AppColor.blueColor, Color(0xFF2196F3)],
|
colors: [Theme.of(context).primaryColor, Colors.blue.shade700],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withAlpha(10),
|
|
||||||
blurRadius: 10,
|
|
||||||
spreadRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
GetBuilder<ImageController>(
|
|
||||||
builder: (imageController) {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(color: Colors.white, width: 3),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.2),
|
|
||||||
blurRadius: 8,
|
|
||||||
spreadRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: imageController.isloading
|
|
||||||
? const CupertinoActivityIndicator()
|
|
||||||
: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(50),
|
|
||||||
child: Image.network(
|
|
||||||
'${AppLink.seferCairoServer}/portrate_captain_image/${(box.read(BoxName.driverID))}.jpg',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: CupertinoButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
onPressed: () {
|
|
||||||
imageController.choosImagePicture(
|
|
||||||
AppLink.uploadImagePortrate, 'portrait');
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(6),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
CupertinoIcons.camera_fill,
|
|
||||||
color: AppColor.blueColor,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
box.read(BoxName.nameDriver).toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
shadows: [
|
|
||||||
Shadow(
|
|
||||||
color: Colors.black26,
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
box.read(BoxName.emailDriver),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white.withOpacity(0.9),
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.15),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
// const Icon(
|
|
||||||
// CupertinoIcons.star_fill,
|
|
||||||
// color: CupertinoColors.systemYellow,
|
|
||||||
// size: 18,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
Get.find<HomeCaptainController>().rating.toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
RatingBar.builder(
|
|
||||||
initialRating: double.parse(
|
|
||||||
Get.find<HomeCaptainController>().rating.toString()),
|
|
||||||
minRating: 1,
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
itemCount: 5,
|
|
||||||
itemSize: 16,
|
|
||||||
ignoreGestures: true,
|
|
||||||
itemBuilder: (context, _) => const Icon(
|
|
||||||
CupertinoIcons.star_fill,
|
|
||||||
color: CupertinoColors.systemYellow,
|
|
||||||
),
|
|
||||||
onRatingUpdate: (rating) {},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ class ConnectWidget extends StatelessWidget {
|
|||||||
child: GetBuilder<HomeCaptainController>(
|
child: GetBuilder<HomeCaptainController>(
|
||||||
builder: (homeCaptainController) => double.parse(
|
builder: (homeCaptainController) => double.parse(
|
||||||
(captainWalletController.totalPoints)) <
|
(captainWalletController.totalPoints)) <
|
||||||
-300
|
-30000
|
||||||
? CupertinoButton(
|
? CupertinoButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.defaultDialog(
|
Get.defaultDialog(
|
||||||
@@ -34,7 +34,7 @@ class ConnectWidget extends StatelessWidget {
|
|||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
title: double.parse(
|
title: double.parse(
|
||||||
(captainWalletController.totalPoints)) <
|
(captainWalletController.totalPoints)) <
|
||||||
-300
|
-30000
|
||||||
? 'You dont have Points'.tr
|
? 'You dont have Points'.tr
|
||||||
: 'You Are Stopped For this Day !'.tr,
|
: 'You Are Stopped For this Day !'.tr,
|
||||||
titleStyle: AppStyle.title,
|
titleStyle: AppStyle.title,
|
||||||
@@ -44,7 +44,7 @@ class ConnectWidget extends StatelessWidget {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
double.parse((captainWalletController
|
double.parse((captainWalletController
|
||||||
.totalPoints)) <
|
.totalPoints)) <
|
||||||
-300
|
-30000
|
||||||
? await Get.find<TextToSpeechController>()
|
? await Get.find<TextToSpeechController>()
|
||||||
.speakText(
|
.speakText(
|
||||||
'You must be recharge your Account'
|
'You must be recharge your Account'
|
||||||
@@ -59,7 +59,7 @@ class ConnectWidget extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
double.parse((captainWalletController
|
double.parse((captainWalletController
|
||||||
.totalPoints)) <
|
.totalPoints)) <
|
||||||
-300
|
-30000
|
||||||
? 'You must be recharge your Account'.tr
|
? 'You must be recharge your Account'.tr
|
||||||
: 'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
: 'You Refused 3 Rides this Day that is the reason \nSee you Tomorrow!'
|
||||||
.tr,
|
.tr,
|
||||||
@@ -69,7 +69,7 @@ class ConnectWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
confirm: double.parse(
|
confirm: double.parse(
|
||||||
(captainWalletController.totalPoints)) <
|
(captainWalletController.totalPoints)) <
|
||||||
-300
|
-30000
|
||||||
? MyElevatedButton(
|
? MyElevatedButton(
|
||||||
title: 'Recharge my Account'.tr,
|
title: 'Recharge my Account'.tr,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
|||||||
border: Border.all(color: AppColor.blueColor),
|
border: Border.all(color: AppColor.blueColor),
|
||||||
borderRadius: BorderRadius.circular(15)),
|
borderRadius: BorderRadius.circular(15)),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
onLongPress: () {
|
||||||
|
box.write(BoxName.statusDriverLocation, 'off');
|
||||||
|
},
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// NotificationController1()
|
// NotificationController1()
|
||||||
// .showNotification('Sefer Driver'.tr, ''.tr, '', '');
|
// .showNotification('Sefer Driver'.tr, ''.tr, '', '');
|
||||||
|
|||||||
@@ -1,263 +1,491 @@
|
|||||||
import 'dart:io';
|
// import 'dart:io';
|
||||||
|
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:flutter/services.dart';
|
||||||
|
// import 'package:get/get.dart';
|
||||||
|
// import 'package:slide_to_act/slide_to_act.dart';
|
||||||
|
// import 'package:vibration/vibration.dart';
|
||||||
|
|
||||||
|
// import '../../../../constant/colors.dart';
|
||||||
|
// import '../../../../constant/style.dart';
|
||||||
|
// import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||||
|
// import '../../../widgets/elevated_btn.dart';
|
||||||
|
|
||||||
|
// GetBuilder<MapDriverController> driverEndRideBar() {
|
||||||
|
// return GetBuilder<MapDriverController>(
|
||||||
|
// builder: (mapDriverController) => mapDriverController.isRideStarted
|
||||||
|
// ? Positioned(
|
||||||
|
// left: 5,
|
||||||
|
// top: 5,
|
||||||
|
// right: 5,
|
||||||
|
// child: Container(
|
||||||
|
// decoration: AppStyle.boxDecoration1.copyWith(
|
||||||
|
// borderRadius: BorderRadius.circular(15),
|
||||||
|
// boxShadow: [
|
||||||
|
// BoxShadow(
|
||||||
|
// color: Colors.black.withOpacity(0.1),
|
||||||
|
// blurRadius: 10,
|
||||||
|
// offset: Offset(0, 5),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// padding: const EdgeInsets.all(10),
|
||||||
|
// height: mapDriverController.remainingTimeTimerRideBegin < 60
|
||||||
|
// ? mapDriverController.driverEndPage = 190
|
||||||
|
// : mapDriverController.carType == 'Mishwar Vip'
|
||||||
|
// ? 120
|
||||||
|
// : 170,
|
||||||
|
// child: Column(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
// children: [
|
||||||
|
// if (mapDriverController.carType != 'Mishwar Vip')
|
||||||
|
// Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
// children: [
|
||||||
|
// _buildInfoColumn(
|
||||||
|
// icon: Icons.social_distance,
|
||||||
|
// text: '${mapDriverController.distance} ${'KM'.tr}',
|
||||||
|
// ),
|
||||||
|
// _buildInfoColumn(
|
||||||
|
// icon: Icons.timelapse,
|
||||||
|
// text: mapDriverController.hours > 1
|
||||||
|
// ? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
|
||||||
|
// : '${mapDriverController.minutes} ${'m'.tr}',
|
||||||
|
// ),
|
||||||
|
// _buildInfoColumn(
|
||||||
|
// icon: Icons.money_sharp,
|
||||||
|
// text:
|
||||||
|
// '${mapDriverController.paymentAmount} ${'\$'.tr}',
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// if (mapDriverController.carType != 'Speed' &&
|
||||||
|
// mapDriverController.carType != 'Awfar Car' &&
|
||||||
|
// mapDriverController.carType != 'Scooter')
|
||||||
|
// Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
// children: [
|
||||||
|
// _buildInfoBox(
|
||||||
|
// icon: Icons.timer,
|
||||||
|
// text:
|
||||||
|
// mapDriverController.stringRemainingTimeRideBegin1,
|
||||||
|
// ),
|
||||||
|
// _buildInfoBox(
|
||||||
|
// icon: Icons.location_on,
|
||||||
|
// text:
|
||||||
|
// '${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
|
||||||
|
// ),
|
||||||
|
// _buildInfoBox(
|
||||||
|
// icon: Icons.attach_money,
|
||||||
|
// text: mapDriverController.price.toStringAsFixed(2),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// _builtTimerAndCarType(),
|
||||||
|
// Container(
|
||||||
|
// width: Get.width * 0.8,
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// borderRadius: BorderRadius.circular(15),
|
||||||
|
// boxShadow: [
|
||||||
|
// BoxShadow(
|
||||||
|
// color: AppColor.redColor.withOpacity(0.3),
|
||||||
|
// blurRadius: 8,
|
||||||
|
// offset: Offset(0, 4),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// child: SlideAction(
|
||||||
|
// height: 50,
|
||||||
|
// borderRadius: 15,
|
||||||
|
// elevation: 4,
|
||||||
|
// text: 'Slide to End Trip'.tr,
|
||||||
|
// textStyle: AppStyle.title.copyWith(
|
||||||
|
// fontSize: 18,
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
// color: Colors.white,
|
||||||
|
// ),
|
||||||
|
// outerColor: AppColor.redColor,
|
||||||
|
// innerColor: Colors.white,
|
||||||
|
// sliderButtonIcon: const Icon(
|
||||||
|
// Icons.arrow_forward_ios,
|
||||||
|
// color: AppColor.redColor,
|
||||||
|
// size: 24,
|
||||||
|
// ),
|
||||||
|
// sliderRotate: false,
|
||||||
|
// onSubmit: () {
|
||||||
|
// HapticFeedback.mediumImpact();
|
||||||
|
// mapDriverController.finishRideFromDriver();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : const SizedBox(),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class _builtTimerAndCarType extends StatelessWidget {
|
||||||
|
// const _builtTimerAndCarType({
|
||||||
|
// super.key,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// final mapDriverController = Get.find<MapDriverController>();
|
||||||
|
// return Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
// children: [
|
||||||
|
// Container(
|
||||||
|
// decoration: AppStyle.boxDecoration1.copyWith(
|
||||||
|
// boxShadow: [
|
||||||
|
// BoxShadow(
|
||||||
|
// color: AppColor.accentColor.withOpacity(0.2),
|
||||||
|
// blurRadius: 8,
|
||||||
|
// offset: Offset(0, 4),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
// child: Text(
|
||||||
|
// mapDriverController.carType,
|
||||||
|
// style: AppStyle.title,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// if (mapDriverController.carType != 'Comfort' &&
|
||||||
|
// mapDriverController.carType != 'Mishwar Vip' &&
|
||||||
|
// mapDriverController.carType != 'Lady')
|
||||||
|
// Container(
|
||||||
|
// width: Get.width * 0.6,
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// borderRadius: BorderRadius.circular(12),
|
||||||
|
// gradient: LinearGradient(
|
||||||
|
// colors: [
|
||||||
|
// mapDriverController.remainingTimeTimerRideBegin < 60
|
||||||
|
// ? AppColor.redColor.withOpacity(0.8)
|
||||||
|
// : AppColor.greenColor.withOpacity(0.8),
|
||||||
|
// mapDriverController.remainingTimeTimerRideBegin < 60
|
||||||
|
// ? AppColor.redColor
|
||||||
|
// : AppColor.greenColor,
|
||||||
|
// ],
|
||||||
|
// begin: Alignment.centerLeft,
|
||||||
|
// end: Alignment.centerRight,
|
||||||
|
// ),
|
||||||
|
// boxShadow: [
|
||||||
|
// BoxShadow(
|
||||||
|
// color: (mapDriverController.remainingTimeTimerRideBegin < 60
|
||||||
|
// ? AppColor.redColor
|
||||||
|
// : AppColor.greenColor)
|
||||||
|
// .withOpacity(0.3),
|
||||||
|
// blurRadius: 8,
|
||||||
|
// offset: Offset(0, 4),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// child: ClipRRect(
|
||||||
|
// borderRadius: BorderRadius.circular(12),
|
||||||
|
// child: Stack(
|
||||||
|
// children: [
|
||||||
|
// LinearProgressIndicator(
|
||||||
|
// backgroundColor: Colors.white.withOpacity(0.2),
|
||||||
|
// valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
// Colors.white.withOpacity(0.5),
|
||||||
|
// ),
|
||||||
|
// minHeight: 40,
|
||||||
|
// value:
|
||||||
|
// mapDriverController.progressTimerRideBegin.toDouble(),
|
||||||
|
// ),
|
||||||
|
// Center(
|
||||||
|
// child: AnimatedDefaultTextStyle(
|
||||||
|
// duration: Duration(milliseconds: 300),
|
||||||
|
// style: AppStyle.title.copyWith(
|
||||||
|
// color: Colors.white,
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
// fontSize:
|
||||||
|
// mapDriverController.remainingTimeTimerRideBegin < 60
|
||||||
|
// ? 18
|
||||||
|
// : 16,
|
||||||
|
// shadows: [
|
||||||
|
// Shadow(
|
||||||
|
// color: Colors.black26,
|
||||||
|
// offset: Offset(0, 2),
|
||||||
|
// blurRadius: 4,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// child: Text(
|
||||||
|
// mapDriverController.stringRemainingTimeRideBegin,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Widget _buildInfoColumn({required IconData icon, required String text}) {
|
||||||
|
// return Column(
|
||||||
|
// children: [
|
||||||
|
// Icon(icon),
|
||||||
|
// Text(
|
||||||
|
// text,
|
||||||
|
// style: AppStyle.title,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Widget _buildInfoBox({required IconData icon, required String text}) {
|
||||||
|
// return Container(
|
||||||
|
// width: Get.width * .2,
|
||||||
|
// decoration: AppStyle.boxDecoration1,
|
||||||
|
// padding: const EdgeInsets.all(4),
|
||||||
|
// child: Row(
|
||||||
|
// children: [
|
||||||
|
// Icon(icon),
|
||||||
|
// SizedBox(width: 4),
|
||||||
|
// Text(
|
||||||
|
// text,
|
||||||
|
// style: AppStyle.number,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// GetBuilder<MapDriverController> speedCircle() {
|
||||||
|
// if (Get.find<MapDriverController>().speed > 100) {
|
||||||
|
// if (Platform.isIOS) {
|
||||||
|
// HapticFeedback.selectionClick();
|
||||||
|
// } else {
|
||||||
|
// Vibration.vibrate(duration: 1000);
|
||||||
|
// }
|
||||||
|
// Get.defaultDialog(
|
||||||
|
// barrierDismissible: false,
|
||||||
|
// titleStyle: AppStyle.title,
|
||||||
|
// title: 'Speed Over'.tr,
|
||||||
|
// middleText: 'Please slow down'.tr,
|
||||||
|
// middleTextStyle: AppStyle.title,
|
||||||
|
// confirm: MyElevatedButton(
|
||||||
|
// title: 'I will slow down'.tr,
|
||||||
|
// onPressed: () => Get.back(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return GetBuilder<MapDriverController>(
|
||||||
|
// builder: (mapDriverController) {
|
||||||
|
// return mapDriverController.isRideStarted
|
||||||
|
// ? Positioned(
|
||||||
|
// bottom: 25,
|
||||||
|
// right: 100,
|
||||||
|
// child: Container(
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// shape: BoxShape.circle,
|
||||||
|
// color: mapDriverController.speed > 100
|
||||||
|
// ? Colors.red
|
||||||
|
// : AppColor.secondaryColor,
|
||||||
|
// border: Border.all(width: 3, color: AppColor.redColor),
|
||||||
|
// ),
|
||||||
|
// height: 60,
|
||||||
|
// width: 60,
|
||||||
|
// child: Center(
|
||||||
|
// child: Text(
|
||||||
|
// mapDriverController.speed.toStringAsFixed(0),
|
||||||
|
// style: AppStyle.number,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : const SizedBox();
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:slide_to_act/slide_to_act.dart';
|
import 'package:slide_to_act/slide_to_act.dart';
|
||||||
import 'package:vibration/vibration.dart';
|
import 'package:vibration/vibration.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import '../../../../constant/colors.dart';
|
import '../../../../constant/colors.dart';
|
||||||
import '../../../../constant/style.dart';
|
import '../../../../constant/style.dart';
|
||||||
import '../../../../controller/home/captin/map_driver_controller.dart';
|
import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||||
import '../../../widgets/elevated_btn.dart';
|
import '../../../widgets/elevated_btn.dart';
|
||||||
|
|
||||||
|
// Changed: إعادة تصميم كاملة للشريط ليصبح شريطًا علويًا عند بدء الرحلة
|
||||||
GetBuilder<MapDriverController> driverEndRideBar() {
|
GetBuilder<MapDriverController> driverEndRideBar() {
|
||||||
return GetBuilder<MapDriverController>(
|
return GetBuilder<MapDriverController>(
|
||||||
builder: (mapDriverController) => mapDriverController.isRideStarted
|
builder: (controller) => AnimatedPositioned(
|
||||||
? Positioned(
|
duration: const Duration(milliseconds: 300),
|
||||||
left: 5,
|
// New: يظهر الشريط من الأعلى عندما تبدأ الرحلة
|
||||||
top: 5,
|
top: controller.isRideStarted ? 0 : -200,
|
||||||
right: 5,
|
left: 0,
|
||||||
child: Container(
|
right: 0,
|
||||||
decoration: AppStyle.boxDecoration1.copyWith(
|
child: Card(
|
||||||
borderRadius: BorderRadius.circular(15),
|
margin: EdgeInsets.zero,
|
||||||
boxShadow: [
|
elevation: 10,
|
||||||
BoxShadow(
|
shape: const RoundedRectangleBorder(
|
||||||
color: Colors.black.withOpacity(0.1),
|
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24)),
|
||||||
blurRadius: 10,
|
),
|
||||||
offset: Offset(0, 5),
|
child: Padding(
|
||||||
),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
],
|
child: Column(
|
||||||
),
|
children: [
|
||||||
padding: const EdgeInsets.all(10),
|
// -- معلومات الرحلة --
|
||||||
height: mapDriverController.remainingTimeTimerRideBegin < 60
|
if (controller.carType != 'Mishwar Vip')
|
||||||
? mapDriverController.driverEndPage = 190
|
Row(
|
||||||
: mapDriverController.carType == 'Mishwar Vip'
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
? 120
|
children: [
|
||||||
: 170,
|
_buildInfoColumn(
|
||||||
child: Column(
|
icon: Icons.social_distance,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
text: '${controller.distance} ${'KM'.tr}',
|
||||||
children: [
|
label: 'Distance'.tr,
|
||||||
if (mapDriverController.carType != 'Mishwar Vip')
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
_buildInfoColumn(
|
|
||||||
icon: Icons.social_distance,
|
|
||||||
text: '${mapDriverController.distance} ${'KM'.tr}',
|
|
||||||
),
|
|
||||||
_buildInfoColumn(
|
|
||||||
icon: Icons.timelapse,
|
|
||||||
text: mapDriverController.hours > 1
|
|
||||||
? '${mapDriverController.hours} ${'H and'.tr} ${mapDriverController.minutes} m'
|
|
||||||
: '${mapDriverController.minutes} ${'m'.tr}',
|
|
||||||
),
|
|
||||||
_buildInfoColumn(
|
|
||||||
icon: Icons.money_sharp,
|
|
||||||
text:
|
|
||||||
'${mapDriverController.paymentAmount} ${'\$'.tr}',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (mapDriverController.carType != 'Speed' &&
|
_buildInfoColumn(
|
||||||
mapDriverController.carType != 'Awfar Car' &&
|
icon: Icons.timelapse,
|
||||||
mapDriverController.carType != 'Scooter')
|
text: controller.hours > 1
|
||||||
Row(
|
? '${controller.hours}h ${controller.minutes}m'
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
: '${controller.minutes}m',
|
||||||
children: [
|
label: 'Time'.tr,
|
||||||
_buildInfoBox(
|
|
||||||
icon: Icons.timer,
|
|
||||||
text:
|
|
||||||
mapDriverController.stringRemainingTimeRideBegin1,
|
|
||||||
),
|
|
||||||
_buildInfoBox(
|
|
||||||
icon: Icons.location_on,
|
|
||||||
text:
|
|
||||||
'${mapDriverController.recentDistanceToDash.toStringAsFixed(0)} ${'KM'.tr}',
|
|
||||||
),
|
|
||||||
_buildInfoBox(
|
|
||||||
icon: Icons.attach_money,
|
|
||||||
text: mapDriverController.price.toStringAsFixed(2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
_builtTimerAndCarType(),
|
_buildInfoColumn(
|
||||||
Container(
|
icon: Icons.money_sharp,
|
||||||
width: Get.width * 0.8,
|
text: '${controller.paymentAmount} ${'\$'.tr}',
|
||||||
decoration: BoxDecoration(
|
label: 'Price'.tr,
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColor.redColor.withOpacity(0.3),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: SlideAction(
|
],
|
||||||
height: 50,
|
),
|
||||||
borderRadius: 15,
|
if (controller.carType != 'Mishwar Vip')
|
||||||
elevation: 4,
|
const Divider(height: 20),
|
||||||
text: 'Slide to End Trip'.tr,
|
|
||||||
textStyle: AppStyle.title.copyWith(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
outerColor: AppColor.redColor,
|
|
||||||
innerColor: Colors.white,
|
|
||||||
sliderButtonIcon: const Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
color: AppColor.redColor,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
sliderRotate: false,
|
|
||||||
onSubmit: () {
|
|
||||||
HapticFeedback.mediumImpact();
|
|
||||||
mapDriverController.finishRideFromDriver();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _builtTimerAndCarType extends StatelessWidget {
|
// -- مؤقت الرحلة المتبقي (إن وجد) --
|
||||||
const _builtTimerAndCarType({
|
_builtTimerAndCarType(),
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
const SizedBox(height: 12),
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final mapDriverController = Get.find<MapDriverController>();
|
// -- زر إنهاء الرحلة المنزلق --
|
||||||
return Row(
|
SlideAction(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
height: 55,
|
||||||
children: [
|
borderRadius: 15,
|
||||||
Container(
|
elevation: 4,
|
||||||
decoration: AppStyle.boxDecoration1.copyWith(
|
text: 'Slide to End Trip'.tr,
|
||||||
boxShadow: [
|
textStyle: AppStyle.title.copyWith(
|
||||||
BoxShadow(
|
fontSize: 18,
|
||||||
color: AppColor.accentColor.withOpacity(0.2),
|
fontWeight: FontWeight.bold,
|
||||||
blurRadius: 8,
|
color: Colors.white,
|
||||||
offset: Offset(0, 4),
|
),
|
||||||
|
outerColor: AppColor.redColor,
|
||||||
|
innerColor: Colors.white,
|
||||||
|
sliderButtonIcon: const Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
color: AppColor.redColor,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
sliderRotate: false,
|
||||||
|
onSubmit: () {
|
||||||
|
HapticFeedback.mediumImpact();
|
||||||
|
controller.finishRideFromDriver();
|
||||||
|
return null; // New: onSubmit now returns null
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: ودجت لعرض معلومات الرحلة في الشريط العلوي
|
||||||
|
Widget _buildInfoColumn(
|
||||||
|
{required IconData icon, required String text, required String label}) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(icon, color: AppColor.primaryColor),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||||
|
Text(label,
|
||||||
|
style:
|
||||||
|
AppStyle.title.copyWith(color: Colors.grey[600], fontSize: 12)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed: تم تعديل تصميم ودجت عرض المؤقت ونوع السيارة
|
||||||
|
class _builtTimerAndCarType extends StatelessWidget {
|
||||||
|
const _builtTimerAndCarType();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final controller = Get.find<MapDriverController>();
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// -- نوع السيارة --
|
||||||
|
Container(
|
||||||
|
decoration: AppStyle.boxDecoration1.copyWith(color: Colors.grey[200]),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
mapDriverController.carType,
|
controller.carType,
|
||||||
style: AppStyle.title,
|
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (mapDriverController.carType != 'Comfort' &&
|
// -- مؤقت الرحلة --
|
||||||
mapDriverController.carType != 'Mishwar Vip' &&
|
if (controller.carType != 'Comfort' &&
|
||||||
mapDriverController.carType != 'Lady')
|
controller.carType != 'Mishwar Vip' &&
|
||||||
Container(
|
controller.carType != 'Lady') ...[
|
||||||
width: Get.width * 0.6,
|
const SizedBox(width: 10),
|
||||||
decoration: BoxDecoration(
|
Expanded(
|
||||||
borderRadius: BorderRadius.circular(12),
|
child: Container(
|
||||||
gradient: LinearGradient(
|
height: 40,
|
||||||
colors: [
|
decoration: BoxDecoration(
|
||||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
borderRadius: BorderRadius.circular(12),
|
||||||
? AppColor.redColor.withOpacity(0.8)
|
gradient: LinearGradient(
|
||||||
: AppColor.greenColor.withOpacity(0.8),
|
colors: [
|
||||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
controller.remainingTimeTimerRideBegin < 60
|
||||||
? AppColor.redColor
|
? AppColor.redColor.withOpacity(0.8)
|
||||||
: AppColor.greenColor,
|
: AppColor.greenColor.withOpacity(0.8),
|
||||||
],
|
controller.remainingTimeTimerRideBegin < 60
|
||||||
begin: Alignment.centerLeft,
|
? AppColor.redColor
|
||||||
end: Alignment.centerRight,
|
: AppColor.greenColor,
|
||||||
),
|
],
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: (mapDriverController.remainingTimeTimerRideBegin < 60
|
|
||||||
? AppColor.redColor
|
|
||||||
: AppColor.greenColor)
|
|
||||||
.withOpacity(0.3),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: Offset(0, 4),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
child: ClipRRect(
|
||||||
child: ClipRRect(
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderRadius: BorderRadius.circular(12),
|
child: Stack(
|
||||||
child: Stack(
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
backgroundColor: Colors.white.withOpacity(0.2),
|
backgroundColor: Colors.transparent,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
Colors.white.withOpacity(0.5),
|
Colors.white.withOpacity(0.2)),
|
||||||
|
minHeight: 40,
|
||||||
|
value: controller.progressTimerRideBegin.toDouble(),
|
||||||
),
|
),
|
||||||
minHeight: 40,
|
Text(
|
||||||
value:
|
controller.stringRemainingTimeRideBegin,
|
||||||
mapDriverController.progressTimerRideBegin.toDouble(),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: AnimatedDefaultTextStyle(
|
|
||||||
duration: Duration(milliseconds: 300),
|
|
||||||
style: AppStyle.title.copyWith(
|
style: AppStyle.title.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize:
|
|
||||||
mapDriverController.remainingTimeTimerRideBegin < 60
|
|
||||||
? 18
|
|
||||||
: 16,
|
|
||||||
shadows: [
|
|
||||||
Shadow(
|
|
||||||
color: Colors.black26,
|
|
||||||
offset: Offset(0, 2),
|
|
||||||
blurRadius: 4,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
mapDriverController.stringRemainingTimeRideBegin,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInfoColumn({required IconData icon, required String text}) {
|
// Changed: تم تعديل مكان ومظهر دائرة السرعة
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Icon(icon),
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildInfoBox({required IconData icon, required String text}) {
|
|
||||||
return Container(
|
|
||||||
width: Get.width * .2,
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: AppStyle.number,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
GetBuilder<MapDriverController> speedCircle() {
|
GetBuilder<MapDriverController> speedCircle() {
|
||||||
if (Get.find<MapDriverController>().speed > 100) {
|
if (Get.find<MapDriverController>().speed > 100) {
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
@@ -278,25 +506,36 @@ GetBuilder<MapDriverController> speedCircle() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GetBuilder<MapDriverController>(
|
return GetBuilder<MapDriverController>(
|
||||||
builder: (mapDriverController) {
|
builder: (controller) {
|
||||||
return mapDriverController.isRideStarted
|
return controller.isRideStarted
|
||||||
? Positioned(
|
? Positioned(
|
||||||
|
// New: تم وضع دائرة السرعة في الأسفل يمينًا
|
||||||
bottom: 25,
|
bottom: 25,
|
||||||
right: 100,
|
left: 16,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: mapDriverController.speed > 100
|
color: Colors.white,
|
||||||
? Colors.red
|
boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
|
||||||
: AppColor.secondaryColor,
|
border: Border.all(
|
||||||
border: Border.all(width: 3, color: AppColor.redColor),
|
width: 4,
|
||||||
|
color: controller.speed > 100
|
||||||
|
? Colors.red
|
||||||
|
: AppColor.greenColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
height: 60,
|
height: 70,
|
||||||
width: 60,
|
width: 70,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Column(
|
||||||
mapDriverController.speed.toStringAsFixed(0),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style: AppStyle.number,
|
children: [
|
||||||
|
Text(
|
||||||
|
controller.speed.toStringAsFixed(0),
|
||||||
|
style: AppStyle.number.copyWith(fontSize: 24),
|
||||||
|
),
|
||||||
|
Text("km/h", style: TextStyle(fontSize: 10)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,117 @@
|
|||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:get/get.dart';
|
||||||
|
// import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
|
||||||
|
// import '../../../../controller/functions/location_controller.dart';
|
||||||
|
// import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||||
|
|
||||||
|
// class GoogleDriverMap extends StatelessWidget {
|
||||||
|
// const GoogleDriverMap({
|
||||||
|
// super.key,
|
||||||
|
// required this.locationController,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// final LocationController locationController;
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// Get.put(MapDriverController());
|
||||||
|
// return Padding(
|
||||||
|
// padding: const EdgeInsets.all(8.0),
|
||||||
|
// child: GetBuilder<MapDriverController>(
|
||||||
|
// builder: (controller) => Column(
|
||||||
|
// children: [
|
||||||
|
// SizedBox(
|
||||||
|
// height: Get.height * .92,
|
||||||
|
// child: GoogleMap(
|
||||||
|
// onMapCreated: controller.onMapCreated,
|
||||||
|
// zoomControlsEnabled: true,
|
||||||
|
// // initialCameraPosition: CameraPosition(
|
||||||
|
// // target: locationController.myLocation,
|
||||||
|
// // zoom: 13,
|
||||||
|
// // bearing: locationController.heading,
|
||||||
|
// // tilt: 40,
|
||||||
|
// // ),
|
||||||
|
// initialCameraPosition: CameraPosition(
|
||||||
|
// target: locationController.myLocation,
|
||||||
|
// zoom: 17,
|
||||||
|
// bearing: locationController.heading, // استخدام اتجاه السائق
|
||||||
|
// tilt: 60, // زاوية ميل
|
||||||
|
// ),
|
||||||
|
// cameraTargetBounds:
|
||||||
|
// CameraTargetBounds.unbounded, // Allow unrestricted movement
|
||||||
|
// onCameraMove: (position) {
|
||||||
|
// CameraPosition(
|
||||||
|
// target: locationController.myLocation,
|
||||||
|
// zoom: 13,
|
||||||
|
// bearing: locationController.heading,
|
||||||
|
// tilt: 40,
|
||||||
|
// );
|
||||||
|
// //todo
|
||||||
|
// // locationController.myLocation = position.target;
|
||||||
|
// //
|
||||||
|
// // controller.mapController
|
||||||
|
// // ?.animateCamera(CameraUpdate.newCameraPosition(position));
|
||||||
|
// },
|
||||||
|
// minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
|
||||||
|
// myLocationEnabled: true,
|
||||||
|
// myLocationButtonEnabled: true,
|
||||||
|
// compassEnabled: true,
|
||||||
|
// mapType: MapType.terrain,
|
||||||
|
// rotateGesturesEnabled: true,
|
||||||
|
// scrollGesturesEnabled: true,
|
||||||
|
// trafficEnabled: false,
|
||||||
|
// buildingsEnabled: true,
|
||||||
|
// mapToolbarEnabled: true,
|
||||||
|
// fortyFiveDegreeImageryEnabled: true,
|
||||||
|
// zoomGesturesEnabled: true,
|
||||||
|
// polylines: {
|
||||||
|
// Polyline(
|
||||||
|
// zIndex: 2,
|
||||||
|
// geodesic: true,
|
||||||
|
// polylineId: const PolylineId('route1'),
|
||||||
|
// points: controller.polylineCoordinates,
|
||||||
|
// color: const Color.fromARGB(255, 163, 81, 246),
|
||||||
|
// width: 5,
|
||||||
|
// ),
|
||||||
|
// Polyline(
|
||||||
|
// zIndex: 2,
|
||||||
|
// geodesic: true,
|
||||||
|
// polylineId: const PolylineId('route'),
|
||||||
|
// points: controller.polylineCoordinatesDestination,
|
||||||
|
// color: const Color.fromARGB(255, 10, 29, 126),
|
||||||
|
// width: 5,
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
// markers: {
|
||||||
|
// Marker(
|
||||||
|
// markerId: MarkerId('MyLocation'.tr),
|
||||||
|
// position: locationController.myLocation,
|
||||||
|
// draggable: true,
|
||||||
|
// icon: controller.carIcon,
|
||||||
|
// rotation: locationController.heading,
|
||||||
|
// ),
|
||||||
|
// Marker(
|
||||||
|
// markerId: MarkerId('start'.tr),
|
||||||
|
// position: controller.latLngPassengerLocation,
|
||||||
|
// draggable: true,
|
||||||
|
// icon: controller.startIcon,
|
||||||
|
// ),
|
||||||
|
// Marker(
|
||||||
|
// markerId: MarkerId('end'.tr),
|
||||||
|
// position: controller.latLngPassengerDestination,
|
||||||
|
// draggable: true,
|
||||||
|
// icon: controller.endIcon,
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
@@ -15,93 +129,88 @@ class GoogleDriverMap extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(MapDriverController());
|
final MapDriverController controller = Get.put(MapDriverController());
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
// New: تحديد قيمة الـ padding لتحريك مركز الخريطة للأعلى
|
||||||
child: GetBuilder<MapDriverController>(
|
final double mapPaddingBottom = MediaQuery.of(context).size.height * 0.3;
|
||||||
builder: (controller) => Column(
|
|
||||||
children: [
|
return GetBuilder<MapDriverController>(
|
||||||
SizedBox(
|
builder: (controller) => GoogleMap(
|
||||||
height: Get.height * .92,
|
onMapCreated: (mapController) {
|
||||||
child: GoogleMap(
|
controller.onMapCreated(mapController);
|
||||||
onMapCreated: controller.onMapCreated,
|
// New: تطبيق الـ padding بعد إنشاء الخريطة مباشرة
|
||||||
zoomControlsEnabled: true,
|
mapController.setMapStyle('[]'); // يمكنك إضافة تصميم مخصص للخريطة هنا
|
||||||
initialCameraPosition: CameraPosition(
|
// يمكنك استخدام CameraUpdate.scrollBy لتحريك الكاميرا إذا رغبت بذلك:
|
||||||
target: locationController.myLocation,
|
// controller.mapController!.animateCamera(CameraUpdate.scrollBy(0, mapPaddingBottom));
|
||||||
zoom: 13,
|
},
|
||||||
bearing: locationController.heading,
|
// New: إضافة padding لتحريك مركز الخريطة للأعلى، مما يجعل أيقونة السائق تظهر في الأسفل
|
||||||
tilt: 40,
|
|
||||||
),
|
zoomControlsEnabled: false, // Changed: تم إخفاء أزرار الزوم الافتراضية
|
||||||
cameraTargetBounds:
|
initialCameraPosition: CameraPosition(
|
||||||
CameraTargetBounds.unbounded, // Allow unrestricted movement
|
target: locationController.myLocation,
|
||||||
onCameraMove: (position) {
|
zoom: 17,
|
||||||
CameraPosition(
|
bearing: locationController.heading, // استخدام اتجاه السائق
|
||||||
target: locationController.myLocation,
|
tilt: 60, // زاوية ميل
|
||||||
zoom: 13,
|
|
||||||
bearing: locationController.heading,
|
|
||||||
tilt: 40,
|
|
||||||
);
|
|
||||||
//todo
|
|
||||||
// locationController.myLocation = position.target;
|
|
||||||
//
|
|
||||||
// controller.mapController
|
|
||||||
// ?.animateCamera(CameraUpdate.newCameraPosition(position));
|
|
||||||
},
|
|
||||||
minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
|
|
||||||
myLocationEnabled: true,
|
|
||||||
myLocationButtonEnabled: true,
|
|
||||||
compassEnabled: true,
|
|
||||||
mapType: MapType.terrain,
|
|
||||||
rotateGesturesEnabled: true,
|
|
||||||
scrollGesturesEnabled: true,
|
|
||||||
trafficEnabled: false,
|
|
||||||
buildingsEnabled: true,
|
|
||||||
mapToolbarEnabled: true,
|
|
||||||
fortyFiveDegreeImageryEnabled: true,
|
|
||||||
zoomGesturesEnabled: true,
|
|
||||||
polylines: {
|
|
||||||
Polyline(
|
|
||||||
zIndex: 2,
|
|
||||||
geodesic: true,
|
|
||||||
polylineId: const PolylineId('route1'),
|
|
||||||
points: controller.polylineCoordinates,
|
|
||||||
color: const Color.fromARGB(255, 163, 81, 246),
|
|
||||||
width: 5,
|
|
||||||
),
|
|
||||||
Polyline(
|
|
||||||
zIndex: 2,
|
|
||||||
geodesic: true,
|
|
||||||
polylineId: const PolylineId('route'),
|
|
||||||
points: controller.polylineCoordinatesDestination,
|
|
||||||
color: const Color.fromARGB(255, 10, 29, 126),
|
|
||||||
width: 5,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
markers: {
|
|
||||||
Marker(
|
|
||||||
markerId: MarkerId('MyLocation'.tr),
|
|
||||||
position: locationController.myLocation,
|
|
||||||
draggable: true,
|
|
||||||
icon: controller.carIcon,
|
|
||||||
rotation: locationController.heading,
|
|
||||||
),
|
|
||||||
Marker(
|
|
||||||
markerId: MarkerId('start'.tr),
|
|
||||||
position: controller.latLngPassengerLocation,
|
|
||||||
draggable: true,
|
|
||||||
icon: controller.startIcon,
|
|
||||||
),
|
|
||||||
Marker(
|
|
||||||
markerId: MarkerId('end'.tr),
|
|
||||||
position: controller.latLngPassengerDestination,
|
|
||||||
draggable: true,
|
|
||||||
icon: controller.endIcon,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
onCameraMove: (position) {
|
||||||
|
CameraPosition(
|
||||||
|
target: locationController.myLocation,
|
||||||
|
zoom: 17.5,
|
||||||
|
tilt: 50.0,
|
||||||
|
bearing: locationController.heading,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
padding: EdgeInsets.only(bottom: 50, top: Get.height * 0.7),
|
||||||
|
minMaxZoomPreference: const MinMaxZoomPreference(8, 18),
|
||||||
|
myLocationEnabled: false, // Changed: تم الاعتماد على ماركر مخصص
|
||||||
|
myLocationButtonEnabled: true,
|
||||||
|
compassEnabled: true,
|
||||||
|
mapType: MapType.terrain,
|
||||||
|
trafficEnabled: true, // Changed: تفعيل عرض حركة المرور
|
||||||
|
buildingsEnabled: true,
|
||||||
|
polylines: {
|
||||||
|
Polyline(
|
||||||
|
zIndex: 2,
|
||||||
|
|
||||||
|
polylineId: const PolylineId('route1'),
|
||||||
|
points: controller.polylineCoordinates,
|
||||||
|
color: const Color.fromARGB(255, 163, 81, 246),
|
||||||
|
width: 6, // Changed: زيادة عرض الخط
|
||||||
|
startCap: Cap.roundCap,
|
||||||
|
endCap: Cap.roundCap,
|
||||||
|
),
|
||||||
|
Polyline(
|
||||||
|
zIndex: 2,
|
||||||
|
|
||||||
|
polylineId: const PolylineId('route'),
|
||||||
|
points: controller.polylineCoordinatesDestination,
|
||||||
|
color: const Color.fromARGB(255, 10, 29, 126),
|
||||||
|
width: 6, // Changed: زيادة عرض الخط
|
||||||
|
startCap: Cap.roundCap,
|
||||||
|
endCap: Cap.roundCap,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
markers: {
|
||||||
|
Marker(
|
||||||
|
markerId: MarkerId('MyLocation'.tr),
|
||||||
|
position: locationController.myLocation,
|
||||||
|
draggable: false, // Changed: لا يمكن سحب ماركر السائق
|
||||||
|
icon: controller.carIcon,
|
||||||
|
rotation: locationController.heading,
|
||||||
|
anchor: const Offset(
|
||||||
|
0.5, 0.5), // New: وضع نقطة ارتكاز الأيقونة في المنتصف
|
||||||
|
),
|
||||||
|
Marker(
|
||||||
|
markerId: MarkerId('start'.tr),
|
||||||
|
position: controller.latLngPassengerLocation,
|
||||||
|
icon: controller.startIcon,
|
||||||
|
),
|
||||||
|
Marker(
|
||||||
|
markerId: MarkerId('end'.tr),
|
||||||
|
position: controller.latLngPassengerDestination,
|
||||||
|
icon: controller.endIcon,
|
||||||
|
),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
import 'package:bubble_head/bubble.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sefer_driver/constant/colors.dart';
|
import 'package:sefer_driver/constant/colors.dart';
|
||||||
import 'package:sefer_driver/constant/info.dart';
|
import 'package:sefer_driver/constant/info.dart';
|
||||||
@@ -15,373 +9,97 @@ import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
|||||||
|
|
||||||
import '../../../../constant/box_name.dart';
|
import '../../../../constant/box_name.dart';
|
||||||
import '../../../../constant/style.dart';
|
import '../../../../constant/style.dart';
|
||||||
import '../../../../controller/functions/launch.dart';
|
|
||||||
import '../../../../main.dart';
|
import '../../../../main.dart';
|
||||||
import '../../../../print.dart';
|
|
||||||
|
|
||||||
|
// Changed: إعادة تصميم كاملة لتصبح شريط معلومات علوي مدمج
|
||||||
class PassengerInfoWindow extends StatelessWidget {
|
class PassengerInfoWindow extends StatelessWidget {
|
||||||
const PassengerInfoWindow({super.key});
|
const PassengerInfoWindow({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<MapDriverController>(
|
return GetBuilder<MapDriverController>(
|
||||||
builder: (controller) => controller.isPassengerInfoWindow == true
|
builder: (controller) => AnimatedPositioned(
|
||||||
? Positioned(
|
duration: const Duration(milliseconds: 400),
|
||||||
bottom: 10,
|
curve: Curves.easeInOut,
|
||||||
left: 10,
|
// Changed: تم تغيير الموضع من الأسفل إلى الأعلى
|
||||||
right: 10,
|
top: controller.isPassengerInfoWindow ? 15.0 : -200.0,
|
||||||
child: Card(
|
left: 15.0,
|
||||||
elevation: 5,
|
right: 15.0,
|
||||||
shape: RoundedRectangleBorder(
|
child: Card(
|
||||||
borderRadius: BorderRadius.circular(15),
|
elevation: 8,
|
||||||
),
|
shadowColor: Colors.black.withOpacity(0.3),
|
||||||
child: Padding(
|
shape: RoundedRectangleBorder(
|
||||||
padding: const EdgeInsets.all(16.0),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Column(
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding:
|
||||||
children: [
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
Row(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// New: صف علوي للمعلومات الأساسية
|
||||||
'Go to passenger Location'.tr,
|
_buildTopInfoRow(controller),
|
||||||
style: AppStyle.title.copyWith(
|
const Divider(height: 16),
|
||||||
color: AppColor.greenColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
// Changed: الأزرار الآن في صف أفقي ومدمج
|
||||||
),
|
if (!controller.isRideBegin) _buildActionButtons(controller),
|
||||||
),
|
|
||||||
if (!controller.isRideBegin)
|
// New: مؤشر انتظار الراكب المدمج
|
||||||
Wrap(
|
if (controller.remainingTimeInPassengerLocatioWait < 300 &&
|
||||||
spacing: 16.0,
|
controller.remainingTimeInPassengerLocatioWait != 0 &&
|
||||||
children: [
|
!controller.isRideBegin) ...[
|
||||||
IconButton(
|
const SizedBox(height: 10),
|
||||||
onPressed: () async {
|
_buildWaitingIndicator(controller),
|
||||||
controller.isSocialPressed = true;
|
],
|
||||||
await controller.driverCallPassenger();
|
|
||||||
makePhoneCall(
|
// زر الإلغاء بعد انتهاء وقت الانتظار
|
||||||
controller.passengerPhone.toString());
|
if (controller.isdriverWaitTimeEnd &&
|
||||||
},
|
!controller.isRideBegin) ...[
|
||||||
icon: const Icon(
|
const SizedBox(height: 10),
|
||||||
Icons.phone,
|
_buildCancelAfterWaitButton(controller),
|
||||||
color: AppColor.blueColor,
|
]
|
||||||
),
|
],
|
||||||
tooltip: 'Call Passenger',
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
),
|
||||||
onPressed: () {
|
),
|
||||||
Get.bottomSheet(
|
);
|
||||||
backgroundColor: Colors.white,
|
}
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.vertical(
|
// New: ودجت لعرض المعلومات العلوية بشكل مدمج
|
||||||
top: Radius.circular(20)),
|
Widget _buildTopInfoRow(MapDriverController controller) {
|
||||||
),
|
return Row(
|
||||||
Padding(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
padding: const EdgeInsets.all(16.0),
|
children: [
|
||||||
child: _buildMessageOptions(controller),
|
// معلومات الراكب
|
||||||
),
|
Expanded(
|
||||||
);
|
child: Column(
|
||||||
},
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
icon: const Icon(
|
children: [
|
||||||
Icons.message,
|
Text(
|
||||||
color: AppColor.redColor,
|
'Go to passenger:'.tr,
|
||||||
),
|
style: AppStyle.title
|
||||||
tooltip: 'Send Message',
|
.copyWith(color: Colors.grey[600], fontSize: 13),
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
Bubble().startBubbleHead(
|
|
||||||
sendAppToBackground: true);
|
|
||||||
}
|
|
||||||
await controller
|
|
||||||
.openGoogleMapFromDriverToPassenger();
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
MaterialCommunityIcons.map_marker_radius,
|
|
||||||
size: 28,
|
|
||||||
color: AppColor.blueColor,
|
|
||||||
),
|
|
||||||
tooltip: 'Open in Maps',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
_buildInfoTile(
|
|
||||||
icon: Icons.timer,
|
|
||||||
text: controller.hours > 1
|
|
||||||
? '${controller.hours}h ${controller.minutes}m'
|
|
||||||
: '${controller.minutes}m',
|
|
||||||
label: 'Duration',
|
|
||||||
),
|
|
||||||
_buildInfoTile(
|
|
||||||
icon: Icons.map,
|
|
||||||
text: '${controller.distance} km',
|
|
||||||
label: 'Distance',
|
|
||||||
),
|
|
||||||
_buildInfoTile(
|
|
||||||
icon: Icons.person,
|
|
||||||
text: controller.passengerName,
|
|
||||||
label: 'Passenger',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
_buildInfoTile(
|
|
||||||
icon: Icons.attach_money,
|
|
||||||
text: controller.totalPricePassenger,
|
|
||||||
label: 'Cost',
|
|
||||||
),
|
|
||||||
_buildInfoTile(
|
|
||||||
icon: Icons.directions_car,
|
|
||||||
text: controller.carType.tr,
|
|
||||||
label: 'Car Type',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
if (!controller.isRideBegin)
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: MyElevatedButton(
|
|
||||||
title: 'Start the Ride'.tr,
|
|
||||||
kolor: AppColor.greenColor,
|
|
||||||
onPressed: () {
|
|
||||||
MyDialog().getDialog(
|
|
||||||
"Is the Passenger in your Car?".tr,
|
|
||||||
"Don't start trip if passenger not in your car"
|
|
||||||
.tr,
|
|
||||||
() async {
|
|
||||||
await controller
|
|
||||||
.startRideFromDriver();
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (controller.isArrivedSend)
|
|
||||||
Expanded(
|
|
||||||
child: MyElevatedButton(
|
|
||||||
title: 'I Arrive'.tr,
|
|
||||||
kolor: AppColor.yellowColor,
|
|
||||||
onPressed: () async {
|
|
||||||
if (await controller
|
|
||||||
.calculateDistanceBetweenDriverAndPassengerLocation() <
|
|
||||||
140) {
|
|
||||||
Get.find<FirebaseMessagesController>()
|
|
||||||
.sendNotificationToDriverMAP(
|
|
||||||
'Hi ,I Arrive your site',
|
|
||||||
'I Arrive at your site'.tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'ding.wav',
|
|
||||||
);
|
|
||||||
controller
|
|
||||||
.startTimerToShowDriverWaitPassengerDuration();
|
|
||||||
controller.isArrivedSend = false;
|
|
||||||
} else {
|
|
||||||
MyDialog().getDialog(
|
|
||||||
'You are not near the passenger location'
|
|
||||||
.tr,
|
|
||||||
'Please go to the pickup location exactly'
|
|
||||||
.tr, () {
|
|
||||||
Get.back();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
if (controller.remainingTimeInPassengerLocatioWait <
|
|
||||||
300 &&
|
|
||||||
controller
|
|
||||||
.remainingTimeInPassengerLocatioWait !=
|
|
||||||
0)
|
|
||||||
Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
LinearProgressIndicator(
|
|
||||||
backgroundColor: AppColor.greyColor,
|
|
||||||
color: controller
|
|
||||||
.remainingTimeInPassengerLocatioWait <
|
|
||||||
60
|
|
||||||
? AppColor.redColor
|
|
||||||
: AppColor.greenColor,
|
|
||||||
minHeight: 20,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
value: controller
|
|
||||||
.progressInPassengerLocationFromDriver
|
|
||||||
.toDouble(),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
controller
|
|
||||||
.stringRemainingTimeWaitingPassenger,
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
if (controller.isdriverWaitTimeEnd)
|
|
||||||
MyElevatedButton(
|
|
||||||
title:
|
|
||||||
'You Can Cancel the Trip and get Cost From '
|
|
||||||
.tr +
|
|
||||||
AppInformation.appName.tr,
|
|
||||||
kolor: AppColor.deepPurpleAccent,
|
|
||||||
onPressed: () {
|
|
||||||
MyDialog().getDialog(
|
|
||||||
'Are you sure to cancel?'.tr, '',
|
|
||||||
() async {
|
|
||||||
Get.find<FirebaseMessagesController>()
|
|
||||||
.sendNotificationToDriverMAP(
|
|
||||||
'Driver Cancelled Your Trip',
|
|
||||||
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
|
|
||||||
.tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'cancel.wav',
|
|
||||||
);
|
|
||||||
Log.print(
|
|
||||||
'rideStatus from passenge info 261 : ${box.read(BoxName.rideStatus)}');
|
|
||||||
box.write(BoxName.rideStatus, 'Cancel');
|
|
||||||
await controller
|
|
||||||
.addWaitingTimeCostFromPassengerToDriverWallet();
|
|
||||||
controller.isdriverWaitTimeEnd = false;
|
|
||||||
Get.back();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
Text(
|
||||||
: const SizedBox(),
|
controller.passengerName,
|
||||||
);
|
style: AppStyle.title
|
||||||
}
|
.copyWith(fontWeight: FontWeight.bold, fontSize: 18),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
Widget _buildInfoTile({
|
),
|
||||||
required IconData icon,
|
],
|
||||||
required String text,
|
),
|
||||||
required String label,
|
|
||||||
}) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: Colors.grey[700]),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(text, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
|
||||||
Text(label.tr, style: AppStyle.title),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMessageOptions(MapDriverController controller) {
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text('Select a quick message'.tr, style: AppStyle.title),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
_buildMessageTile(
|
|
||||||
text: "Where are you, sir?".tr,
|
|
||||||
onTap: () {
|
|
||||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
|
||||||
'message From Driver',
|
|
||||||
"Where are you, sir?".tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'ding.wav',
|
|
||||||
);
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_buildMessageTile(
|
// معلومات المسافة والزمن
|
||||||
text: "I've been trying to reach you but your phone is off.".tr,
|
|
||||||
onTap: () {
|
|
||||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
|
||||||
'message From Driver',
|
|
||||||
"I've been trying to reach you but your phone is off.".tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'ding.wav',
|
|
||||||
);
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMessageTile(
|
|
||||||
text:
|
|
||||||
"Please don't be late, I'm waiting for you at the specified location."
|
|
||||||
.tr,
|
|
||||||
onTap: () {
|
|
||||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
|
||||||
'message From Driver',
|
|
||||||
"Please don't be late, I'm waiting for you at the specified location."
|
|
||||||
.tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'ding.wav',
|
|
||||||
);
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMessageTile(
|
|
||||||
text: "Please don't be late".tr,
|
|
||||||
onTap: () {
|
|
||||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
|
||||||
'message From Driver',
|
|
||||||
"Please don't be late".tr,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'cancel.wav',
|
|
||||||
);
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
_buildInfoChip(Icons.map_outlined, '${controller.distance} km'),
|
||||||
child: Form(
|
const SizedBox(width: 8),
|
||||||
key: controller.formKey2,
|
_buildInfoChip(
|
||||||
child: MyTextForm(
|
Icons.timer_outlined,
|
||||||
controller: controller.messageToPassenger,
|
controller.hours > 1
|
||||||
label: 'Type something'.tr,
|
? '${controller.hours}h ${controller.minutes}m'
|
||||||
hint: 'Type something'.tr,
|
: '${controller.minutes}m',
|
||||||
type: TextInputType.text,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
Get.find<FirebaseMessagesController>()
|
|
||||||
.sendNotificationToDriverMAP(
|
|
||||||
'message From Driver',
|
|
||||||
controller.messageToPassenger.text,
|
|
||||||
controller.tokenPassenger,
|
|
||||||
[],
|
|
||||||
'ding.wav',
|
|
||||||
);
|
|
||||||
controller.messageToPassenger.clear();
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.send),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -389,19 +107,145 @@ class PassengerInfoWindow extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageTile(
|
// New: ودجت مخصص لعرض المعلومات بشكل أنيق
|
||||||
{required String text, required VoidCallback onTap}) {
|
Widget _buildInfoChip(IconData icon, String text) {
|
||||||
return InkWell(
|
return Container(
|
||||||
onTap: onTap,
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
padding: const EdgeInsets.all(12),
|
color: AppColor.primaryColor.withOpacity(0.1),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
borderRadius: BorderRadius.circular(20),
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: Row(
|
||||||
color: Colors.grey[100],
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: [
|
||||||
child: Text(text, style: AppStyle.title),
|
Icon(icon, color: AppColor.primaryColor, size: 16),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColor.primaryColor, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changed: إعادة تصميم أزرار الإجراءات لتكون أكثر دمجًا
|
||||||
|
Widget _buildActionButtons(MapDriverController controller) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
if (controller.isArrivedSend)
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
icon: const Icon(Icons.location_on, size: 18),
|
||||||
|
label: Text('I Arrive'.tr),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppColor.yellowColor,
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
if (await controller
|
||||||
|
.calculateDistanceBetweenDriverAndPassengerLocation() <
|
||||||
|
140) {
|
||||||
|
Get.find<FirebaseMessagesController>()
|
||||||
|
.sendNotificationToDriverMAP(
|
||||||
|
'Hi ,I Arrive your site',
|
||||||
|
'I Arrive at your site'.tr,
|
||||||
|
controller.tokenPassenger,
|
||||||
|
[],
|
||||||
|
'ding.wav',
|
||||||
|
);
|
||||||
|
controller.startTimerToShowDriverWaitPassengerDuration();
|
||||||
|
controller.isArrivedSend = false;
|
||||||
|
} else {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
'You are not near the passenger location'.tr,
|
||||||
|
'Please go to the pickup location exactly'.tr,
|
||||||
|
() => Get.back());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (controller.isArrivedSend) const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
icon: const Icon(Icons.play_arrow_rounded, size: 20),
|
||||||
|
label: Text('Start the Ride'.tr,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppColor.greenColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
"Is the Passenger in your Car?".tr,
|
||||||
|
"Don't start trip if passenger not in your car".tr,
|
||||||
|
() async {
|
||||||
|
await controller.startRideFromDriver();
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed: مؤشر الانتظار الآن أكثر دمجًا
|
||||||
|
Widget _buildWaitingIndicator(MapDriverController controller) {
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
LinearProgressIndicator(
|
||||||
|
backgroundColor: AppColor.greyColor.withOpacity(0.3),
|
||||||
|
color: controller.remainingTimeInPassengerLocatioWait < 60
|
||||||
|
? AppColor.redColor
|
||||||
|
: AppColor.greenColor,
|
||||||
|
minHeight: 25,
|
||||||
|
value: controller.progressInPassengerLocationFromDriver.toDouble(),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
controller.stringRemainingTimeWaitingPassenger,
|
||||||
|
style: AppStyle.title.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13,
|
||||||
|
shadows: [
|
||||||
|
Shadow(color: Colors.black.withOpacity(0.5), blurRadius: 2)
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: زر الإلغاء بعد انتهاء الانتظار
|
||||||
|
Widget _buildCancelAfterWaitButton(MapDriverController controller) {
|
||||||
|
return MyElevatedButton(
|
||||||
|
title: 'You Can Cancel the Trip and get Cost From '.tr +
|
||||||
|
AppInformation.appName.tr,
|
||||||
|
kolor: AppColor.deepPurpleAccent,
|
||||||
|
onPressed: () {
|
||||||
|
MyDialog().getDialog('Are you sure to cancel?'.tr, '', () async {
|
||||||
|
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||||
|
'Driver Cancelled Your Trip',
|
||||||
|
'You will need to pay the cost to the driver, or it will be deducted from your next trip'
|
||||||
|
.tr,
|
||||||
|
controller.tokenPassenger,
|
||||||
|
[],
|
||||||
|
'cancel.wav');
|
||||||
|
box.write(BoxName.rideStatus, 'Cancel');
|
||||||
|
await controller.addWaitingTimeCostFromPassengerToDriverWallet();
|
||||||
|
controller.isdriverWaitTimeEnd = false;
|
||||||
|
Get.back();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +1,287 @@
|
|||||||
import 'dart:io';
|
// import 'dart:io';
|
||||||
|
|
||||||
|
// import 'package:bubble_head/bubble.dart';
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||||
|
// import 'package:get/get.dart';
|
||||||
|
// import 'package:sefer_driver/constant/info.dart';
|
||||||
|
// import 'package:sefer_driver/controller/functions/location_controller.dart';
|
||||||
|
// import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||||
|
// import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||||
|
// import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
// import '../../../../constant/box_name.dart';
|
||||||
|
// import '../../../../constant/colors.dart';
|
||||||
|
// import '../../../../constant/style.dart';
|
||||||
|
// import '../../../../controller/functions/launch.dart';
|
||||||
|
// import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||||
|
// import '../../../../main.dart';
|
||||||
|
|
||||||
|
// class SosConnect extends StatelessWidget {
|
||||||
|
// const SosConnect({super.key});
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return GetBuilder<MapDriverController>(
|
||||||
|
// builder: (mapDriverController) => mapDriverController.isRideStarted
|
||||||
|
// ? Positioned(
|
||||||
|
// left: 16,
|
||||||
|
// bottom: 16,
|
||||||
|
// child: Card(
|
||||||
|
// elevation: 4,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(12),
|
||||||
|
// ),
|
||||||
|
// child: SizedBox(
|
||||||
|
// height: 60,
|
||||||
|
// width: 180,
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
// children: [
|
||||||
|
// IconButton(
|
||||||
|
// onPressed: () {
|
||||||
|
// _handleSosCall(mapDriverController);
|
||||||
|
// },
|
||||||
|
// icon: const Icon(
|
||||||
|
// Icons.sos_sharp,
|
||||||
|
// size: 32,
|
||||||
|
// color: AppColor.redColor,
|
||||||
|
// ),
|
||||||
|
// tooltip: 'SOS - Call Emergency',
|
||||||
|
// ),
|
||||||
|
// VerticalDivider(
|
||||||
|
// color: Colors.grey[300],
|
||||||
|
// thickness: 1,
|
||||||
|
// ),
|
||||||
|
// IconButton(
|
||||||
|
// onPressed: () {
|
||||||
|
// _handleWhatsApp(mapDriverController);
|
||||||
|
// },
|
||||||
|
// icon: const Icon(
|
||||||
|
// FontAwesome.whatsapp,
|
||||||
|
// color: AppColor.greenColor,
|
||||||
|
// size: 32,
|
||||||
|
// ),
|
||||||
|
// tooltip: 'SOS - Send WhatsApp Message',
|
||||||
|
// ),
|
||||||
|
// VerticalDivider(
|
||||||
|
// color: Colors.grey[300],
|
||||||
|
// thickness: 1,
|
||||||
|
// ),
|
||||||
|
// IconButton(
|
||||||
|
// onPressed: () {
|
||||||
|
// _handleGoogleMap(mapDriverController);
|
||||||
|
// },
|
||||||
|
// icon: const Icon(
|
||||||
|
// MaterialCommunityIcons.map_marker_radius,
|
||||||
|
// color: AppColor.primaryColor,
|
||||||
|
// size: 32,
|
||||||
|
// ),
|
||||||
|
// tooltip: 'Google Maps - Navigate',
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : const SizedBox(),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void _handleSosCall(MapDriverController mapDriverController) {
|
||||||
|
// if (box.read(BoxName.sosPhoneDriver) == null) {
|
||||||
|
// Get.defaultDialog(
|
||||||
|
// title: 'Insert Emergency Number'.tr,
|
||||||
|
// content: Form(
|
||||||
|
// key: mapDriverController.formKey1,
|
||||||
|
// child: MyTextForm(
|
||||||
|
// controller: mapDriverController.sosEmergincyNumberCotroller,
|
||||||
|
// label: 'Emergency Number'.tr,
|
||||||
|
// hint: 'Enter phone number'.tr,
|
||||||
|
// type: TextInputType.phone,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// confirm: MyElevatedButton(
|
||||||
|
// title: 'Save'.tr,
|
||||||
|
// onPressed: () {
|
||||||
|
// if (mapDriverController.formKey1.currentState!.validate()) {
|
||||||
|
// box.write(BoxName.sosPhoneDriver,
|
||||||
|
// mapDriverController.sosEmergincyNumberCotroller.text);
|
||||||
|
// Get.back(); // Close the dialog
|
||||||
|
// launchCommunication(
|
||||||
|
// 'phone', box.read(BoxName.sosPhoneDriver), '');
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// launchCommunication('phone', box.read(BoxName.sosPhoneDriver), '');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void _handleWhatsApp(MapDriverController mapDriverController) {
|
||||||
|
// if (box.read(BoxName.sosPhoneDriver) == null) {
|
||||||
|
// Get.defaultDialog(
|
||||||
|
// title: 'Insert Emergency Number'.tr,
|
||||||
|
// content: Form(
|
||||||
|
// key: mapDriverController.formKey1,
|
||||||
|
// child: MyTextForm(
|
||||||
|
// controller: mapDriverController.sosEmergincyNumberCotroller,
|
||||||
|
// label: 'Emergency Number'.tr,
|
||||||
|
// hint: 'Enter phone number'.tr,
|
||||||
|
// type: TextInputType.phone,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// confirm: MyElevatedButton(
|
||||||
|
// title: 'Save'.tr,
|
||||||
|
// onPressed: () {
|
||||||
|
// if (mapDriverController.formKey1.currentState!.validate()) {
|
||||||
|
// box.write(BoxName.sosPhoneDriver,
|
||||||
|
// mapDriverController.sosEmergincyNumberCotroller.text);
|
||||||
|
// Get.back(); // Close the dialog
|
||||||
|
// _sendWhatsAppMessage(mapDriverController);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// _sendWhatsAppMessage(mapDriverController);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void _handleGoogleMap(MapDriverController mapDriverController) {
|
||||||
|
// () async {
|
||||||
|
// if (Platform.isAndroid) {
|
||||||
|
// Bubble().startBubbleHead(sendAppToBackground: true);
|
||||||
|
// }
|
||||||
|
// var startLat =
|
||||||
|
// Get.find<MapDriverController>().latLngPassengerLocation.latitude;
|
||||||
|
// var startLng =
|
||||||
|
// Get.find<MapDriverController>().latLngPassengerLocation.longitude;
|
||||||
|
|
||||||
|
// var endLat =
|
||||||
|
// Get.find<MapDriverController>().latLngPassengerDestination.latitude;
|
||||||
|
// var endLng =
|
||||||
|
// Get.find<MapDriverController>().latLngPassengerDestination.longitude;
|
||||||
|
|
||||||
|
// String url =
|
||||||
|
// 'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
|
||||||
|
// if (await canLaunchUrl(Uri.parse(url))) {
|
||||||
|
// await launchUrl(Uri.parse(url));
|
||||||
|
// } else {
|
||||||
|
// throw 'Could not launch google maps';
|
||||||
|
// }
|
||||||
|
// }();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void _sendWhatsAppMessage(MapDriverController mapDriverController) {
|
||||||
|
// final sosNumber = box.read(BoxName.sosPhoneDriver);
|
||||||
|
// if (sosNumber != null) {
|
||||||
|
// launchCommunication(
|
||||||
|
// 'whatsapp',
|
||||||
|
// '+2$sosNumber', // Consider international format
|
||||||
|
// "${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
|
||||||
|
// "${"My current location is:".tr} "
|
||||||
|
// "https://www.google.com/maps/place/"
|
||||||
|
// "${Get.find<LocationController>().myLocation.latitude},"
|
||||||
|
// "${Get.find<LocationController>().myLocation.longitude} "
|
||||||
|
// "${"\nI have a trip on".tr} ${AppInformation.appName} "
|
||||||
|
// "${"app with passenger".tr} ${mapDriverController.passengerName}.",
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
import 'package:bubble_head/bubble.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sefer_driver/constant/info.dart';
|
|
||||||
import 'package:sefer_driver/controller/functions/location_controller.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
||||||
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
import 'package:sefer_driver/views/widgets/my_textField.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
import '../../../../constant/box_name.dart';
|
import '../../../../constant/box_name.dart';
|
||||||
import '../../../../constant/colors.dart';
|
import '../../../../constant/colors.dart';
|
||||||
import '../../../../constant/style.dart';
|
import '../../../../constant/style.dart';
|
||||||
|
import '../../../../controller/firebase/firbase_messge.dart';
|
||||||
import '../../../../controller/functions/launch.dart';
|
import '../../../../controller/functions/launch.dart';
|
||||||
import '../../../../controller/home/captin/map_driver_controller.dart';
|
import '../../../../controller/home/captin/map_driver_controller.dart';
|
||||||
import '../../../../main.dart';
|
import '../../../../main.dart';
|
||||||
|
|
||||||
|
// Changed: إعادة تصميم وتغيير موضع أزرار التواصل والطوارئ
|
||||||
class SosConnect extends StatelessWidget {
|
class SosConnect extends StatelessWidget {
|
||||||
const SosConnect({super.key});
|
const SosConnect({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<MapDriverController>(
|
return GetBuilder<MapDriverController>(
|
||||||
builder: (mapDriverController) => mapDriverController.isRideStarted
|
builder: (controller) {
|
||||||
? Positioned(
|
// New: تجميع الأزرار في عمود واحد على الجانب الأيمن
|
||||||
left: 16,
|
return Positioned(
|
||||||
bottom: 16,
|
bottom: 110, // New: فوق عداد السرعة
|
||||||
child: Card(
|
right: 16,
|
||||||
elevation: 4,
|
child: Column(
|
||||||
shape: RoundedRectangleBorder(
|
children: [
|
||||||
borderRadius: BorderRadius.circular(12),
|
// زر الاتصال بالراكب (يظهر قبل بدء الرحلة)
|
||||||
|
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
|
||||||
|
_buildSocialButton(
|
||||||
|
icon: Icons.phone,
|
||||||
|
color: AppColor.blueColor,
|
||||||
|
tooltip: 'Call Passenger',
|
||||||
|
onPressed: () async {
|
||||||
|
controller.isSocialPressed = true;
|
||||||
|
await controller.driverCallPassenger();
|
||||||
|
makePhoneCall(controller.passengerPhone.toString());
|
||||||
|
},
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
|
||||||
height: 60,
|
// زر الرسائل للراكب (يظهر قبل بدء الرحلة)
|
||||||
width: 180,
|
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
|
||||||
child: Row(
|
const SizedBox(height: 12),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
if (!controller.isRideBegin && controller.isPassengerInfoWindow)
|
||||||
children: [
|
_buildSocialButton(
|
||||||
IconButton(
|
icon: Icons.message,
|
||||||
onPressed: () {
|
color: AppColor.greenColor,
|
||||||
_handleSosCall(mapDriverController);
|
tooltip: 'Send Message',
|
||||||
},
|
onPressed: () {
|
||||||
icon: const Icon(
|
// الكود الخاص بنافذة الرسائل السريعة
|
||||||
Icons.sos_sharp,
|
_showMessageOptions(context, controller);
|
||||||
size: 32,
|
},
|
||||||
color: AppColor.redColor,
|
|
||||||
),
|
|
||||||
tooltip: 'SOS - Call Emergency',
|
|
||||||
),
|
|
||||||
VerticalDivider(
|
|
||||||
color: Colors.grey[300],
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
_handleWhatsApp(mapDriverController);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
FontAwesome.whatsapp,
|
|
||||||
color: AppColor.greenColor,
|
|
||||||
size: 32,
|
|
||||||
),
|
|
||||||
tooltip: 'SOS - Send WhatsApp Message',
|
|
||||||
),
|
|
||||||
VerticalDivider(
|
|
||||||
color: Colors.grey[300],
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
_handleGoogleMap(mapDriverController);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
MaterialCommunityIcons.map_marker_radius,
|
|
||||||
color: AppColor.primaryColor,
|
|
||||||
size: 32,
|
|
||||||
),
|
|
||||||
tooltip: 'Google Maps - Navigate',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)
|
// زر الطوارئ (SOS) (يظهر بعد بدء الرحلة)
|
||||||
: const SizedBox(),
|
if (controller.isRideStarted)
|
||||||
|
_buildSocialButton(
|
||||||
|
icon: Icons.sos_sharp,
|
||||||
|
color: AppColor.redColor,
|
||||||
|
tooltip: 'SOS - Call Emergency',
|
||||||
|
onPressed: () => _handleSosCall(controller),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New: ودجت منفصل لبناء أزرار التواصل
|
||||||
|
Widget _buildSocialButton(
|
||||||
|
{required IconData icon,
|
||||||
|
required Color color,
|
||||||
|
required String tooltip,
|
||||||
|
required VoidCallback onPressed}) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(icon, color: color, size: 28),
|
||||||
|
tooltip: tooltip,
|
||||||
|
onPressed: onPressed,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// الكود الخاص بنافذة إدخال رقم الطوارئ
|
||||||
void _handleSosCall(MapDriverController mapDriverController) {
|
void _handleSosCall(MapDriverController mapDriverController) {
|
||||||
if (box.read(BoxName.sosPhoneDriver) == null) {
|
if (box.read(BoxName.sosPhoneDriver) == null) {
|
||||||
Get.defaultDialog(
|
Get.defaultDialog(
|
||||||
@@ -107,7 +301,7 @@ class SosConnect extends StatelessWidget {
|
|||||||
if (mapDriverController.formKey1.currentState!.validate()) {
|
if (mapDriverController.formKey1.currentState!.validate()) {
|
||||||
box.write(BoxName.sosPhoneDriver,
|
box.write(BoxName.sosPhoneDriver,
|
||||||
mapDriverController.sosEmergincyNumberCotroller.text);
|
mapDriverController.sosEmergincyNumberCotroller.text);
|
||||||
Get.back(); // Close the dialog
|
Get.back();
|
||||||
launchCommunication(
|
launchCommunication(
|
||||||
'phone', box.read(BoxName.sosPhoneDriver), '');
|
'phone', box.read(BoxName.sosPhoneDriver), '');
|
||||||
}
|
}
|
||||||
@@ -119,75 +313,99 @@ class SosConnect extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleWhatsApp(MapDriverController mapDriverController) {
|
// New: الكود الخاص بنافذة الرسائل السريعة (مستخرج من passenger_info_window.dart)
|
||||||
if (box.read(BoxName.sosPhoneDriver) == null) {
|
void _showMessageOptions(
|
||||||
Get.defaultDialog(
|
BuildContext context, MapDriverController controller) {
|
||||||
title: 'Insert Emergency Number'.tr,
|
Get.bottomSheet(
|
||||||
content: Form(
|
backgroundColor: Colors.white,
|
||||||
key: mapDriverController.formKey1,
|
shape: const RoundedRectangleBorder(
|
||||||
child: MyTextForm(
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
controller: mapDriverController.sosEmergincyNumberCotroller,
|
),
|
||||||
label: 'Emergency Number'.tr,
|
Padding(
|
||||||
hint: 'Enter phone number'.tr,
|
padding: const EdgeInsets.all(16.0),
|
||||||
type: TextInputType.phone,
|
child: _buildMessageOptions(controller),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
confirm: MyElevatedButton(
|
|
||||||
title: 'Save'.tr,
|
|
||||||
onPressed: () {
|
|
||||||
if (mapDriverController.formKey1.currentState!.validate()) {
|
|
||||||
box.write(BoxName.sosPhoneDriver,
|
|
||||||
mapDriverController.sosEmergincyNumberCotroller.text);
|
|
||||||
Get.back(); // Close the dialog
|
|
||||||
_sendWhatsAppMessage(mapDriverController);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_sendWhatsAppMessage(mapDriverController);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGoogleMap(MapDriverController mapDriverController) {
|
Widget _buildMessageOptions(MapDriverController controller) {
|
||||||
() async {
|
return Column(
|
||||||
if (Platform.isAndroid) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
Bubble().startBubbleHead(sendAppToBackground: true);
|
children: [
|
||||||
}
|
Text('Select a quick message'.tr, style: AppStyle.title),
|
||||||
var startLat =
|
const SizedBox(height: 16),
|
||||||
Get.find<MapDriverController>().latLngPassengerLocation.latitude;
|
_buildMessageTile(
|
||||||
var startLng =
|
text: "Where are you, sir?".tr,
|
||||||
Get.find<MapDriverController>().latLngPassengerLocation.longitude;
|
onTap: () {
|
||||||
|
Get.find<FirebaseMessagesController>()
|
||||||
var endLat =
|
.sendNotificationToDriverMAP(
|
||||||
Get.find<MapDriverController>().latLngPassengerDestination.latitude;
|
'message From Driver',
|
||||||
var endLng =
|
"Where are you, sir?".tr,
|
||||||
Get.find<MapDriverController>().latLngPassengerDestination.longitude;
|
controller.tokenPassenger,
|
||||||
|
[],
|
||||||
String url =
|
'ding.wav');
|
||||||
'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
|
Get.back();
|
||||||
if (await canLaunchUrl(Uri.parse(url))) {
|
}),
|
||||||
await launchUrl(Uri.parse(url));
|
_buildMessageTile(
|
||||||
} else {
|
text: "I've been trying to reach you but your phone is off.".tr,
|
||||||
throw 'Could not launch google maps';
|
onTap: () {
|
||||||
}
|
Get.find<FirebaseMessagesController>()
|
||||||
}();
|
.sendNotificationToDriverMAP(
|
||||||
|
'message From Driver',
|
||||||
|
"I've been trying to reach you but your phone is off.".tr,
|
||||||
|
controller.tokenPassenger,
|
||||||
|
[],
|
||||||
|
'ding.wav');
|
||||||
|
Get.back();
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Form(
|
||||||
|
key: controller.formKey2,
|
||||||
|
child: MyTextForm(
|
||||||
|
controller: controller.messageToPassenger,
|
||||||
|
label: 'Type something'.tr,
|
||||||
|
hint: 'Type something'.tr,
|
||||||
|
type: TextInputType.text,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.find<FirebaseMessagesController>()
|
||||||
|
.sendNotificationToDriverMAP(
|
||||||
|
'message From Driver',
|
||||||
|
controller.messageToPassenger.text,
|
||||||
|
controller.tokenPassenger,
|
||||||
|
[],
|
||||||
|
'ding.wav');
|
||||||
|
controller.messageToPassenger.clear();
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.send),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sendWhatsAppMessage(MapDriverController mapDriverController) {
|
Widget _buildMessageTile(
|
||||||
final sosNumber = box.read(BoxName.sosPhoneDriver);
|
{required String text, required VoidCallback onTap}) {
|
||||||
if (sosNumber != null) {
|
return InkWell(
|
||||||
launchCommunication(
|
onTap: onTap,
|
||||||
'whatsapp',
|
child: Container(
|
||||||
'+2$sosNumber', // Consider international format
|
width: double.infinity,
|
||||||
"${"Hello, this is Driver".tr} ${box.read(BoxName.nameDriver)}. "
|
padding: const EdgeInsets.all(12),
|
||||||
"${"My current location is:".tr} "
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
"https://www.google.com/maps/place/"
|
decoration: BoxDecoration(
|
||||||
"${Get.find<LocationController>().myLocation.latitude},"
|
borderRadius: BorderRadius.circular(8),
|
||||||
"${Get.find<LocationController>().myLocation.longitude} "
|
color: Colors.grey[100],
|
||||||
"${"\nI have a trip on".tr} ${AppInformation.appName} "
|
),
|
||||||
"${"app with passenger".tr} ${mapDriverController.passengerName}.",
|
child: Text(text, style: AppStyle.title),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class WalletCaptainRefactored extends StatelessWidget {
|
|||||||
.speakText(
|
.speakText(
|
||||||
'This amount for all trip I get from Passengers and Collected For me in'
|
'This amount for all trip I get from Passengers and Collected For me in'
|
||||||
.tr +
|
.tr +
|
||||||
' SAFAR Wallet'.tr),
|
' Intaleq Wallet'.tr),
|
||||||
child: const Icon(Icons.headphones),
|
child: const Icon(Icons.headphones),
|
||||||
),
|
),
|
||||||
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
|
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class BehaviorPage extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const Text("Overall Behavior Score",
|
Text("Overall Behavior Score".tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20, fontWeight: FontWeight.bold)),
|
fontSize: 20, fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
@@ -49,7 +49,7 @@ class BehaviorPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
const Text("Last 10 Trips",
|
Text("Last 10 Trips".tr,
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
@@ -70,10 +70,12 @@ class BehaviorPage extends StatelessWidget {
|
|||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Behavior Score: ${trip['behavior_score']}"),
|
Text(
|
||||||
Text("Max Speed: ${trip['max_speed']} km/h"),
|
"${'Behavior Score'.tr}: ${trip['behavior_score']}"),
|
||||||
Text("Hard Brakes: ${trip['hard_brakes']}"),
|
Text("${'Max Speed'.tr}: ${trip['max_speed']} km/h"),
|
||||||
Text("Distance: ${trip['total_distance']} km"),
|
Text("${'Hard Brake'.tr}s: ${trip['hard_brakes']}"),
|
||||||
|
Text(
|
||||||
|
"${'Distance'.tr}: ${trip['total_distance']} km"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,373 +1,202 @@
|
|||||||
import 'package:sefer_driver/constant/style.dart';
|
|
||||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
|
||||||
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
|
|
||||||
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sefer_driver/controller/profile/captain_profile_controller.dart';
|
import 'package:sefer_driver/controller/profile/captain_profile_controller.dart';
|
||||||
|
import 'package:sefer_driver/views/auth/captin/criminal_documents_page.dart';
|
||||||
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
import 'package:sefer_driver/views/widgets/my_scafold.dart';
|
||||||
|
import 'package:sefer_driver/views/widgets/mycircular.dart';
|
||||||
import '../my_wallet/walet_captain.dart';
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
import 'behavior_page.dart';
|
import 'behavior_page.dart';
|
||||||
import 'captains_cars.dart';
|
import 'captains_cars.dart';
|
||||||
|
|
||||||
|
// الصفحة الرئيسية الجديدة
|
||||||
class ProfileCaptain extends StatelessWidget {
|
class ProfileCaptain extends StatelessWidget {
|
||||||
ProfileCaptain({super.key});
|
const ProfileCaptain({super.key});
|
||||||
CaptainWalletController captainWalletController = CaptainWalletController();
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(CaptainProfileController());
|
// Get.put() يجب أن يكون في مكان يتم استدعاؤه مرة واحدة فقط،
|
||||||
|
// لكن سنبقيه هنا حسب الكود الأصلي
|
||||||
|
final controller = Get.put(CaptainProfileController());
|
||||||
|
|
||||||
return MyScafolld(
|
return MyScafolld(
|
||||||
title: 'My Profile'.tr,
|
title: 'My Profile'.tr,
|
||||||
|
isleading: true,
|
||||||
body: [
|
body: [
|
||||||
GetBuilder<CaptainProfileController>(
|
GetBuilder<CaptainProfileController>(
|
||||||
builder: (controller) => Padding(
|
builder: (controller) {
|
||||||
padding: const EdgeInsets.all(16.0),
|
if (controller.isLoading) {
|
||||||
child: SingleChildScrollView(
|
return const Center(child: MyCircularProgressIndicator());
|
||||||
child: Center(
|
}
|
||||||
child: controller.isLoading
|
if (controller.captainProfileData.isEmpty) {
|
||||||
? const MyCircularProgressIndicator()
|
return Center(child: Text('Failed to load profile data.'.tr));
|
||||||
: Column(
|
}
|
||||||
children: [
|
return SingleChildScrollView(
|
||||||
Material(
|
padding:
|
||||||
elevation: 2,
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: Column(
|
||||||
child: Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
// 1. رأس الصفحة: صورة واسم وتقييم
|
||||||
children: [
|
ProfileHeader(
|
||||||
InkWell(
|
name:
|
||||||
onTap: () async {
|
'${controller.captainProfileData['first_name'] ?? ''} ${controller.captainProfileData['last_name'] ?? ''}',
|
||||||
// addBankCodeEgypt(captainWalletController);
|
rating:
|
||||||
},
|
controller.captainProfileData['ratingDriver'] != null
|
||||||
borderRadius: BorderRadius.circular(8),
|
? double.tryParse(controller
|
||||||
child: Padding(
|
.captainProfileData['ratingDriver']
|
||||||
padding: const EdgeInsets.symmetric(
|
.toString()) ??
|
||||||
vertical: 16, horizontal: 16),
|
0.0
|
||||||
child: Text(
|
: 0.0,
|
||||||
'Add bank Account'.tr,
|
ratingCount: int.tryParse(controller
|
||||||
style: AppStyle.title,
|
.captainProfileData['ratingCount']
|
||||||
),
|
.toString()) ??
|
||||||
),
|
0,
|
||||||
),
|
),
|
||||||
InkWell(
|
const SizedBox(height: 24),
|
||||||
onTap: () async {
|
|
||||||
Get.to(() => BehaviorPage());
|
// 2. قسم الإجراءات السريعة
|
||||||
},
|
ActionsGrid(),
|
||||||
borderRadius: BorderRadius.circular(8),
|
const SizedBox(height: 24),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
// 3. بطاقة المعلومات الشخصية
|
||||||
vertical: 16, horizontal: 16),
|
PersonalInfoCard(
|
||||||
child: Text(
|
data: controller.captainProfileData
|
||||||
'Show behavior page'.tr,
|
.cast<String, dynamic>()),
|
||||||
style: AppStyle.title,
|
const SizedBox(height: 16),
|
||||||
),
|
|
||||||
),
|
// 4. بطاقة معلومات المركبة (قابلة للتوسيع)
|
||||||
),
|
VehicleInfoCard(
|
||||||
],
|
data: controller.captainProfileData
|
||||||
),
|
.cast<String, dynamic>()),
|
||||||
),
|
],
|
||||||
const SizedBox(height: 15),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
MyElevatedButton(
|
|
||||||
title: 'Show my Cars'.tr,
|
|
||||||
onPressed: () async {
|
|
||||||
Get.to(() => CaptainsCars());
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// const SizedBox(height: 15),
|
|
||||||
MyElevatedButton(
|
|
||||||
title: 'Add criminal page'.tr,
|
|
||||||
onPressed: () async {
|
|
||||||
Get.to(() => CriminalDocumemtPage());
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: Get.height * .7,
|
|
||||||
child: DriverProfileCard(
|
|
||||||
driverId:
|
|
||||||
controller.captainProfileData['driverID'] ??
|
|
||||||
'',
|
|
||||||
name:
|
|
||||||
'${((controller.captainProfileData['first_name']) ?? '')} ${((controller.captainProfileData['last_name']) ?? '')}',
|
|
||||||
phoneNumber:
|
|
||||||
controller.captainProfileData['phone'] ?? '',
|
|
||||||
email:
|
|
||||||
controller.captainProfileData['email'] ?? '',
|
|
||||||
birthdate: controller
|
|
||||||
.captainProfileData['birthdate'] is String
|
|
||||||
? controller.captainProfileData['birthdate']
|
|
||||||
: '',
|
|
||||||
gender: controller.captainProfileData['gender']
|
|
||||||
is String
|
|
||||||
? controller.captainProfileData['gender']
|
|
||||||
: '',
|
|
||||||
education: controller
|
|
||||||
.captainProfileData['education'] is String
|
|
||||||
? controller.captainProfileData['education']
|
|
||||||
: '',
|
|
||||||
carMake:
|
|
||||||
controller.captainProfileData['make'] ?? '',
|
|
||||||
carModel:
|
|
||||||
controller.captainProfileData['model'] ?? '',
|
|
||||||
carPlate:
|
|
||||||
controller.captainProfileData['car_plate'] ??
|
|
||||||
'',
|
|
||||||
carColor:
|
|
||||||
controller.captainProfileData['color'] ?? '',
|
|
||||||
vin: controller.captainProfileData['vin'] ?? '',
|
|
||||||
registrationDate: controller.captainProfileData[
|
|
||||||
'registration_date'] ??
|
|
||||||
'',
|
|
||||||
expirationDate: controller
|
|
||||||
.captainProfileData['expiration_date'] ??
|
|
||||||
'',
|
|
||||||
ratingCount: int.tryParse(controller
|
|
||||||
.captainProfileData['ratingCount']
|
|
||||||
.toString()) ??
|
|
||||||
0,
|
|
||||||
ratingDriver: controller
|
|
||||||
.captainProfileData['ratingDriver'] !=
|
|
||||||
null
|
|
||||||
? double.tryParse(controller
|
|
||||||
.captainProfileData['ratingDriver']
|
|
||||||
.toString()) ??
|
|
||||||
0
|
|
||||||
: null,
|
|
||||||
age: int.tryParse(controller
|
|
||||||
.captainProfileData['age']
|
|
||||||
.toString()) ??
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
isleading: true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DriverProfileCard extends StatelessWidget {
|
// --- الويدجتس الجديدة المنفصلة لتحسين التصميم ---
|
||||||
final String driverId;
|
|
||||||
final String name;
|
|
||||||
final String phoneNumber;
|
|
||||||
final String email;
|
|
||||||
final String birthdate;
|
|
||||||
final String gender;
|
|
||||||
final String education;
|
|
||||||
final String carMake;
|
|
||||||
final String carModel;
|
|
||||||
final String carPlate;
|
|
||||||
final String carColor;
|
|
||||||
final String vin;
|
|
||||||
final String registrationDate;
|
|
||||||
final String expirationDate;
|
|
||||||
final int ratingCount;
|
|
||||||
final double? ratingDriver;
|
|
||||||
final int age;
|
|
||||||
|
|
||||||
DriverProfileCard({
|
/// 1. ويدجت رأس الصفحة
|
||||||
required this.driverId,
|
class ProfileHeader extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final double rating;
|
||||||
|
final int ratingCount;
|
||||||
|
|
||||||
|
const ProfileHeader({
|
||||||
|
super.key,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.phoneNumber,
|
required this.rating,
|
||||||
required this.email,
|
|
||||||
required this.birthdate,
|
|
||||||
required this.gender,
|
|
||||||
required this.education,
|
|
||||||
required this.carMake,
|
|
||||||
required this.carModel,
|
|
||||||
required this.carPlate,
|
|
||||||
required this.carColor,
|
|
||||||
required this.vin,
|
|
||||||
required this.registrationDate,
|
|
||||||
required this.expirationDate,
|
|
||||||
required this.ratingCount,
|
required this.ratingCount,
|
||||||
required this.ratingDriver,
|
|
||||||
required this.age,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Column(
|
||||||
// elevation: 8,
|
children: [
|
||||||
decoration: AppStyle.boxDecoration1,
|
CircleAvatar(
|
||||||
margin: const EdgeInsets.all(16),
|
radius: 50,
|
||||||
child: SingleChildScrollView(
|
backgroundColor: Get.theme.primaryColor.withOpacity(0.1),
|
||||||
|
child: Icon(Icons.person, size: 60, color: Get.theme.primaryColor),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: Get.textTheme.headlineSmall
|
||||||
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.star, color: Colors.amber, size: 20),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${rating.toStringAsFixed(1)} (${'reviews'.tr} $ratingCount)',
|
||||||
|
style: Get.textTheme.titleMedium
|
||||||
|
?.copyWith(color: Colors.grey.shade600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 2. ويدجت شبكة الأزرار
|
||||||
|
class ActionsGrid extends StatelessWidget {
|
||||||
|
const ActionsGrid({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GridView.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
crossAxisSpacing: 16,
|
||||||
|
mainAxisSpacing: 16,
|
||||||
|
childAspectRatio: 2.5, // للتحكم في ارتفاع الأزرار
|
||||||
|
children: [
|
||||||
|
_ActionTile(
|
||||||
|
title: 'My Cars'.tr,
|
||||||
|
icon: Icons.directions_car_filled,
|
||||||
|
onTap: () => Get.to(() => CaptainsCars()),
|
||||||
|
),
|
||||||
|
_ActionTile(
|
||||||
|
title: 'Criminal Record'.tr,
|
||||||
|
icon: Icons.description,
|
||||||
|
onTap: () => Get.to(() => CriminalDocumemtPage()),
|
||||||
|
),
|
||||||
|
_ActionTile(
|
||||||
|
title: 'Bank Account'.tr,
|
||||||
|
icon: Icons.account_balance,
|
||||||
|
onTap: () {
|
||||||
|
MyDialog().getDialog('Coming Soon'.tr,
|
||||||
|
'This service will be available soon.'.tr, () => Get.back());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_ActionTile(
|
||||||
|
title: 'Behavior Page'.tr,
|
||||||
|
icon: Icons.checklist_rtl,
|
||||||
|
onTap: () => Get.to(() => BehaviorPage()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ويدجت داخلية لزر في الشبكة
|
||||||
|
class _ActionTile extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _ActionTile(
|
||||||
|
{required this.title, required this.icon, required this.onTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
color: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Icon(icon, color: Get.theme.primaryColor, size: 20),
|
||||||
style: AppStyle.title,
|
const SizedBox(width: 8),
|
||||||
name,
|
Flexible(
|
||||||
),
|
child: Text(
|
||||||
const SizedBox(height: 8),
|
title,
|
||||||
Row(
|
style: Get.textTheme.labelLarge,
|
||||||
children: [
|
textAlign: TextAlign.center,
|
||||||
const Icon(Icons.phone),
|
)),
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(style: AppStyle.title, phoneNumber),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.calendar_today),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'birthdate'.tr} : $birthdate',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.wc),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'gender'.tr} : $gender',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.school),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'education'.tr} : $education',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.car_repair),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'Make'.tr} : $carMake',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.model_training),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'car_model'.tr} : $carModel',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.drive_eta),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'car_plate'.tr} : $carPlate',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.color_lens),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'car_color'.tr} : $carColor',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.confirmation_number),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'vin'.tr} : $vin',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.calendar_today),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'registration_date'.tr} : $registrationDate',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.calendar_today),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'expiration_date'.tr} : $expirationDate',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.star),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'rating_count'.tr} : $ratingCount',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.star_rate),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
ratingDriver != null
|
|
||||||
? Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'rating_driver'.tr} : $ratingDriver',
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'rating_driver'.tr} : 0',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.person),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
style: AppStyle.title,
|
|
||||||
'${'age'.tr} : $age',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -375,3 +204,147 @@ class DriverProfileCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 3. بطاقة المعلومات الشخصية
|
||||||
|
class PersonalInfoCard extends StatelessWidget {
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
PersonalInfoCard({super.key, required this.data});
|
||||||
|
final controller = Get.find<CaptainProfileController>();
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Personal Information'.tr, style: Get.textTheme.titleLarge),
|
||||||
|
const Divider(height: 24),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.phone,
|
||||||
|
label: 'Phone Number'.tr,
|
||||||
|
value: data['phone'] ?? ''),
|
||||||
|
if (data['email'] != null &&
|
||||||
|
data['email'].toString().contains('intaleqapp')) ...[
|
||||||
|
TextFormField(
|
||||||
|
controller: controller.emailController,
|
||||||
|
keyboardType:
|
||||||
|
TextInputType.emailAddress, // ✅ لوحة مفاتيح خاصة بالإيميل
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Email'.tr,
|
||||||
|
hintText: 'Enter your email'.tr,
|
||||||
|
prefixIcon: Icon(Icons.email),
|
||||||
|
),
|
||||||
|
autofillHints: [
|
||||||
|
AutofillHints.email
|
||||||
|
], // اختياري لتحسين تجربة المستخدم
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.updateEmail();
|
||||||
|
},
|
||||||
|
child: Text('Update'.tr),
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.email,
|
||||||
|
label: 'Email'.tr,
|
||||||
|
value: data['email'] ?? '',
|
||||||
|
),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.cake,
|
||||||
|
label: 'Age'.tr,
|
||||||
|
value: data['age']?.toString() ?? 'N/A'),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.wc,
|
||||||
|
label: 'Gender'.tr,
|
||||||
|
value: data['gender'] ?? 'N/A'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 4. بطاقة معلومات المركبة
|
||||||
|
class VehicleInfoCard extends StatelessWidget {
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
const VehicleInfoCard({super.key, required this.data});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text('Vehicle Details'.tr, style: Get.textTheme.titleLarge),
|
||||||
|
leading: Icon(Icons.directions_car, color: Get.theme.primaryColor),
|
||||||
|
childrenPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
|
children: [
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.branding_watermark,
|
||||||
|
label: 'Make'.tr,
|
||||||
|
value: data['make'] ?? ''),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.category,
|
||||||
|
label: 'Model'.tr,
|
||||||
|
value: data['model'] ?? ''),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.palette,
|
||||||
|
label: 'Color'.tr,
|
||||||
|
value: data['color'] ?? ''),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.pin,
|
||||||
|
label: 'Plate Number'.tr,
|
||||||
|
value: data['car_plate'] ?? ''),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.confirmation_number,
|
||||||
|
label: 'VIN'.tr,
|
||||||
|
value: data['vin'] ?? ''),
|
||||||
|
_InfoRow(
|
||||||
|
icon: Icons.event,
|
||||||
|
label: 'Expiration Date'.tr,
|
||||||
|
value: data['expiration_date'] ?? ''),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ويدجت لعرض سطر معلومة (أيقونة + عنوان + قيمة) لتجنب التكرار
|
||||||
|
class _InfoRow extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String label;
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
const _InfoRow(
|
||||||
|
{required this.icon, required this.label, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, color: Colors.grey.shade500, size: 20),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Text(label, style: Get.textTheme.bodyLarge),
|
||||||
|
const Spacer(),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: Get.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.grey.shade700, fontWeight: FontWeight.w500),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class MyDialog extends GetxController {
|
|||||||
Get.back();
|
Get.back();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Cancel',
|
'Cancel'.tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: AppColor.redColor,
|
color: AppColor.redColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@@ -836,6 +836,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.1"
|
version: "5.2.1"
|
||||||
|
flutter_staggered_animations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_staggered_animations
|
||||||
|
sha256: "81d3c816c9bb0dca9e8a5d5454610e21ffb068aedb2bde49d2f8d04f75538351"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
flutter_stripe:
|
flutter_stripe:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ dependencies:
|
|||||||
shimmer: ^3.0.0
|
shimmer: ^3.0.0
|
||||||
flutter_svg: ^2.2.0
|
flutter_svg: ^2.2.0
|
||||||
lottie: ^3.3.1
|
lottie: ^3.3.1
|
||||||
|
flutter_staggered_animations: ^1.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user