25-8-9-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 = 7
|
versionCode = 8
|
||||||
versionName = '1.0.7'
|
versionName = '1.0.8'
|
||||||
multiDexEnabled =true
|
multiDexEnabled =true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
|
|||||||
BIN
android/app/src/main/res/raw/app_icon.png
Executable file
BIN
android/app/src/main/res/raw/app_icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
BIN
android/app/src/main/res/raw/cancel.wav
Executable file
BIN
android/app/src/main/res/raw/cancel.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/ding.wav
Executable file
BIN
android/app/src/main/res/raw/ding.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/iphone_ringtone.wav
Executable file
BIN
android/app/src/main/res/raw/iphone_ringtone.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/order.wav
Executable file
BIN
android/app/src/main/res/raw/order.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/order1.wav
Executable file
BIN
android/app/src/main/res/raw/order1.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/promo.wav
Executable file
BIN
android/app/src/main/res/raw/promo.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/start.wav
Executable file
BIN
android/app/src/main/res/raw/start.wav
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/tone1.mp3
Executable file
BIN
android/app/src/main/res/raw/tone1.mp3
Executable file
Binary file not shown.
BIN
android/app/src/main/res/raw/tone2.wav
Executable file
BIN
android/app/src/main/res/raw/tone2.wav
Executable file
Binary file not shown.
@@ -79,12 +79,14 @@ class LoginDriverController extends GetxController {
|
|||||||
var res = await CRUD().get(
|
var res = await CRUD().get(
|
||||||
link: AppLink.getTesterApp,
|
link: AppLink.getTesterApp,
|
||||||
payload: {'appPlatform': AppInformation.appName});
|
payload: {'appPlatform': AppInformation.appName});
|
||||||
|
Log.print('res: ${res}');
|
||||||
if (res != 'failure') {
|
if (res != 'failure') {
|
||||||
var d = jsonDecode(res);
|
var d = jsonDecode(res);
|
||||||
isTest = d['message'][0]['isTest'];
|
isTest = d['message'][0]['isTest'];
|
||||||
|
Log.print('isTest: ${isTest}');
|
||||||
box.write(BoxName.isTest, isTest);
|
box.write(BoxName.isTest, isTest);
|
||||||
|
|
||||||
Log.print('isTest: ${box.read(BoxName.isTest)}');
|
// Log.print('isTest: ${box.read(BoxName.isTest)}');
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
isTest = 0;
|
isTest = 0;
|
||||||
@@ -164,22 +166,22 @@ class LoginDriverController extends GetxController {
|
|||||||
|
|
||||||
getJWT() async {
|
getJWT() async {
|
||||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||||
Log.print(
|
// Log.print(
|
||||||
'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
|
// 'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
|
||||||
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
|
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
|
||||||
var payload = {
|
var payload = {
|
||||||
'id': box.read(BoxName.driverID) ?? AK.newId,
|
'id': box.read(BoxName.driverID) ?? AK.newId,
|
||||||
'password': AK.passnpassenger,
|
'password': AK.passnpassenger,
|
||||||
'aud': '${AK.allowed}$dev',
|
'aud': '${AK.allowed}$dev',
|
||||||
};
|
};
|
||||||
Log.print('payload: ${payload}');
|
// Log.print('payload: ${payload}');
|
||||||
|
|
||||||
var response0 = await http.post(
|
var response0 = await http.post(
|
||||||
Uri.parse(AppLink.loginFirstTimeDriver),
|
Uri.parse(AppLink.loginFirstTimeDriver),
|
||||||
body: payload,
|
body: payload,
|
||||||
);
|
);
|
||||||
Log.print('response0: ${response0.body}');
|
// Log.print('response0: ${response0.body}');
|
||||||
Log.print('request: ${response0.request}');
|
// Log.print('request: ${response0.request}');
|
||||||
if (response0.statusCode == 200) {
|
if (response0.statusCode == 200) {
|
||||||
final decodedResponse1 = jsonDecode(response0.body);
|
final decodedResponse1 = jsonDecode(response0.body);
|
||||||
// Log.print('decodedResponse1: ${decodedResponse1}');
|
// Log.print('decodedResponse1: ${decodedResponse1}');
|
||||||
@@ -207,13 +209,13 @@ class LoginDriverController extends GetxController {
|
|||||||
'password': box.read(BoxName.emailDriver),
|
'password': box.read(BoxName.emailDriver),
|
||||||
'aud': '${AK.allowed}$dev',
|
'aud': '${AK.allowed}$dev',
|
||||||
};
|
};
|
||||||
print(payload);
|
// print(payload);
|
||||||
var response1 = await http.post(
|
var response1 = await http.post(
|
||||||
Uri.parse(AppLink.loginJwtDriver),
|
Uri.parse(AppLink.loginJwtDriver),
|
||||||
body: payload,
|
body: payload,
|
||||||
);
|
);
|
||||||
print(response1.request);
|
// print(response1.request);
|
||||||
print(response1.body);
|
// print(response1.body);
|
||||||
|
|
||||||
if (response1.statusCode == 200) {
|
if (response1.statusCode == 200) {
|
||||||
final decodedResponse1 = jsonDecode(response1.body);
|
final decodedResponse1 = jsonDecode(response1.body);
|
||||||
@@ -265,7 +267,7 @@ class LoginDriverController extends GetxController {
|
|||||||
isloading = true;
|
isloading = true;
|
||||||
update();
|
update();
|
||||||
await SecurityHelper.performSecurityChecks();
|
await SecurityHelper.performSecurityChecks();
|
||||||
Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
// Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||||
|
|
||||||
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
||||||
'email': email ?? 'yet',
|
'email': email ?? 'yet',
|
||||||
@@ -329,8 +331,8 @@ class LoginDriverController extends GetxController {
|
|||||||
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
|
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
|
||||||
await storage.write(
|
await storage.write(
|
||||||
key: BoxName.fingerPrint, value: fingerPrint.toString());
|
key: BoxName.fingerPrint, value: fingerPrint.toString());
|
||||||
print(jsonDecode(token)['data'][0]['token'].toString());
|
// print(jsonDecode(token)['data'][0]['token'].toString());
|
||||||
print(box.read(BoxName.tokenDriver).toString());
|
// print(box.read(BoxName.tokenDriver).toString());
|
||||||
if (email == '962798583052@intaleqapp.com') {
|
if (email == '962798583052@intaleqapp.com') {
|
||||||
} else {
|
} else {
|
||||||
if (token != 'failure') {
|
if (token != 'failure') {
|
||||||
@@ -379,7 +381,7 @@ class LoginDriverController extends GetxController {
|
|||||||
isloading = true;
|
isloading = true;
|
||||||
update();
|
update();
|
||||||
await SecurityHelper.performSecurityChecks();
|
await SecurityHelper.performSecurityChecks();
|
||||||
Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
// Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||||
|
|
||||||
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
||||||
'email': email ?? 'yet',
|
'email': email ?? 'yet',
|
||||||
|
|||||||
365
lib/controller/auth/syria/registration_controller.dart
Normal file
365
lib/controller/auth/syria/registration_controller.dart
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:image_cropper/image_cropper.dart';
|
||||||
|
import 'package:image/image.dart' as img;
|
||||||
|
import '../../../constant/box_name.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
// --- Final Submission ---
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:mime/mime.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import '../../../constant/colors.dart';
|
||||||
|
import '../../../constant/info.dart';
|
||||||
|
import '../../../main.dart';
|
||||||
|
import '../../functions/crud.dart';
|
||||||
|
import '../../functions/encrypt_decrypt.dart';
|
||||||
|
|
||||||
|
// You can create a simple enum to manage image types
|
||||||
|
enum ImageType {
|
||||||
|
driverLicenseFront,
|
||||||
|
driverLicenseBack,
|
||||||
|
carLicenseFront,
|
||||||
|
carLicenseBack,
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegistrationController extends GetxController {
|
||||||
|
// Page Controller for managing steps
|
||||||
|
late PageController pageController;
|
||||||
|
var currentPage =
|
||||||
|
0.obs; // Use .obs for reactive updates on the step indicator
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
var isLoading = false.obs;
|
||||||
|
String? colorHex; // سيُملى من الدروب داون
|
||||||
|
// Form Keys for validation
|
||||||
|
final driverInfoFormKey = GlobalKey<FormState>();
|
||||||
|
final carInfoFormKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
// STEP 1: Driver Information Controllers
|
||||||
|
final firstNameController = TextEditingController();
|
||||||
|
final lastNameController = TextEditingController();
|
||||||
|
final nationalIdController = TextEditingController();
|
||||||
|
final phoneController = TextEditingController(); // You can pre-fill this
|
||||||
|
final driverLicenseExpiryController = TextEditingController();
|
||||||
|
DateTime? driverLicenseExpiryDate;
|
||||||
|
|
||||||
|
// STEP 2: Car Information Controllers
|
||||||
|
final carPlateController = TextEditingController();
|
||||||
|
final carMakeController = TextEditingController();
|
||||||
|
final carModelController = TextEditingController();
|
||||||
|
final carYearController = TextEditingController();
|
||||||
|
final carColorController = TextEditingController();
|
||||||
|
final carVinController = TextEditingController(); // Chassis number
|
||||||
|
final carRegistrationExpiryController = TextEditingController();
|
||||||
|
DateTime? carRegistrationExpiryDate;
|
||||||
|
|
||||||
|
// STEP 3: Document Uploads
|
||||||
|
File? driverLicenseFrontImage;
|
||||||
|
File? driverLicenseBackImage;
|
||||||
|
File? carLicenseFrontImage;
|
||||||
|
File? carLicenseBackImage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
pageController = PageController();
|
||||||
|
// Pre-fill phone number if it exists in storage
|
||||||
|
// phoneController.text = box.read(BoxName.phoneDriver) ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
pageController.dispose();
|
||||||
|
// Dispose all other text controllers
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Page Navigation ---
|
||||||
|
void goToNextStep() {
|
||||||
|
bool isValid = false;
|
||||||
|
if (currentPage.value == 0) {
|
||||||
|
// Validate Step 1
|
||||||
|
isValid = driverInfoFormKey.currentState!.validate();
|
||||||
|
if (isValid) {
|
||||||
|
// Optional: Check if license is expired
|
||||||
|
if (driverLicenseExpiryDate != null &&
|
||||||
|
driverLicenseExpiryDate!.isBefore(DateTime.now())) {
|
||||||
|
Get.snackbar('Expired License', 'Your driver’s license has expired.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white);
|
||||||
|
return; // Stop progression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentPage.value == 1) {
|
||||||
|
// Validate Step 2
|
||||||
|
isValid = carInfoFormKey.currentState!.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
pageController.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToPreviousStep() {
|
||||||
|
pageController.previousPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Image Picking ---
|
||||||
|
Future<void> pickImage(ImageType type) async {
|
||||||
|
try {
|
||||||
|
final picker = ImagePicker();
|
||||||
|
final picked = await picker.pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
imageQuality: 95, // جودة أولية من الكاميرا
|
||||||
|
maxWidth: 3000, // نسمح بصورة كبيرة ثم نصغّر نحن
|
||||||
|
);
|
||||||
|
if (picked == null) return;
|
||||||
|
|
||||||
|
// قصّ الصورة
|
||||||
|
final cropped = await ImageCropper().cropImage(
|
||||||
|
sourcePath: picked.path,
|
||||||
|
uiSettings: [
|
||||||
|
AndroidUiSettings(
|
||||||
|
toolbarTitle: 'Cropper'.tr,
|
||||||
|
toolbarColor: AppColor.accentColor,
|
||||||
|
toolbarWidgetColor: AppColor.redColor,
|
||||||
|
initAspectRatio: CropAspectRatioPreset.original,
|
||||||
|
lockAspectRatio: false,
|
||||||
|
),
|
||||||
|
IOSUiSettings(title: 'Cropper'.tr),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
if (cropped == null) return; // المستخدم ألغى
|
||||||
|
|
||||||
|
// قراءة bytes + التصحيح حسب EXIF ثم التصغير
|
||||||
|
final rawBytes = await File(cropped.path).readAsBytes();
|
||||||
|
final decoded = img.decodeImage(rawBytes);
|
||||||
|
if (decoded == null) throw Exception('Decode image failed');
|
||||||
|
|
||||||
|
// تصحيح اتجاه الصورة (EXIF)
|
||||||
|
final fixed = img.bakeOrientation(decoded);
|
||||||
|
|
||||||
|
// تصغير لعرض 800px (عدّل عند الحاجة)
|
||||||
|
final resized = img.copyResize(fixed, width: 800);
|
||||||
|
|
||||||
|
// حفظ مؤقت بصيغة JPG
|
||||||
|
final tmpDir = await getTemporaryDirectory();
|
||||||
|
final outPath =
|
||||||
|
'${tmpDir.path}/doc_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
||||||
|
final outFile = File(outPath);
|
||||||
|
await outFile.writeAsBytes(img.encodeJpg(resized, quality: 85));
|
||||||
|
|
||||||
|
// عيّن الملف في المتغير الصحيح حسب النوع
|
||||||
|
if (outFile != null) {
|
||||||
|
switch (type) {
|
||||||
|
case ImageType.driverLicenseFront:
|
||||||
|
driverLicenseFrontImage = File(outFile.path);
|
||||||
|
break;
|
||||||
|
case ImageType.driverLicenseBack:
|
||||||
|
driverLicenseBackImage = File(outFile.path);
|
||||||
|
break;
|
||||||
|
case ImageType.carLicenseFront:
|
||||||
|
carLicenseFrontImage = File(outFile.path);
|
||||||
|
break;
|
||||||
|
case ImageType.carLicenseBack:
|
||||||
|
carLicenseBackImage = File(outFile.path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
update(); // Use update() to refresh the GetBuilder UI
|
||||||
|
}
|
||||||
|
|
||||||
|
update(); // لتحديث الـ UI
|
||||||
|
|
||||||
|
// // الإرسال للذكاء الاصطناعي
|
||||||
|
// await sendToAI(type, imageFile: outFile);
|
||||||
|
} catch (e) {
|
||||||
|
Get.snackbar('Error'.tr, '${'An unexpected error occurred:'.tr} $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ثابت: 20 لون سيارة شائع
|
||||||
|
static const List<Map<String, String>> kCarColorOptions = [
|
||||||
|
{'key': 'color.white', 'hex': '#FFFFFF'},
|
||||||
|
{'key': 'color.black', 'hex': '#000000'},
|
||||||
|
{'key': 'color.silver', 'hex': '#C0C0C0'},
|
||||||
|
{'key': 'color.gray', 'hex': '#808080'},
|
||||||
|
{'key': 'color.gunmetal', 'hex': '#2A3439'},
|
||||||
|
{'key': 'color.red', 'hex': '#C62828'},
|
||||||
|
{'key': 'color.blue', 'hex': '#1565C0'},
|
||||||
|
{'key': 'color.navy', 'hex': '#0D47A1'},
|
||||||
|
{'key': 'color.green', 'hex': '#2E7D32'},
|
||||||
|
{'key': 'color.darkGreen', 'hex': '#1B5E20'},
|
||||||
|
{'key': 'color.beige', 'hex': '#D7CCC8'},
|
||||||
|
{'key': 'color.brown', 'hex': '#5D4037'},
|
||||||
|
{'key': 'color.maroon', 'hex': '#800000'},
|
||||||
|
{'key': 'color.burgundy', 'hex': '#800020'},
|
||||||
|
{'key': 'color.yellow', 'hex': '#F9A825'},
|
||||||
|
{'key': 'color.orange', 'hex': '#EF6C00'},
|
||||||
|
{'key': 'color.gold', 'hex': '#D4AF37'},
|
||||||
|
{'key': 'color.bronze', 'hex': '#CD7F32'},
|
||||||
|
{'key': 'color.champagne', 'hex': '#EFE1C6'},
|
||||||
|
{'key': 'color.purple', 'hex': '#6A1B9A'},
|
||||||
|
];
|
||||||
|
|
||||||
|
Color hexToColor(String hex) {
|
||||||
|
var v = hex.replaceAll('#', '');
|
||||||
|
if (v.length == 6) v = 'FF$v';
|
||||||
|
return Color(int.parse(v, radix: 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> submitRegistration() async {
|
||||||
|
// 1) تحقق من الصور
|
||||||
|
if (driverLicenseFrontImage == null ||
|
||||||
|
driverLicenseBackImage == null ||
|
||||||
|
carLicenseFrontImage == null ||
|
||||||
|
carLicenseBackImage == null) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Missing Documents'.tr, 'Please upload all 4 required documents.'.tr,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
final uri = Uri.parse(
|
||||||
|
'https://intaleq.xyz/intaleq/auth/syria/driver/register_driver_and_car.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
final client = http.Client();
|
||||||
|
try {
|
||||||
|
final req = http.MultipartRequest('POST', uri);
|
||||||
|
|
||||||
|
// مهم: لا تضع Content-Type يدويًا، الـ MultipartRequest يتكفّل فيه ببناء boundary.
|
||||||
|
final headers = {
|
||||||
|
'Authorization':
|
||||||
|
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||||
|
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||||
|
};
|
||||||
|
// 2) الحقول النصية
|
||||||
|
final fields = <String, String>{};
|
||||||
|
|
||||||
|
// --- Driver Data ---
|
||||||
|
_addField(fields, 'id', box.read(BoxName.driverID)?.toString());
|
||||||
|
_addField(fields, 'first_name', firstNameController.text);
|
||||||
|
_addField(fields, 'last_name', lastNameController.text);
|
||||||
|
_addField(fields, 'phone', box.read(BoxName.phoneDriver) ?? '');
|
||||||
|
_addField(fields, 'national_number', nationalIdController.text);
|
||||||
|
_addField(fields, 'expiry_date', driverLicenseExpiryController.text);
|
||||||
|
_addField(
|
||||||
|
fields, 'password', 'generate_your_password_here'); // عدّل حسب منطقك
|
||||||
|
_addField(fields, 'status', 'yet');
|
||||||
|
_addField(fields, 'email',
|
||||||
|
'Not specified'); // سكربت السيرفر سيحوّلها null ويبني ايميل افتراضي
|
||||||
|
_addField(fields, 'gender', 'Male');
|
||||||
|
|
||||||
|
// --- Car Data (مطابقة لما يتوقّعه السكربت) ---
|
||||||
|
_addField(fields, 'vin', 'carVinController.text);');
|
||||||
|
_addField(fields, 'car_plate', carPlateController.text);
|
||||||
|
_addField(fields, 'make', carMakeController.text);
|
||||||
|
_addField(fields, 'model', carModelController.text);
|
||||||
|
_addField(fields, 'year', carYearController.text);
|
||||||
|
_addField(fields, 'expiration_date', 'carRegistrationExpiryController');
|
||||||
|
_addField(fields, 'color', carColorController.text);
|
||||||
|
_addField(fields, 'fuel', 'Gasoline'); // أو حسب اختيارك
|
||||||
|
_addField(fields, 'color_hex', colorHex); // مهم
|
||||||
|
// لو عندك حقول إضافية مطلوبة بالسكربت (مالك المركبة / الكود اللوني / الوقود) مرّرها:
|
||||||
|
_addField(fields, 'owner',
|
||||||
|
firstNameController.text + ' ' + lastNameController.text);
|
||||||
|
// if (colorHex != null) _addField(fields, 'color_hex', colorHex);
|
||||||
|
// if (fuelType != null) _addField(fields, 'fuel', fuelType);
|
||||||
|
req.headers.addAll(headers);
|
||||||
|
req.fields.addAll(fields);
|
||||||
|
|
||||||
|
// 3) الملفات (4 صور) — مفاتيحها مطابقة للسكربت
|
||||||
|
Future<void> addFile(String field, File file) async {
|
||||||
|
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||||
|
final parts = mime.split('/');
|
||||||
|
final mediaType = MediaType(parts.first, parts.last);
|
||||||
|
req.files.add(
|
||||||
|
await http.MultipartFile.fromPath(
|
||||||
|
field,
|
||||||
|
file.path,
|
||||||
|
filename: p.basename(file.path),
|
||||||
|
contentType: mediaType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await addFile('driver_license_front', driverLicenseFrontImage!);
|
||||||
|
await addFile('driver_license_back', driverLicenseBackImage!);
|
||||||
|
await addFile('car_license_front', carLicenseFrontImage!);
|
||||||
|
await addFile('car_license_back', carLicenseBackImage!);
|
||||||
|
|
||||||
|
// (اختياري) هيدر للقبول بـ JSON
|
||||||
|
|
||||||
|
// 4) الإرسال
|
||||||
|
final streamed =
|
||||||
|
await client.send(req).timeout(const Duration(seconds: 60));
|
||||||
|
final resp = await http.Response.fromStream(streamed);
|
||||||
|
|
||||||
|
// 5) فحص النتيجة
|
||||||
|
Map<String, dynamic>? json;
|
||||||
|
try {
|
||||||
|
json = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (resp.statusCode == 200 &&
|
||||||
|
json != null &&
|
||||||
|
json['status'] == 'success') {
|
||||||
|
// ممكن يرجّع driverID, carRegID, documents
|
||||||
|
final driverID =
|
||||||
|
(json['data']?['driverID'] ?? json['driverID'])?.toString();
|
||||||
|
if (driverID != null && driverID.isNotEmpty) {
|
||||||
|
box.write(BoxName.driverID, driverID);
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.snackbar('Success'.tr, 'Registration completed successfully!'.tr,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white);
|
||||||
|
|
||||||
|
// TODO: انتقل للصفحة التالية أو حدّث الحالة…
|
||||||
|
} else {
|
||||||
|
final msg =
|
||||||
|
(json?['message'] ?? 'Registration failed. Please try again.')
|
||||||
|
.toString();
|
||||||
|
Get.snackbar('Error'.tr, msg,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Get.snackbar('Error'.tr, '${'An unexpected error occurred:'.tr} $e',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
void _addField(Map<String, String> fields, String key, String? value) {
|
||||||
|
if (value != null && value.toString().trim().isNotEmpty) {
|
||||||
|
fields[key] = value.toString().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
lib/controller/functions/battery_status.dart
Normal file
39
lib/controller/functions/battery_status.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:battery_plus/battery_plus.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class BatteryNotifier {
|
||||||
|
static final Battery _battery = Battery();
|
||||||
|
static int? _lastNotifiedLevel;
|
||||||
|
|
||||||
|
static Future<void> checkBatteryAndNotify() async {
|
||||||
|
try {
|
||||||
|
final int batteryLevel = await _battery.batteryLevel;
|
||||||
|
|
||||||
|
// ✅ لا تكرر الإشعار إذا الفرق قليل
|
||||||
|
if (_lastNotifiedLevel != null &&
|
||||||
|
(batteryLevel >= _lastNotifiedLevel! - 2)) return;
|
||||||
|
|
||||||
|
if (batteryLevel <= 30) {
|
||||||
|
Color backgroundColor = Colors.yellow;
|
||||||
|
if (batteryLevel <= 20) {
|
||||||
|
backgroundColor = Colors.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.snackbar(
|
||||||
|
"⚠️ تنبيه البطارية", // العنوان
|
||||||
|
"مستوى البطارية: $batteryLevel٪", // النص
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 10), // مدة الظهور
|
||||||
|
margin: const EdgeInsets.all(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
_lastNotifiedLevel = batteryLevel;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Battery check error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,9 +43,9 @@ class CRUD {
|
|||||||
'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
|
'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
print(response.request);
|
// print(response.request);
|
||||||
Log.print('response.body: ${response.body}');
|
// Log.print('response.body: ${response.body}');
|
||||||
print(payload);
|
// print(payload);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var jsonData = jsonDecode(response.body);
|
var jsonData = jsonDecode(response.body);
|
||||||
if (jsonData['status'] == 'success') {
|
if (jsonData['status'] == 'success') {
|
||||||
@@ -94,9 +94,9 @@ class CRUD {
|
|||||||
'X-HMAC-Auth': hmac.toString(),
|
'X-HMAC-Auth': hmac.toString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Log.print('response.request: ${response.request}');
|
// Log.print('response.request: ${response.request}');
|
||||||
Log.print('response.body: ${response.body}');
|
// Log.print('response.body: ${response.body}');
|
||||||
print(payload);
|
// print(payload);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var jsonData = jsonDecode(response.body);
|
var jsonData = jsonDecode(response.body);
|
||||||
if (jsonData['status'] == 'success') {
|
if (jsonData['status'] == 'success') {
|
||||||
@@ -145,9 +145,9 @@ class CRUD {
|
|||||||
'X-HMAC-Auth': hmac.toString(),
|
'X-HMAC-Auth': hmac.toString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Log.print('response.request:${response.request}');
|
// Log.print('response.request:${response.request}');
|
||||||
Log.print('response.body: ${response.body}');
|
// Log.print('response.body: ${response.body}');
|
||||||
Log.print('payload:$payload');
|
// Log.print('payload:$payload');
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
try {
|
try {
|
||||||
var jsonData = jsonDecode(response.body);
|
var jsonData = jsonDecode(response.body);
|
||||||
@@ -203,9 +203,9 @@ class CRUD {
|
|||||||
// 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
|
// 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
print(response.request);
|
// print(response.request);
|
||||||
Log.print('response.body: ${response.body}');
|
// Log.print('response.body: ${response.body}');
|
||||||
print(payload);
|
// print(payload);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
try {
|
try {
|
||||||
var jsonData = jsonDecode(response.body);
|
var jsonData = jsonDecode(response.body);
|
||||||
|
|||||||
205
lib/controller/functions/device_analyzer.dart
Normal file
205
lib/controller/functions/device_analyzer.dart
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'performance_test.dart'; // Make sure this path is correct
|
||||||
|
|
||||||
|
/// Analyzes various device hardware and software aspects to generate a compatibility score.
|
||||||
|
/// This class provides a standardized output for the UI to consume easily.
|
||||||
|
class DeviceAnalyzer {
|
||||||
|
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
||||||
|
|
||||||
|
/// Reads the total RAM from the system's meminfo file.
|
||||||
|
/// Returns the value in Megabytes (MB).
|
||||||
|
Future<double> _readTotalRamMB() async {
|
||||||
|
try {
|
||||||
|
final file = File('/proc/meminfo');
|
||||||
|
if (!await file.exists()) return 0.0;
|
||||||
|
final lines = await file.readAsLines();
|
||||||
|
for (var line in lines) {
|
||||||
|
if (line.startsWith('MemTotal')) {
|
||||||
|
// Extracts the numeric value from the line.
|
||||||
|
final kb = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '0') ?? 0;
|
||||||
|
return kb / 1024.0; // Convert from Kilobytes to Megabytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Error reading total RAM: $e');
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the current RAM usage percentage from the system's meminfo file.
|
||||||
|
Future<double> _readUsedRamPercent() async {
|
||||||
|
try {
|
||||||
|
final file = File('/proc/meminfo');
|
||||||
|
if (!await file.exists()) return 0.0;
|
||||||
|
final lines = await file.readAsLines();
|
||||||
|
int? total, available;
|
||||||
|
for (var line in lines) {
|
||||||
|
if (line.startsWith('MemTotal')) {
|
||||||
|
total = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '');
|
||||||
|
} else if (line.startsWith('MemAvailable')) {
|
||||||
|
available = int.tryParse(RegExp(r'\d+').stringMatch(line) ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total != null && available != null && total > 0) {
|
||||||
|
final used = total - available;
|
||||||
|
return (used / total) * 100.0;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Error reading used RAM: $e');
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main analysis function that runs all checks.
|
||||||
|
Future<Map<String, dynamic>> analyzeDevice() async {
|
||||||
|
List<Map<String, dynamic>> details = [];
|
||||||
|
|
||||||
|
if (!Platform.isAndroid) {
|
||||||
|
return {
|
||||||
|
'score': 0,
|
||||||
|
'details': [
|
||||||
|
{
|
||||||
|
'label': 'النظام غير مدعوم',
|
||||||
|
'status': false,
|
||||||
|
'achieved_score': 0,
|
||||||
|
'max_score': 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
final info = await _deviceInfo.androidInfo;
|
||||||
|
final data = info.data;
|
||||||
|
final features = List<String>.from(data['systemFeatures'] ?? []);
|
||||||
|
|
||||||
|
// 1. Android Version (Max: 10 points)
|
||||||
|
final version =
|
||||||
|
int.tryParse(info.version.release?.split('.').first ?? '0') ?? 0;
|
||||||
|
final int androidScore = version >= 9 ? 10 : 0;
|
||||||
|
details.add({
|
||||||
|
'label': 'إصدار أندرويد ${info.version.release}',
|
||||||
|
'status': androidScore > 0,
|
||||||
|
'achieved_score': androidScore,
|
||||||
|
'max_score': 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Total RAM (Max: 10 points)
|
||||||
|
final totalRam = await _readTotalRamMB();
|
||||||
|
int ramScore;
|
||||||
|
if (totalRam >= 8000) {
|
||||||
|
ramScore = 10;
|
||||||
|
} else if (totalRam >= 4000) {
|
||||||
|
ramScore = 5;
|
||||||
|
} else if (totalRam >= 3000) {
|
||||||
|
ramScore = 3;
|
||||||
|
} else {
|
||||||
|
ramScore = 0;
|
||||||
|
}
|
||||||
|
details.add({
|
||||||
|
'label': 'إجمالي الرام ${totalRam.toStringAsFixed(0)} ميجابايت',
|
||||||
|
'status': ramScore >= 5,
|
||||||
|
'achieved_score': ramScore,
|
||||||
|
'max_score': 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. CPU Cores (Max: 10 points)
|
||||||
|
final cores = Platform.numberOfProcessors;
|
||||||
|
int coreScore = cores >= 6 ? 10 : (cores >= 4 ? 5 : 0);
|
||||||
|
details.add({
|
||||||
|
'label': 'أنوية المعالج ($cores)',
|
||||||
|
'status': coreScore >= 5,
|
||||||
|
'achieved_score': coreScore,
|
||||||
|
'max_score': 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Free Storage (Max: 5 points)
|
||||||
|
final freeBytes = data['freeDiskSize'] ?? 0;
|
||||||
|
final freeGB = freeBytes / (1024 * 1024 * 1024);
|
||||||
|
int storeScore = freeGB >= 5 ? 5 : (freeGB >= 2 ? 3 : 0);
|
||||||
|
details.add({
|
||||||
|
'label': 'المساحة الحرة ${freeGB.toStringAsFixed(1)} جيجابايت',
|
||||||
|
'status': storeScore >= 3,
|
||||||
|
'achieved_score': storeScore,
|
||||||
|
'max_score': 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. GPS + Gyroscope Sensors (Max: 10 points)
|
||||||
|
bool okSensors = features.contains('android.hardware.location.gps') &&
|
||||||
|
features.contains('android.hardware.sensor.gyroscope');
|
||||||
|
final int sensorScore = okSensors ? 10 : 0;
|
||||||
|
details.add({
|
||||||
|
'label': 'حساسات GPS و Gyroscope',
|
||||||
|
'status': okSensors,
|
||||||
|
'achieved_score': sensorScore,
|
||||||
|
'max_score': 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. Storage Write Speed (Max: 20 points)
|
||||||
|
final writeSpeed = await PerformanceTester.testStorageWriteSpeed();
|
||||||
|
int writeScore;
|
||||||
|
if (writeSpeed >= 30) {
|
||||||
|
writeScore = 20;
|
||||||
|
} else if (writeSpeed >= 15) {
|
||||||
|
writeScore = 15;
|
||||||
|
} else if (writeSpeed >= 5) {
|
||||||
|
writeScore = 10;
|
||||||
|
} else {
|
||||||
|
writeScore = 5;
|
||||||
|
}
|
||||||
|
details.add({
|
||||||
|
'label': 'سرعة الكتابة (${writeSpeed.toStringAsFixed(1)} MB/s)',
|
||||||
|
'status': writeScore >= 10,
|
||||||
|
'achieved_score': writeScore,
|
||||||
|
'max_score': 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. CPU Compute Speed (Max: 20 points)
|
||||||
|
final cpuTime = await PerformanceTester.testCPUSpeed();
|
||||||
|
int cpuScore;
|
||||||
|
if (cpuTime <= 1.0) {
|
||||||
|
cpuScore = 20;
|
||||||
|
} else if (cpuTime <= 2.5) {
|
||||||
|
cpuScore = 15;
|
||||||
|
} else if (cpuTime <= 4.0) {
|
||||||
|
cpuScore = 10;
|
||||||
|
} else {
|
||||||
|
cpuScore = 5;
|
||||||
|
}
|
||||||
|
details.add({
|
||||||
|
'label': 'سرعة المعالجة (${cpuTime.toStringAsFixed(2)} ثانية)',
|
||||||
|
'status': cpuScore >= 10,
|
||||||
|
'achieved_score': cpuScore,
|
||||||
|
'max_score': 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 8. Memory Pressure (Max: 15 points)
|
||||||
|
final usedPercent = await _readUsedRamPercent();
|
||||||
|
int memScore;
|
||||||
|
if (usedPercent <= 60) {
|
||||||
|
memScore = 15;
|
||||||
|
} else if (usedPercent <= 80) {
|
||||||
|
memScore = 10;
|
||||||
|
} else if (usedPercent <= 90) {
|
||||||
|
memScore = 5;
|
||||||
|
} else {
|
||||||
|
memScore = 0;
|
||||||
|
}
|
||||||
|
details.add({
|
||||||
|
'label': 'استخدام الرام الحالي (${usedPercent.toStringAsFixed(0)}%)',
|
||||||
|
'status': memScore >= 10,
|
||||||
|
'achieved_score': memScore,
|
||||||
|
'max_score': 15,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate the final total score by summing up the achieved scores.
|
||||||
|
final totalScore = details.fold<int>(
|
||||||
|
0, (sum, item) => sum + (item['achieved_score'] as int));
|
||||||
|
|
||||||
|
return {
|
||||||
|
'score': totalScore.clamp(0, 100),
|
||||||
|
'details': details,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
|
||||||
|
|
||||||
import 'package:sefer_driver/constant/box_name.dart';
|
import 'package:sefer_driver/constant/box_name.dart';
|
||||||
import 'package:sefer_driver/constant/info.dart';
|
import 'package:sefer_driver/constant/info.dart';
|
||||||
@@ -15,7 +14,6 @@ import 'package:sefer_driver/controller/firebase/local_notification.dart';
|
|||||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||||
import 'package:sefer_driver/env/env.dart';
|
import 'package:sefer_driver/env/env.dart';
|
||||||
import 'package:sefer_driver/main.dart';
|
import 'package:sefer_driver/main.dart';
|
||||||
import 'package:sefer_driver/views/home/Captin/home_captain/home_captin.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -565,7 +563,7 @@ class AI extends GetxController {
|
|||||||
|
|
||||||
Future<void> pickAndSendImage(String type) async {
|
Future<void> pickAndSendImage(String type) async {
|
||||||
final picker = ImagePicker();
|
final picker = ImagePicker();
|
||||||
final pickedImage = await picker.pickImage(source: ImageSource.gallery);
|
final pickedImage = await picker.pickImage(source: ImageSource.camera);
|
||||||
if (pickedImage != null) {
|
if (pickedImage != null) {
|
||||||
image = File(pickedImage.path);
|
image = File(pickedImage.path);
|
||||||
// Crop the image
|
// Crop the image
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../main.dart';
|
|||||||
import '../../print.dart';
|
import '../../print.dart';
|
||||||
import '../home/captin/home_captain_controller.dart';
|
import '../home/captin/home_captain_controller.dart';
|
||||||
import '../home/payment/captain_wallet_controller.dart';
|
import '../home/payment/captain_wallet_controller.dart';
|
||||||
|
import 'battery_status.dart';
|
||||||
import 'crud.dart';
|
import 'crud.dart';
|
||||||
import 'encrypt_decrypt.dart';
|
import 'encrypt_decrypt.dart';
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ class LocationController extends GetxController {
|
|||||||
_locationTimer =
|
_locationTimer =
|
||||||
Timer.periodic(const Duration(seconds: 5), (timer) async {
|
Timer.periodic(const Duration(seconds: 5), (timer) async {
|
||||||
try {
|
try {
|
||||||
|
await BatteryNotifier.checkBatteryAndNotify();
|
||||||
totalPoints =
|
totalPoints =
|
||||||
Get.find<CaptainWalletController>().totalPoints.toString();
|
Get.find<CaptainWalletController>().totalPoints.toString();
|
||||||
isActive = Get.find<HomeCaptainController>().isActive;
|
isActive = Get.find<HomeCaptainController>().isActive;
|
||||||
|
|||||||
43
lib/controller/functions/performance_test.dart
Normal file
43
lib/controller/functions/performance_test.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class PerformanceTester {
|
||||||
|
/// ✅ فحص سرعة الكتابة إلى التخزين (Storage Write Speed) بوحدة MB/s
|
||||||
|
static Future<double> testStorageWriteSpeed() async {
|
||||||
|
try {
|
||||||
|
final tempDir = Directory.systemTemp;
|
||||||
|
final testFile = File('${tempDir.path}/speed_test.txt');
|
||||||
|
final data = List<int>.filled(1024 * 1024 * 5, 0); // 5MB
|
||||||
|
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
await testFile.writeAsBytes(data, flush: true);
|
||||||
|
stopwatch.stop();
|
||||||
|
|
||||||
|
await testFile.delete();
|
||||||
|
|
||||||
|
double seconds = stopwatch.elapsedMilliseconds / 1000;
|
||||||
|
if (seconds == 0) seconds = 0.001;
|
||||||
|
|
||||||
|
final speed = 5 / seconds;
|
||||||
|
return double.parse(speed.toStringAsFixed(2));
|
||||||
|
} catch (e) {
|
||||||
|
print("❌ Storage write error: $e");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ✅ فحص سرعة المعالج (CPU Compute Speed) بوحدة الثواني
|
||||||
|
static Future<double> testCPUSpeed() async {
|
||||||
|
try {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
double x = 0;
|
||||||
|
for (int i = 0; i < 100000000; i++) {
|
||||||
|
x += i * 0.000001;
|
||||||
|
}
|
||||||
|
stopwatch.stop();
|
||||||
|
return stopwatch.elapsedMilliseconds / 1000.0;
|
||||||
|
} catch (e) {
|
||||||
|
print("❌ CPU compute error: $e");
|
||||||
|
return 999.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:math' as 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';
|
||||||
@@ -399,6 +400,9 @@ class MapDriverController extends GetxController {
|
|||||||
remainingTimeInPassengerLocatioWait = 0;
|
remainingTimeInPassengerLocatioWait = 0;
|
||||||
timeWaitingPassenger = 0;
|
timeWaitingPassenger = 0;
|
||||||
box.write(BoxName.statusDriverLocation, 'on');
|
box.write(BoxName.statusDriverLocation, 'on');
|
||||||
|
|
||||||
|
// // ابدأ التتبع الحيّ لخطوات الملاحة
|
||||||
|
await startListeningStepNavigation();
|
||||||
// box.write(BoxName.rideStatus, 'Begin'); //
|
// box.write(BoxName.rideStatus, 'Begin'); //
|
||||||
// todo ride details
|
// todo ride details
|
||||||
// Get.find<HomeCaptainController>().changeToAppliedRide('Begin');
|
// Get.find<HomeCaptainController>().changeToAppliedRide('Begin');
|
||||||
@@ -615,7 +619,7 @@ class MapDriverController extends GetxController {
|
|||||||
// originalDistanceM - distanceToDestination;
|
// originalDistanceM - distanceToDestination;
|
||||||
|
|
||||||
// 3. عتبة ثلث المسافة
|
// 3. عتبة ثلث المسافة
|
||||||
final oneThirdDistanceM = originalDistanceM / 3;
|
final oneThirdDistanceM = originalDistanceM / 4;
|
||||||
|
|
||||||
// Logging للتتبع
|
// Logging للتتبع
|
||||||
Log.print('originalDistanceM: $originalDistanceM');
|
Log.print('originalDistanceM: $originalDistanceM');
|
||||||
@@ -624,7 +628,7 @@ class MapDriverController extends GetxController {
|
|||||||
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
|
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
|
||||||
|
|
||||||
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
|
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
|
||||||
if (movedDistanceM > oneThirdDistanceM * 2) {
|
if (movedDistanceM > oneThirdDistanceM) {
|
||||||
MyDialog().getDialog(
|
MyDialog().getDialog(
|
||||||
'Are you sure to exit ride?'.tr,
|
'Are you sure to exit ride?'.tr,
|
||||||
'',
|
'',
|
||||||
@@ -1321,6 +1325,12 @@ class MapDriverController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<LatLngBounds> stepBounds = [];
|
||||||
|
List<LatLng> stepEndPoints = [];
|
||||||
|
List<String> stepInstructions = [];
|
||||||
|
StreamSubscription<Position>? _posSub;
|
||||||
|
DateTime _lastCameraUpdateTs = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
|
|
||||||
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}');
|
||||||
@@ -1329,56 +1339,251 @@ class MapDriverController extends GetxController {
|
|||||||
dataDestination = response['routes'][0]['legs'];
|
dataDestination = response['routes'][0]['legs'];
|
||||||
final points =
|
final points =
|
||||||
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
|
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
|
||||||
|
|
||||||
|
polylineCoordinatesDestination.clear();
|
||||||
for (int i = 0; i < points.length; i++) {
|
for (int i = 0; i < points.length; i++) {
|
||||||
double lat = points[i][0].toDouble();
|
double lat = points[i][0].toDouble();
|
||||||
double lng = points[i][1].toDouble();
|
double lng = points[i][1].toDouble();
|
||||||
polylineCoordinatesDestination.add(LatLng(lat, lng));
|
polylineCoordinatesDestination.add(LatLng(lat, lng));
|
||||||
}
|
}
|
||||||
// استخراج الخطوات
|
|
||||||
|
// الخطوات من أول leg
|
||||||
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
|
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
|
||||||
Log.print('routeSteps: ${routeSteps}');
|
|
||||||
|
// حضّر بيانات كل step
|
||||||
|
_prepareStepData(routeSteps);
|
||||||
|
|
||||||
currentStepIndex = 0;
|
currentStepIndex = 0;
|
||||||
if (routeSteps.isNotEmpty) {
|
if (stepInstructions.isNotEmpty) {
|
||||||
currentInstruction =
|
currentInstruction = _parseInstruction(stepInstructions[0]);
|
||||||
_parseInstruction(routeSteps[0]['html_instructions']);
|
Log.print('currentInstruction: $currentInstruction');
|
||||||
Log.print('currentInstruction: ${currentInstruction}');
|
|
||||||
Get.isRegistered<TextToSpeechController>()
|
Get.isRegistered<TextToSpeechController>()
|
||||||
? Get.find<TextToSpeechController>().speakText(currentInstruction)
|
? Get.find<TextToSpeechController>().speakText(currentInstruction)
|
||||||
: Get.put(TextToSpeechController()).speakText(currentInstruction);
|
: Get.put(TextToSpeechController()).speakText(currentInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ارسم الـ polyline الرئيسي (مثل ما لديك)
|
||||||
|
final summary = response["routes"][0]["summary"];
|
||||||
|
final polyline = Polyline(
|
||||||
|
polylineId: PolylineId(summary.isEmpty ? 'route' : summary),
|
||||||
|
points: polylineCoordinatesDestination,
|
||||||
|
width: 10,
|
||||||
|
color: AppColor.redColor,
|
||||||
|
);
|
||||||
|
polyLinesDestination.add(polyline);
|
||||||
|
|
||||||
|
// fit للخريطة على أول step
|
||||||
|
if (stepBounds.isNotEmpty) {
|
||||||
|
_fitToBounds(stepBounds[0]);
|
||||||
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
// دالة مساعدة لتنظيف التعليمات
|
// // ابدأ التتبع الحيّ لخطوات الملاحة
|
||||||
|
// await startListeningStepNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
if (polyLinesDestination.isNotEmpty) {
|
// ————————————————————————————————————————————————————————————————
|
||||||
// clearPolyline();
|
// يُنادى عند كل تحديث للموقع
|
||||||
var polyline = Polyline(
|
void _onLocationTick(LatLng pos) async {
|
||||||
polylineId: PolylineId(response["routes"][0]["summary"]),
|
if (routeSteps.isEmpty || currentStepIndex >= stepBounds.length) return;
|
||||||
points: polylineCoordinatesDestination,
|
|
||||||
width: 10,
|
|
||||||
color: AppColor.redColor,
|
|
||||||
);
|
|
||||||
polyLinesDestination.add(polyline);
|
|
||||||
// rideConfirm = false;
|
|
||||||
update();
|
|
||||||
} else {
|
|
||||||
var polyline = Polyline(
|
|
||||||
polylineId: PolylineId(response["routes"][0]["summary"]),
|
|
||||||
points: polylineCoordinatesDestination,
|
|
||||||
width: 10,
|
|
||||||
color: AppColor.redColor,
|
|
||||||
);
|
|
||||||
// final dataBounds = response["routes"][0]["bounds"];
|
|
||||||
|
|
||||||
// updateCameraFromBoundsAfterGetMap(dataBounds);
|
// إذا تجاوزت نهاية الـ step الحالية بمسافة صغيرة -> انتقل
|
||||||
// polyLinesDestination.add(polyline);
|
final double dToEnd = _distanceMeters(pos, stepEndPoints[currentStepIndex]);
|
||||||
// rideConfirm = false;
|
final bool nearEnd = dToEnd <= 25; // عتبة 25م (عدّلها حسب حاجتك)
|
||||||
// Define the northeast and southwest coordinates
|
|
||||||
|
|
||||||
update();
|
// إذا دخلت نطاق الـ step التالية -> انتقل
|
||||||
|
bool insideNext = false;
|
||||||
|
if (currentStepIndex < stepBounds.length - 1) {
|
||||||
|
insideNext = _contains(stepBounds[currentStepIndex + 1], pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearEnd || insideNext) {
|
||||||
|
_advanceStep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// تحديث الكاميرا بشكل خفيف أثناء الحركة (كل 2 ثانية مثلاً)
|
||||||
|
final now = DateTime.now();
|
||||||
|
if (now.difference(_lastCameraUpdateTs).inMilliseconds > 2000) {
|
||||||
|
_lastCameraUpdateTs = now;
|
||||||
|
// ركّز على bounds الحالية مع الحفاظ على تتبّع عام
|
||||||
|
_fitToBounds(stepBounds[currentStepIndex], padding: 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LatLngBounds _boundsFromPoints(List<LatLng> pts) {
|
||||||
|
double? minLat, maxLat, minLng, maxLng;
|
||||||
|
for (final p in pts) {
|
||||||
|
minLat = (minLat == null) ? p.latitude : math.min(minLat, p.latitude);
|
||||||
|
maxLat = (maxLat == null) ? p.latitude : math.max(maxLat, p.latitude);
|
||||||
|
minLng = (minLng == null) ? p.longitude : math.min(minLng, p.longitude);
|
||||||
|
maxLng = (maxLng == null) ? p.longitude : math.max(maxLng, p.longitude);
|
||||||
|
}
|
||||||
|
return LatLngBounds(
|
||||||
|
southwest: LatLng(minLat ?? 0, minLng ?? 0),
|
||||||
|
northeast: LatLng(maxLat ?? 0, maxLng ?? 0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _contains(LatLngBounds b, LatLng p) {
|
||||||
|
final south = math.min(b.southwest.latitude, b.northeast.latitude);
|
||||||
|
final north = math.max(b.southwest.latitude, b.northeast.latitude);
|
||||||
|
final west = math.min(b.southwest.longitude, b.northeast.longitude);
|
||||||
|
final east = math.max(b.southwest.longitude, b.northeast.longitude);
|
||||||
|
return (p.latitude >= south &&
|
||||||
|
p.latitude <= north &&
|
||||||
|
p.longitude >= west &&
|
||||||
|
p.longitude <= east);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _distanceMeters(LatLng a, LatLng b) {
|
||||||
|
// هافرساين مبسطة
|
||||||
|
const R = 6371000.0; // m
|
||||||
|
final dLat = _deg2rad(b.latitude - a.latitude);
|
||||||
|
final dLng = _deg2rad(b.longitude - a.longitude);
|
||||||
|
final s1 = math.sin(dLat / 2);
|
||||||
|
final s2 = math.sin(dLng / 2);
|
||||||
|
final aa = s1 * s1 +
|
||||||
|
math.cos(_deg2rad(a.latitude)) *
|
||||||
|
math.cos(_deg2rad(b.latitude)) *
|
||||||
|
s2 *
|
||||||
|
s2;
|
||||||
|
final c = 2 * math.atan2(math.sqrt(aa), math.sqrt(1 - aa));
|
||||||
|
return R * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _deg2rad(double d) => d * math.pi / 180.0;
|
||||||
|
|
||||||
|
// تحريك الكاميرا لباوند معيّن
|
||||||
|
Future<void> _fitToBounds(LatLngBounds b, {double padding = 40}) async {
|
||||||
|
if (mapController == null) return;
|
||||||
|
try {
|
||||||
|
// أحياناً يلزم انتظار فريم حتى تكون الخريطة مرسومة
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
await mapController!.animateCamera(
|
||||||
|
CameraUpdate.newLatLngBounds(b, padding),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// fallback لو حصلت مشكلة الحجم
|
||||||
|
final center = LatLng(
|
||||||
|
(b.northeast.latitude + b.southwest.latitude) / 2,
|
||||||
|
(b.northeast.longitude + b.southwest.longitude) / 2,
|
||||||
|
);
|
||||||
|
await mapController!.animateCamera(CameraUpdate.newLatLng(center));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// الانتقال للخطوة التالية وتحديث التعليمات والكاميرا
|
||||||
|
void _advanceStep() {
|
||||||
|
if (currentStepIndex >= stepBounds.length - 1) return;
|
||||||
|
|
||||||
|
currentStepIndex++;
|
||||||
|
currentInstruction = _parseInstruction(stepInstructions[currentStepIndex]);
|
||||||
|
|
||||||
|
// نطق التعليمات
|
||||||
|
if (Get.isRegistered<TextToSpeechController>()) {
|
||||||
|
Get.find<TextToSpeechController>().speakText(currentInstruction);
|
||||||
|
} else {
|
||||||
|
Get.put(TextToSpeechController()).speakText(currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// تركيز الكاميرا على باوند الخطوة الجديدة
|
||||||
|
_fitToBounds(stepBounds[currentStepIndex]);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _prepareStepData(List<Map<String, dynamic>> steps) {
|
||||||
|
stepBounds.clear();
|
||||||
|
stepEndPoints.clear();
|
||||||
|
stepInstructions.clear();
|
||||||
|
|
||||||
|
for (final s in steps) {
|
||||||
|
// 1) instruction
|
||||||
|
final html = (s['html_instructions'] ?? '').toString();
|
||||||
|
stepInstructions.add(html);
|
||||||
|
|
||||||
|
// 2) end point
|
||||||
|
final end = s['end_location'];
|
||||||
|
final endLatLng = LatLng(
|
||||||
|
(end['lat'] as num).toDouble(), (end['lng'] as num).toDouble());
|
||||||
|
stepEndPoints.add(endLatLng);
|
||||||
|
|
||||||
|
// 3) bounds من الـ polyline (إن لم يوجد bounds جاهز من الـ API)
|
||||||
|
List<LatLng> pts = [];
|
||||||
|
if (s['polyline'] != null && s['polyline']['points'] != null) {
|
||||||
|
final decoded = decodePolyline(s['polyline']['points']);
|
||||||
|
for (var p in decoded) {
|
||||||
|
pts.add(LatLng((p[0] as num).toDouble(), (p[1] as num).toDouble()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fallback: استخدم start/end فقط
|
||||||
|
final start = s['start_location'];
|
||||||
|
pts.add(LatLng((start['lat'] as num).toDouble(),
|
||||||
|
(start['lng'] as num).toDouble()));
|
||||||
|
pts.add(endLatLng);
|
||||||
|
}
|
||||||
|
stepBounds.add(_boundsFromPoints(pts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// getMapDestination(String origin, destination) async {
|
||||||
|
// var url =
|
||||||
|
// ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');
|
||||||
|
|
||||||
|
// var response = await CRUD().getGoogleApi(link: url, payload: {});
|
||||||
|
// dataDestination = response['routes'][0]['legs'];
|
||||||
|
// final points =
|
||||||
|
// decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
|
||||||
|
// for (int i = 0; i < points.length; i++) {
|
||||||
|
// double lat = points[i][0].toDouble();
|
||||||
|
// double lng = points[i][1].toDouble();
|
||||||
|
// polylineCoordinatesDestination.add(LatLng(lat, lng));
|
||||||
|
// }
|
||||||
|
// // استخراج الخطوات
|
||||||
|
// routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
|
||||||
|
// Log.print('routeSteps: ${routeSteps}');
|
||||||
|
// currentStepIndex = 0;
|
||||||
|
// if (routeSteps.isNotEmpty) {
|
||||||
|
// currentInstruction =
|
||||||
|
// _parseInstruction(routeSteps[0]['html_instructions']);
|
||||||
|
// Log.print('currentInstruction: ${currentInstruction}');
|
||||||
|
// Get.isRegistered<TextToSpeechController>()
|
||||||
|
// ? Get.find<TextToSpeechController>().speakText(currentInstruction)
|
||||||
|
// : Get.put(TextToSpeechController()).speakText(currentInstruction);
|
||||||
|
// }
|
||||||
|
// update();
|
||||||
|
|
||||||
|
// // دالة مساعدة لتنظيف التعليمات
|
||||||
|
|
||||||
|
// if (polyLinesDestination.isNotEmpty) {
|
||||||
|
// // clearPolyline();
|
||||||
|
// var polyline = Polyline(
|
||||||
|
// polylineId: PolylineId(response["routes"][0]["summary"]),
|
||||||
|
// points: polylineCoordinatesDestination,
|
||||||
|
// width: 10,
|
||||||
|
// color: AppColor.redColor,
|
||||||
|
// );
|
||||||
|
// polyLinesDestination.add(polyline);
|
||||||
|
// // rideConfirm = false;
|
||||||
|
// update();
|
||||||
|
// } else {
|
||||||
|
// var polyline = Polyline(
|
||||||
|
// polylineId: PolylineId(response["routes"][0]["summary"]),
|
||||||
|
// points: polylineCoordinatesDestination,
|
||||||
|
// width: 10,
|
||||||
|
// color: AppColor.redColor,
|
||||||
|
// );
|
||||||
|
// // final dataBounds = response["routes"][0]["bounds"];
|
||||||
|
|
||||||
|
// // updateCameraFromBoundsAfterGetMap(dataBounds);
|
||||||
|
// // polyLinesDestination.add(polyline);
|
||||||
|
// // rideConfirm = false;
|
||||||
|
// // Define the northeast and southwest coordinates
|
||||||
|
|
||||||
|
// update();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
void updateCameraFromBoundsAfterGetMap(dynamic response) {
|
void updateCameraFromBoundsAfterGetMap(dynamic response) {
|
||||||
final bounds = response["routes"][0]["bounds"];
|
final bounds = response["routes"][0]["bounds"];
|
||||||
LatLng northeast =
|
LatLng northeast =
|
||||||
@@ -1516,4 +1721,19 @@ class MapDriverController extends GetxController {
|
|||||||
// checkIsDriverNearPassenger();
|
// checkIsDriverNearPassenger();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> startListeningStepNavigation() async {
|
||||||
|
_posSub?.cancel();
|
||||||
|
_posSub = Geolocator.getPositionStream(
|
||||||
|
locationSettings: const LocationSettings(
|
||||||
|
accuracy: LocationAccuracy.best,
|
||||||
|
distanceFilter: 5, // حدّث كل ~5 متر لتقليل الاهتزاز
|
||||||
|
),
|
||||||
|
).listen((pos) => _onLocationTick(LatLng(pos.latitude, pos.longitude)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopListeningStepNavigation() {
|
||||||
|
_posSub?.cancel();
|
||||||
|
_posSub = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -369,11 +369,71 @@ Raih Gai: For same-day return trips longer than 50km.
|
|||||||
"Add a comment (optional)": "أضف تعليقاً (اختياري)",
|
"Add a comment (optional)": "أضف تعليقاً (اختياري)",
|
||||||
"Type something...": "اكتب شيئاً...",
|
"Type something...": "اكتب شيئاً...",
|
||||||
"Submit rating": "إرسال التقييم",
|
"Submit rating": "إرسال التقييم",
|
||||||
'Trip Summary with': "ملخص الرحلة مع",
|
'Is device compatible': "هل الجهاز متوافق",
|
||||||
'Original Fare': "الأجرة الأصلية",
|
|
||||||
'Your Earnings': "أرباحك",
|
|
||||||
'Exclusive offers and discounts always with the Intaleq app':
|
'Exclusive offers and discounts always with the Intaleq app':
|
||||||
"عروض وخصومات حصرية دائماً مع تطبيق انطلق",
|
"عروض وخصومات حصرية دائماً مع تطبيق انطلق",
|
||||||
|
"Driver Registration": "تسجيل السائق",
|
||||||
|
"Driver": "السائق",
|
||||||
|
"Vehicle": "المركبة",
|
||||||
|
"Docs": "الوثائق",
|
||||||
|
"Driver's Personal Information": "المعلومات الشخصية للسائق",
|
||||||
|
"First Name": "الاسم الأول",
|
||||||
|
"Last Name": "اسم العائلة",
|
||||||
|
"National ID Number": "الرقم الوطني",
|
||||||
|
"License Expiry Date": "تاريخ انتهاء الرخصة",
|
||||||
|
"YYYY-MM-DD": "YYYY-MM-DD",
|
||||||
|
"Please select a date": "يرجى اختيار تاريخ",
|
||||||
|
"Vehicle Information": "معلومات المركبة",
|
||||||
|
"Car Plate Number": "رقم اللوحة",
|
||||||
|
"Car Make (e.g., Toyota)": "نوع السيارة (مثال تويوتا)",
|
||||||
|
"Car Model (e.g., Corolla)": "طراز السيارة (مثال كورولا)",
|
||||||
|
"Year of Manufacture": "سنة الصنع",
|
||||||
|
"Car Color": "لون السيارة",
|
||||||
|
"Upload Documents": "رفع المستندات",
|
||||||
|
"Driver License (Front)": "رخصة السائق (أمام)",
|
||||||
|
"Driver License (Back)": "رخصة السائق (خلف)",
|
||||||
|
"Car Registration (Front)": "ترخيص السيارة (أمام)",
|
||||||
|
"Car Registration (Back)": "ترخيص السيارة (خلف)",
|
||||||
|
"Tap to upload": "اضغط للرفع",
|
||||||
|
"<< BACK": "« السابق",
|
||||||
|
"NEXT >>": "التالي »",
|
||||||
|
"SUBMIT": "إرسال",
|
||||||
|
"Required field": "حقل مطلوب",
|
||||||
|
"Expired License": "الرخصة منتهية",
|
||||||
|
"Your driver’s license has expired.": "انتهت صلاحية رخصة قيادتك.",
|
||||||
|
"Missing Documents": "مستندات ناقصة",
|
||||||
|
"Please upload all 4 required documents.":
|
||||||
|
"يرجى رفع المستندات الأربعة جميعها.",
|
||||||
|
"Success": "تم بنجاح",
|
||||||
|
"Registration completed successfully!": "تم التسجيل بنجاح!",
|
||||||
|
"Error": "خطأ",
|
||||||
|
"Car Color (Name)": "لون السيارة (الاسم)",
|
||||||
|
"Car Color (Hex)": "لون السيارة (كود HEX)",
|
||||||
|
"Required field": "حقل مطلوب",
|
||||||
|
|
||||||
|
"color.white": "أبيض",
|
||||||
|
"color.black": "أسود",
|
||||||
|
"color.silver": "فضي",
|
||||||
|
"color.gray": "رمادي",
|
||||||
|
"color.gunmetal": "رصاصي غامق",
|
||||||
|
"color.red": "أحمر",
|
||||||
|
"color.blue": "أزرق",
|
||||||
|
"color.navy": "كحلي",
|
||||||
|
"color.green": "أخضر",
|
||||||
|
"color.darkGreen": "أخضر داكن",
|
||||||
|
"color.beige": "بيج",
|
||||||
|
"color.brown": "بني",
|
||||||
|
"color.maroon": "خمري",
|
||||||
|
"color.burgundy": "برغندي",
|
||||||
|
"color.yellow": "أصفر",
|
||||||
|
"color.orange": "برتقالي",
|
||||||
|
"color.gold": "ذهبي",
|
||||||
|
"color.bronze": "برونزي",
|
||||||
|
"color.champagne": "شامبانيا",
|
||||||
|
"color.purple": "بنفسجي",
|
||||||
|
"Registration failed. Please try again.": "فشل التسجيل، حاول مجدداً.",
|
||||||
|
"An unexpected error occurred:": "حدث خطأ غير متوقع:",
|
||||||
|
'Your email not updated yet': "بريدك الإلكتروني لم يتم تحديثه بعد",
|
||||||
'Enter your email'
|
'Enter your email'
|
||||||
'''Types of Trips in Intaleq:
|
'''Types of Trips in Intaleq:
|
||||||
|
|
||||||
|
|||||||
248
lib/device_compatibility_page.dart
Normal file
248
lib/device_compatibility_page.dart
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'controller/functions/device_analyzer.dart';
|
||||||
|
|
||||||
|
// --- CompatibilityDetailCard Widget (Updated to use 'max_score') ---
|
||||||
|
class CompatibilityDetailCard extends StatelessWidget {
|
||||||
|
final Map<String, dynamic> detail;
|
||||||
|
const CompatibilityDetailCard({super.key, required this.detail});
|
||||||
|
|
||||||
|
Color _getStatusColor(bool status, int achieved, int max) {
|
||||||
|
if (!status) return Colors.red.shade400;
|
||||||
|
if (achieved < max) return Colors.orange.shade600;
|
||||||
|
return Colors.teal;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData _getIconForLabel(String label) {
|
||||||
|
if (label.contains('رام')) return Icons.memory;
|
||||||
|
if (label.contains('معالج') || label.contains('CPU')) {
|
||||||
|
return Icons.developer_board;
|
||||||
|
}
|
||||||
|
if (label.contains('تخزين') || label.contains('كتابة')) {
|
||||||
|
return Icons.sd_storage_outlined;
|
||||||
|
}
|
||||||
|
if (label.contains('أندرويد')) return Icons.android;
|
||||||
|
if (label.contains('خدمات')) return Icons.g_mobiledata;
|
||||||
|
if (label.contains('حساسات') || label.contains('Gyroscope')) {
|
||||||
|
return Icons.sensors;
|
||||||
|
}
|
||||||
|
return Icons.smartphone;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bool status = detail['status'] ?? false;
|
||||||
|
final String label = detail['label'] ?? "";
|
||||||
|
final int achievedScore = detail['achieved_score'] ?? 0;
|
||||||
|
// Corrected to use 'max_score' from the analyzer
|
||||||
|
final int maxScore = detail['max_score'] ?? 1;
|
||||||
|
final Color color = _getStatusColor(status, achievedScore, maxScore);
|
||||||
|
final double progress =
|
||||||
|
(maxScore > 0) ? (achievedScore / maxScore).clamp(0.0, 1.0) : 0.0;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 7),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withOpacity(0.08),
|
||||||
|
blurRadius: 15,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(_getIconForLabel(label),
|
||||||
|
color: Colors.grey.shade600, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Corrected to display points out of max_score
|
||||||
|
Text(
|
||||||
|
"$achievedScore/$maxScore نقطة",
|
||||||
|
style: TextStyle(
|
||||||
|
color: color, fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
color: color,
|
||||||
|
minHeight: 6,
|
||||||
|
borderRadius: BorderRadius.circular(3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main Page Widget ---
|
||||||
|
class DeviceCompatibilityPage extends StatefulWidget {
|
||||||
|
const DeviceCompatibilityPage({super.key});
|
||||||
|
@override
|
||||||
|
State<DeviceCompatibilityPage> createState() =>
|
||||||
|
_DeviceCompatibilityPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeviceCompatibilityPageState extends State<DeviceCompatibilityPage> {
|
||||||
|
int score = 0;
|
||||||
|
List<Map<String, dynamic>> details = [];
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initializePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initializePage() async {
|
||||||
|
// await BatteryNotifier.checkBatteryAndNotify();
|
||||||
|
final result = await DeviceAnalyzer().analyzeDevice();
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
score = result['score'];
|
||||||
|
details = List<Map<String, dynamic>>.from(result['details']);
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getColorForScore(int value) {
|
||||||
|
if (value >= 80) return Colors.teal;
|
||||||
|
if (value >= 60) return Colors.orange.shade700;
|
||||||
|
return Colors.red.shade600;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getScoreMessage(int value) {
|
||||||
|
if (value >= 80) return "جهازك يقدم أداءً ممتازاً";
|
||||||
|
if (value >= 60) return "جهازك جيد ومناسب جداً";
|
||||||
|
if (value >= 40) return "متوافق، قد تلاحظ بعض البطء";
|
||||||
|
return "قد لا يعمل التطبيق بالشكل الأمثل";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: const Color(0xFFF7F8FC),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("توافق الجهاز",
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.black87, fontWeight: FontWeight.bold)),
|
||||||
|
centerTitle: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black87),
|
||||||
|
),
|
||||||
|
body: isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator(color: Colors.teal))
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
_buildScoreHeader(),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(top: 10, bottom: 20),
|
||||||
|
itemCount: details.length,
|
||||||
|
itemBuilder: (context, i) =>
|
||||||
|
CompatibilityDetailCard(detail: details[i]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.teal,
|
||||||
|
minimumSize: const Size(double.infinity, 50),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12)),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text("المتابعة إلى التطبيق",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ## Corrected Score Header Widget
|
||||||
|
/// This widget now uses a `Stack` to correctly place the text over the `PieChart`.
|
||||||
|
Widget _buildScoreHeader() {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
height: 220, // Give the container a fixed height
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
// Layer 1: The Pie Chart
|
||||||
|
PieChart(
|
||||||
|
PieChartData(
|
||||||
|
sectionsSpace: 4,
|
||||||
|
// This creates the "hole" in the middle.
|
||||||
|
centerSpaceRadius: 80,
|
||||||
|
startDegreeOffset: -90,
|
||||||
|
sections: [
|
||||||
|
PieChartSectionData(
|
||||||
|
color: _getColorForScore(score),
|
||||||
|
value: score.toDouble(),
|
||||||
|
title: '',
|
||||||
|
radius: 25,
|
||||||
|
),
|
||||||
|
PieChartSectionData(
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
value: (100 - score).toDouble().clamp(0, 100),
|
||||||
|
title: '',
|
||||||
|
radius: 25,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Layer 2: The text and message, centered on top of the chart
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"$score%",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 52,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _getColorForScore(score)),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
_getScoreMessage(score),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import 'constant/api_key.dart';
|
|||||||
import 'constant/info.dart';
|
import 'constant/info.dart';
|
||||||
import 'controller/firebase/firbase_messge.dart';
|
import 'controller/firebase/firbase_messge.dart';
|
||||||
import 'controller/firebase/local_notification.dart';
|
import 'controller/firebase/local_notification.dart';
|
||||||
|
import 'controller/functions/battery_status.dart';
|
||||||
import 'controller/functions/encrypt_decrypt.dart';
|
import 'controller/functions/encrypt_decrypt.dart';
|
||||||
import 'controller/functions/secure_storage.dart';
|
import 'controller/functions/secure_storage.dart';
|
||||||
import 'controller/local/local_controller.dart';
|
import 'controller/local/local_controller.dart';
|
||||||
|
|||||||
371
lib/views/auth/syria/registration_view.dart
Normal file
371
lib/views/auth/syria/registration_view.dart
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../../../controller/auth/syria/registration_controller.dart';
|
||||||
|
|
||||||
|
class RegistrationView extends StatelessWidget {
|
||||||
|
const RegistrationView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final RegistrationController controller = Get.put(RegistrationController());
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Driver Registration'.tr),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 90,
|
||||||
|
child: Obx(
|
||||||
|
() => Stepper(
|
||||||
|
currentStep: controller.currentPage.value,
|
||||||
|
type: StepperType.horizontal,
|
||||||
|
controlsBuilder: (_, __) => const SizedBox.shrink(),
|
||||||
|
steps: [
|
||||||
|
Step(
|
||||||
|
title: Text('Driver'.tr),
|
||||||
|
content: const SizedBox.shrink()),
|
||||||
|
Step(
|
||||||
|
title: Text('Vehicle'.tr),
|
||||||
|
content: const SizedBox.shrink()),
|
||||||
|
Step(
|
||||||
|
title: Text('Docs'.tr), content: const SizedBox.shrink()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: PageView(
|
||||||
|
controller: controller.pageController,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
onPageChanged: (i) => controller.currentPage.value = i,
|
||||||
|
children: [
|
||||||
|
_buildDriverInfoStep(context, controller),
|
||||||
|
_buildCarInfoStep(context, controller),
|
||||||
|
_buildDocumentUploadStep(context, controller),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
bottomNavigationBar: _buildBottomNavBar(controller),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 1
|
||||||
|
Widget _buildDriverInfoStep(BuildContext ctx, RegistrationController c) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Form(
|
||||||
|
key: c.driverInfoFormKey,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Driver's Personal Information".tr,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.firstNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'First Name'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.lastNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Last Name'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.nationalIdController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'National ID Number'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.driverLicenseExpiryController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'License Expiry Date'.tr,
|
||||||
|
hintText: 'YYYY-MM-DD'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
readOnly: true,
|
||||||
|
onTap: () async {
|
||||||
|
DateTime? d = await showDatePicker(
|
||||||
|
context: ctx,
|
||||||
|
initialDate: DateTime.now(),
|
||||||
|
firstDate: DateTime(2000),
|
||||||
|
lastDate: DateTime(2101),
|
||||||
|
);
|
||||||
|
if (d != null) {
|
||||||
|
c.driverLicenseExpiryDate = d;
|
||||||
|
c.driverLicenseExpiryController.text =
|
||||||
|
d.toLocal().toString().split(' ')[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Please select a date'.tr : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2
|
||||||
|
Widget _buildCarInfoStep(BuildContext ctx, RegistrationController c) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Form(
|
||||||
|
key: c.carInfoFormKey,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Vehicle Information'.tr,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.carPlateController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Car Plate Number'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.carMakeController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Car Make (e.g., Toyota)'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.carModelController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Car Model (e.g., Corolla)'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: c.carYearController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Year of Manufacture'.tr,
|
||||||
|
border: const OutlineInputBorder()),
|
||||||
|
validator: (v) =>
|
||||||
|
(v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// حقل اسم اللون (يبقى اختياري أو نملؤه تلقائيًا عند اختيار الهكس)
|
||||||
|
// TextFormField(
|
||||||
|
// controller: c.carColorController,
|
||||||
|
// decoration: InputDecoration(
|
||||||
|
// labelText: 'Car Color (Name)'.tr,
|
||||||
|
// border: const OutlineInputBorder(),
|
||||||
|
// ),
|
||||||
|
// validator: (v) =>
|
||||||
|
// (v?.isEmpty ?? true) ? 'Required field'.tr : null,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// الدروب داون للهكس + دائرة اللون
|
||||||
|
GetBuilder<RegistrationController>(
|
||||||
|
id: 'carColor', // اختياري لتحديث انتقائي
|
||||||
|
builder: (c) {
|
||||||
|
return DropdownButtonFormField<String>(
|
||||||
|
value: (c.colorHex != null && c.colorHex!.isNotEmpty)
|
||||||
|
? c.colorHex
|
||||||
|
: null,
|
||||||
|
isExpanded: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Car Color (Hex)'.tr,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
// prefixIcon: Padding(
|
||||||
|
// padding:
|
||||||
|
// const EdgeInsetsDirectional.only(start: 12, end: 8),
|
||||||
|
// child: Container(
|
||||||
|
// width: 18,
|
||||||
|
// height: 18,
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// color: (c.colorHex?.isNotEmpty ?? false)
|
||||||
|
// ? c.hexToColor(c.colorHex!)
|
||||||
|
// : Colors.transparent,
|
||||||
|
// shape: BoxShape.circle,
|
||||||
|
// border: Border.all(color: Colors.black26),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
),
|
||||||
|
items: RegistrationController.kCarColorOptions.map((opt) {
|
||||||
|
final hex = opt['hex']!;
|
||||||
|
final key = opt['key']!;
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: hex,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: c.hexToColor(hex),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.black12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(child: Text(key.tr)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (hex) {
|
||||||
|
c.colorHex = hex; // خزّن الهكس
|
||||||
|
final key = RegistrationController.kCarColorOptions
|
||||||
|
.firstWhere((o) => o['hex'] == hex)['key']!;
|
||||||
|
c.carColorController.text = key.tr;
|
||||||
|
c.update([
|
||||||
|
'carColor'
|
||||||
|
]); // <-- مهم: يعيد بناء الودجت ويحدّث الدائرة
|
||||||
|
},
|
||||||
|
validator: (v) =>
|
||||||
|
(v == null || v.isEmpty) ? 'Required field'.tr : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 3
|
||||||
|
Widget _buildDocumentUploadStep(BuildContext ctx, RegistrationController c) {
|
||||||
|
return GetBuilder<RegistrationController>(
|
||||||
|
builder: (ctrl) => SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Upload Documents'.tr,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildImagePickerBox(
|
||||||
|
'Driver License (Front)'.tr,
|
||||||
|
ctrl.driverLicenseFrontImage,
|
||||||
|
() => ctrl.pickImage(ImageType.driverLicenseFront),
|
||||||
|
),
|
||||||
|
_buildImagePickerBox(
|
||||||
|
'Driver License (Back)'.tr,
|
||||||
|
ctrl.driverLicenseBackImage,
|
||||||
|
() => ctrl.pickImage(ImageType.driverLicenseBack),
|
||||||
|
),
|
||||||
|
_buildImagePickerBox(
|
||||||
|
'Car Registration (Front)'.tr,
|
||||||
|
ctrl.carLicenseFrontImage,
|
||||||
|
() => ctrl.pickImage(ImageType.carLicenseFront),
|
||||||
|
),
|
||||||
|
_buildImagePickerBox(
|
||||||
|
'Car Registration (Back)'.tr,
|
||||||
|
ctrl.carLicenseBackImage,
|
||||||
|
() => ctrl.pickImage(ImageType.carLicenseBack),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildImagePickerBox(String title, File? img, VoidCallback onTap) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 150,
|
||||||
|
width: double.infinity,
|
||||||
|
child: img != null
|
||||||
|
? Image.file(img, fit: BoxFit.fill)
|
||||||
|
: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.camera_alt_outlined,
|
||||||
|
size: 40, color: Colors.grey[600]),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(title, style: TextStyle(color: Colors.grey[700])),
|
||||||
|
Text('Tap to upload'.tr,
|
||||||
|
style:
|
||||||
|
const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBottomNavBar(RegistrationController c) {
|
||||||
|
return Obx(() => Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (c.currentPage.value > 0)
|
||||||
|
TextButton(
|
||||||
|
onPressed: c.goToPreviousStep,
|
||||||
|
child: Text('<< BACK'.tr),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 40, vertical: 12),
|
||||||
|
backgroundColor: c.currentPage.value == 2
|
||||||
|
? Colors.green
|
||||||
|
: Theme.of(Get.context!).primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: c.isLoading.value
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (c.currentPage.value == 2) {
|
||||||
|
c.submitRegistration();
|
||||||
|
} else {
|
||||||
|
c.goToNextStep();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: c.isLoading.value
|
||||||
|
? const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white, strokeWidth: 2))
|
||||||
|
: Text(
|
||||||
|
c.currentPage.value == 2 ? 'SUBMIT'.tr : 'NEXT >>'.tr,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontSize: 16, color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,7 @@ class VideoPlayerPage extends StatelessWidget {
|
|||||||
class VideoPlayerPage1 extends StatelessWidget {
|
class VideoPlayerPage1 extends StatelessWidget {
|
||||||
final String videoUrl;
|
final String videoUrl;
|
||||||
|
|
||||||
VideoPlayerPage1({required this.videoUrl});
|
const VideoPlayerPage1({required this.videoUrl});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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/controller/functions/upload_image.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/device_compatibility_page.dart';
|
||||||
import 'package:sefer_driver/main.dart';
|
import 'package:sefer_driver/main.dart';
|
||||||
|
|
||||||
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
|
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
|
||||||
@@ -105,6 +106,11 @@ class AppDrawer extends StatelessWidget {
|
|||||||
icon: Icons.star,
|
icon: Icons.star,
|
||||||
color: Colors.amber,
|
color: Colors.amber,
|
||||||
onTap: () => Get.to(() => RatingScreen())),
|
onTap: () => Get.to(() => RatingScreen())),
|
||||||
|
DrawerItem(
|
||||||
|
title: 'Is device compatible'.tr,
|
||||||
|
icon: Icons.memory,
|
||||||
|
color: Colors.greenAccent,
|
||||||
|
onTap: () => Get.to(() => DeviceCompatibilityPage())),
|
||||||
DrawerItem(
|
DrawerItem(
|
||||||
title: 'Settings'.tr,
|
title: 'Settings'.tr,
|
||||||
icon: Icons.settings,
|
icon: Icons.settings,
|
||||||
@@ -228,7 +234,10 @@ class UserHeader extends StatelessWidget {
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
shadows: [Shadow(blurRadius: 2, color: Colors.black26)]),
|
shadows: [Shadow(blurRadius: 2, color: Colors.black26)]),
|
||||||
),
|
),
|
||||||
accountEmail: Text(box.read(BoxName.emailDriver)),
|
accountEmail:
|
||||||
|
box.read(BoxName.emailDriver).toString().contains('intaleqapp')
|
||||||
|
? Text('Your email not updated yet'.tr)
|
||||||
|
: Text(box.read(BoxName.emailDriver)),
|
||||||
currentAccountPicture: GetBuilder<ImageController>(
|
currentAccountPicture: GetBuilder<ImageController>(
|
||||||
builder: (controller) => Stack(
|
builder: (controller) => Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
import 'package:sefer_driver/constant/box_name.dart';
|
import 'package:sefer_driver/constant/box_name.dart';
|
||||||
import 'package:sefer_driver/controller/firebase/local_notification.dart';
|
import 'package:sefer_driver/controller/firebase/local_notification.dart';
|
||||||
import 'package:sefer_driver/main.dart';
|
import 'package:sefer_driver/main.dart';
|
||||||
import 'package:sefer_driver/views/auth/captin/login_captin.dart';
|
|
||||||
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
|
||||||
import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
|
import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.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/home/my_wallet/points_captain.dart';
|
|
||||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||||
|
|
||||||
import '../../../../../constant/colors.dart';
|
import '../../../../../constant/colors.dart';
|
||||||
import '../../../../../constant/links.dart';
|
import '../../../../../constant/links.dart';
|
||||||
import '../../../../../controller/auth/google_sign.dart';
|
|
||||||
import '../../../../../controller/firebase/firbase_messge.dart';
|
import '../../../../../controller/firebase/firbase_messge.dart';
|
||||||
import '../../../../../controller/functions/crud.dart';
|
import '../../../../../controller/functions/crud.dart';
|
||||||
import '../../../../../controller/home/captin/order_request_controller.dart';
|
import '../../../../../controller/home/captin/order_request_controller.dart';
|
||||||
import '../../../../Rate/ride_calculate_driver.dart';
|
import '../../../../Rate/ride_calculate_driver.dart';
|
||||||
import '../../../../auth/captin/otp_page.dart';
|
import '../../../../auth/syria/registration_view.dart';
|
||||||
import '../../../my_wallet/ecash.dart';
|
|
||||||
|
|
||||||
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||||
final firebaseMessagesController =
|
final firebaseMessagesController =
|
||||||
@@ -150,35 +146,28 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
|||||||
// height: 5,
|
// height: 5,
|
||||||
// ),
|
// ),
|
||||||
|
|
||||||
// AnimatedContainer(
|
AnimatedContainer(
|
||||||
// duration: const Duration(microseconds: 200),
|
duration: const Duration(microseconds: 200),
|
||||||
// width: controller.widthMapTypeAndTraffic,
|
width: controller.widthMapTypeAndTraffic,
|
||||||
// decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
// color: AppColor.secondaryColor,
|
color: AppColor.secondaryColor,
|
||||||
// border: Border.all(color: AppColor.blueColor),
|
border: Border.all(color: AppColor.blueColor),
|
||||||
// borderRadius: BorderRadius.circular(15)),
|
borderRadius: BorderRadius.circular(15)),
|
||||||
// child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
// return IconButton(
|
return IconButton(
|
||||||
// onPressed: () async {
|
onPressed: () async {
|
||||||
// Get.to(PhoneNumberScreen());
|
Get.to(() => const RegistrationView());
|
||||||
// // payWithEcashDriver(context, '2000');
|
|
||||||
// // payWithMTNWallet(context, '1', 'SYP');
|
// box.write(BoxName.statusDriverLocation, 'off');
|
||||||
// // firebaseMessagesController.sendNotificationToDriverMAP(
|
},
|
||||||
// // 'title',
|
icon: const Icon(
|
||||||
// // DateTime.now().toString(),
|
FontAwesome5.grin_tears,
|
||||||
// // 'ffX7xVXpdE_Xq8JBH3lgS4:APA91bGBHp53E-ZuXdlLBpRZohzqR9sazqcn3pwpEDG7JxkVi9MBtFDlCipzLpPCvD6LHEtds88ugGyCty7pEJVyx6tQYvzHVDCh7l3_7axpyriTBs5iv9E',
|
size: 29,
|
||||||
// // [],
|
color: AppColor.blueColor,
|
||||||
// // '');
|
),
|
||||||
// // box.write(BoxName.statusDriverLocation, 'off');
|
);
|
||||||
// },
|
}),
|
||||||
// icon: const Icon(
|
),
|
||||||
// FontAwesome5.grin_tears,
|
|
||||||
// size: 29,
|
|
||||||
// color: AppColor.blueColor,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
// ),f
|
|
||||||
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 5,
|
height: 5,
|
||||||
|
|||||||
@@ -1,310 +1,3 @@
|
|||||||
// 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';
|
||||||
@@ -355,7 +48,7 @@ GetBuilder<MapDriverController> driverEndRideBar() {
|
|||||||
),
|
),
|
||||||
_buildInfoColumn(
|
_buildInfoColumn(
|
||||||
icon: Icons.money_sharp,
|
icon: Icons.money_sharp,
|
||||||
text: '${controller.paymentAmount} ${'\$'.tr}',
|
text: '${controller.paymentAmount} ${'SYP'.tr}',
|
||||||
label: 'Price'.tr,
|
label: 'Price'.tr,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -194,11 +194,10 @@ class GoogleDriverMap extends StatelessWidget {
|
|||||||
Marker(
|
Marker(
|
||||||
markerId: MarkerId('MyLocation'.tr),
|
markerId: MarkerId('MyLocation'.tr),
|
||||||
position: locationController.myLocation,
|
position: locationController.myLocation,
|
||||||
draggable: false, // Changed: لا يمكن سحب ماركر السائق
|
|
||||||
icon: controller.carIcon,
|
|
||||||
rotation: locationController.heading,
|
rotation: locationController.heading,
|
||||||
anchor: const Offset(
|
flat: true,
|
||||||
0.5, 0.5), // New: وضع نقطة ارتكاز الأيقونة في المنتصف
|
anchor: const Offset(0.5, 0.5),
|
||||||
|
icon: controller.carIcon,
|
||||||
),
|
),
|
||||||
Marker(
|
Marker(
|
||||||
markerId: MarkerId('start'.tr),
|
markerId: MarkerId('start'.tr),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import audio_session
|
import audio_session
|
||||||
|
import battery_plus
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
@@ -35,6 +36,7 @@ import webview_flutter_wkwebview
|
|||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
|
BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
|
|||||||
24
pubspec.lock
24
pubspec.lock
@@ -73,6 +73,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.25"
|
version: "0.1.25"
|
||||||
|
battery_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: battery_plus
|
||||||
|
sha256: fb794c34cee2e4ea31005fb17ff15e1d904951ec7f15eedead741021870ee834
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.2"
|
||||||
|
battery_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: battery_plus_platform_interface
|
||||||
|
sha256: e8342c0f32de4b1dfd0223114b6785e48e579bfc398da9471c9179b907fa4910
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2114,6 +2130,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
upower:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: upower
|
||||||
|
sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ dependencies:
|
|||||||
flutter_svg: ^2.2.0
|
flutter_svg: ^2.2.0
|
||||||
lottie: ^3.3.1
|
lottie: ^3.3.1
|
||||||
flutter_staggered_animations: ^1.1.1
|
flutter_staggered_animations: ^1.1.1
|
||||||
|
battery_plus: ^6.2.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -133,8 +134,6 @@ flutter:
|
|||||||
- assets/
|
- assets/
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/fonts/
|
- assets/fonts/
|
||||||
- assets/lang/
|
|
||||||
# - shorebird.yaml
|
|
||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
# - family: mohanad
|
# - family: mohanad
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <battery_plus/battery_plus_windows_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
BatteryPlusWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FirebaseAuthPluginCApiRegisterWithRegistrar(
|
FirebaseAuthPluginCApiRegisterWithRegistrar(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
battery_plus
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
firebase_auth
|
firebase_auth
|
||||||
firebase_core
|
firebase_core
|
||||||
|
|||||||
Reference in New Issue
Block a user