This commit is contained in:
Hamza-Ayed
2025-08-09 14:35:09 +03:00
parent ba02d41e6d
commit 889c67a691
35 changed files with 1689 additions and 417 deletions

View File

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

View File

@@ -2,7 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -79,12 +79,14 @@ class LoginDriverController extends GetxController {
var res = await CRUD().get(
link: AppLink.getTesterApp,
payload: {'appPlatform': AppInformation.appName});
Log.print('res: ${res}');
if (res != 'failure') {
var d = jsonDecode(res);
isTest = d['message'][0]['isTest'];
Log.print('isTest: ${isTest}');
box.write(BoxName.isTest, isTest);
Log.print('isTest: ${box.read(BoxName.isTest)}');
// Log.print('isTest: ${box.read(BoxName.isTest)}');
update();
} else {
isTest = 0;
@@ -164,22 +166,22 @@ class LoginDriverController extends GetxController {
getJWT() async {
dev = Platform.isAndroid ? 'android' : 'ios';
Log.print(
'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
// Log.print(
// 'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
var payload = {
'id': box.read(BoxName.driverID) ?? AK.newId,
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
};
Log.print('payload: ${payload}');
// Log.print('payload: ${payload}');
var response0 = await http.post(
Uri.parse(AppLink.loginFirstTimeDriver),
body: payload,
);
Log.print('response0: ${response0.body}');
Log.print('request: ${response0.request}');
// Log.print('response0: ${response0.body}');
// Log.print('request: ${response0.request}');
if (response0.statusCode == 200) {
final decodedResponse1 = jsonDecode(response0.body);
// Log.print('decodedResponse1: ${decodedResponse1}');
@@ -207,13 +209,13 @@ class LoginDriverController extends GetxController {
'password': box.read(BoxName.emailDriver),
'aud': '${AK.allowed}$dev',
};
print(payload);
// print(payload);
var response1 = await http.post(
Uri.parse(AppLink.loginJwtDriver),
body: payload,
);
print(response1.request);
print(response1.body);
// print(response1.request);
// print(response1.body);
if (response1.statusCode == 200) {
final decodedResponse1 = jsonDecode(response1.body);
@@ -265,7 +267,7 @@ class LoginDriverController extends GetxController {
isloading = true;
update();
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: {
'email': email ?? 'yet',
@@ -329,8 +331,8 @@ class LoginDriverController extends GetxController {
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
await storage.write(
key: BoxName.fingerPrint, value: fingerPrint.toString());
print(jsonDecode(token)['data'][0]['token'].toString());
print(box.read(BoxName.tokenDriver).toString());
// print(jsonDecode(token)['data'][0]['token'].toString());
// print(box.read(BoxName.tokenDriver).toString());
if (email == '962798583052@intaleqapp.com') {
} else {
if (token != 'failure') {
@@ -379,7 +381,7 @@ class LoginDriverController extends GetxController {
isloading = true;
update();
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: {
'email': email ?? 'yet',

View 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 drivers 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();
}
}
}

View 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');
}
}
}

View File

@@ -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]}'
},
);
print(response.request);
Log.print('response.body: ${response.body}');
print(payload);
// print(response.request);
// Log.print('response.body: ${response.body}');
// print(payload);
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
@@ -94,9 +94,9 @@ class CRUD {
'X-HMAC-Auth': hmac.toString(),
},
);
Log.print('response.request: ${response.request}');
Log.print('response.body: ${response.body}');
print(payload);
// Log.print('response.request: ${response.request}');
// Log.print('response.body: ${response.body}');
// print(payload);
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
@@ -145,9 +145,9 @@ class CRUD {
'X-HMAC-Auth': hmac.toString(),
},
);
Log.print('response.request:${response.request}');
Log.print('response.body: ${response.body}');
Log.print('payload:$payload');
// Log.print('response.request:${response.request}');
// Log.print('response.body: ${response.body}');
// Log.print('payload:$payload');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
@@ -203,9 +203,9 @@ class CRUD {
// 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
},
);
print(response.request);
Log.print('response.body: ${response.body}');
print(payload);
// print(response.request);
// Log.print('response.body: ${response.body}');
// print(payload);
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);

View 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,
};
}
}

View File

@@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:io';
import 'dart:convert';
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/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/env/env.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/mydialoug.dart';
import 'package:flutter/material.dart';
@@ -565,7 +563,7 @@ class AI extends GetxController {
Future<void> pickAndSendImage(String type) async {
final picker = ImagePicker();
final pickedImage = await picker.pickImage(source: ImageSource.gallery);
final pickedImage = await picker.pickImage(source: ImageSource.camera);
if (pickedImage != null) {
image = File(pickedImage.path);
// Crop the image

View File

@@ -13,6 +13,7 @@ import '../../main.dart';
import '../../print.dart';
import '../home/captin/home_captain_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
import 'battery_status.dart';
import 'crud.dart';
import 'encrypt_decrypt.dart';
@@ -84,6 +85,7 @@ class LocationController extends GetxController {
_locationTimer =
Timer.periodic(const Duration(seconds: 5), (timer) async {
try {
await BatteryNotifier.checkBatteryAndNotify();
totalPoints =
Get.find<CaptainWalletController>().totalPoints.toString();
isActive = Get.find<HomeCaptainController>().isActive;

View 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;
}
}
}

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
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/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
@@ -399,6 +400,9 @@ class MapDriverController extends GetxController {
remainingTimeInPassengerLocatioWait = 0;
timeWaitingPassenger = 0;
box.write(BoxName.statusDriverLocation, 'on');
// // ابدأ التتبع الحيّ لخطوات الملاحة
await startListeningStepNavigation();
// box.write(BoxName.rideStatus, 'Begin'); //
// todo ride details
// Get.find<HomeCaptainController>().changeToAppliedRide('Begin');
@@ -615,7 +619,7 @@ class MapDriverController extends GetxController {
// originalDistanceM - distanceToDestination;
// 3. عتبة ثلث المسافة
final oneThirdDistanceM = originalDistanceM / 3;
final oneThirdDistanceM = originalDistanceM / 4;
// Logging للتتبع
Log.print('originalDistanceM: $originalDistanceM');
@@ -624,7 +628,7 @@ class MapDriverController extends GetxController {
Log.print('oneThirdDistanceM: $oneThirdDistanceM');
// 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
if (movedDistanceM > oneThirdDistanceM * 2) {
if (movedDistanceM > oneThirdDistanceM) {
MyDialog().getDialog(
'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 {
var url =
('${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'];
final points =
decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
polylineCoordinatesDestination.clear();
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));
}
// استخراج الخطوات
// الخطوات من أول leg
routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
Log.print('routeSteps: ${routeSteps}');
// حضّر بيانات كل step
_prepareStepData(routeSteps);
currentStepIndex = 0;
if (routeSteps.isNotEmpty) {
currentInstruction =
_parseInstruction(routeSteps[0]['html_instructions']);
Log.print('currentInstruction: ${currentInstruction}');
if (stepInstructions.isNotEmpty) {
currentInstruction = _parseInstruction(stepInstructions[0]);
Log.print('currentInstruction: $currentInstruction');
Get.isRegistered<TextToSpeechController>()
? Get.find<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();
// دالة مساعدة لتنظيف التعليمات
// // ابدأ التتبع الحيّ لخطوات الملاحة
// await startListeningStepNavigation();
}
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"];
// ————————————————————————————————————————————————————————————————
// يُنادى عند كل تحديث للموقع
void _onLocationTick(LatLng pos) async {
if (routeSteps.isEmpty || currentStepIndex >= stepBounds.length) return;
// updateCameraFromBoundsAfterGetMap(dataBounds);
// polyLinesDestination.add(polyline);
// rideConfirm = false;
// Define the northeast and southwest coordinates
// إذا تجاوزت نهاية الـ step الحالية بمسافة صغيرة -> انتقل
final double dToEnd = _distanceMeters(pos, stepEndPoints[currentStepIndex]);
final bool nearEnd = dToEnd <= 25; // عتبة 25م (عدّلها حسب حاجتك)
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) {
final bounds = response["routes"][0]["bounds"];
LatLng northeast =
@@ -1516,4 +1721,19 @@ class MapDriverController extends GetxController {
// checkIsDriverNearPassenger();
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;
}
}

View File

@@ -369,11 +369,71 @@ Raih Gai: For same-day return trips longer than 50km.
"Add a comment (optional)": "أضف تعليقاً (اختياري)",
"Type something...": "اكتب شيئاً...",
"Submit rating": "إرسال التقييم",
'Trip Summary with': "ملخص الرحلة مع",
'Original Fare': "الأجرة الأصلية",
'Your Earnings': "أرباحك",
'Is device compatible': "هل الجهاز متوافق",
'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 drivers 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'
'''Types of Trips in Intaleq:

View 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),
),
],
),
],
),
);
}
}

View File

@@ -17,6 +17,7 @@ import 'constant/api_key.dart';
import 'constant/info.dart';
import 'controller/firebase/firbase_messge.dart';
import 'controller/firebase/local_notification.dart';
import 'controller/functions/battery_status.dart';
import 'controller/functions/encrypt_decrypt.dart';
import 'controller/functions/secure_storage.dart';
import 'controller/local/local_controller.dart';

View 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),
),
),
],
),
));
}
}

View File

@@ -147,7 +147,7 @@ class VideoPlayerPage extends StatelessWidget {
class VideoPlayerPage1 extends StatelessWidget {
final String videoUrl;
VideoPlayerPage1({required this.videoUrl});
const VideoPlayerPage1({required this.videoUrl});
@override
Widget build(BuildContext context) {

View File

@@ -5,6 +5,7 @@ import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.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/device_compatibility_page.dart';
import 'package:sefer_driver/main.dart';
// استيراد الصفحات الأخرى... تأكد من صحة المسارات
@@ -105,6 +106,11 @@ class AppDrawer extends StatelessWidget {
icon: Icons.star,
color: Colors.amber,
onTap: () => Get.to(() => RatingScreen())),
DrawerItem(
title: 'Is device compatible'.tr,
icon: Icons.memory,
color: Colors.greenAccent,
onTap: () => Get.to(() => DeviceCompatibilityPage())),
DrawerItem(
title: 'Settings'.tr,
icon: Icons.settings,
@@ -228,7 +234,10 @@ class UserHeader extends StatelessWidget {
fontSize: 18,
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>(
builder: (controller) => Stack(
clipBehavior: Clip.none,

View File

@@ -1,25 +1,21 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/local_notification.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/orderCaptin/vip_order_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.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 '../../../../../constant/colors.dart';
import '../../../../../constant/links.dart';
import '../../../../../controller/auth/google_sign.dart';
import '../../../../../controller/firebase/firbase_messge.dart';
import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../Rate/ride_calculate_driver.dart';
import '../../../../auth/captin/otp_page.dart';
import '../../../my_wallet/ecash.dart';
import '../../../../auth/syria/registration_view.dart';
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
final firebaseMessagesController =
@@ -150,35 +146,28 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
// height: 5,
// ),
// AnimatedContainer(
// duration: const Duration(microseconds: 200),
// width: controller.widthMapTypeAndTraffic,
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// border: Border.all(color: AppColor.blueColor),
// borderRadius: BorderRadius.circular(15)),
// child: Builder(builder: (context) {
// return IconButton(
// onPressed: () async {
// Get.to(PhoneNumberScreen());
// // payWithEcashDriver(context, '2000');
// // payWithMTNWallet(context, '1', 'SYP');
// // firebaseMessagesController.sendNotificationToDriverMAP(
// // 'title',
// // DateTime.now().toString(),
// // 'ffX7xVXpdE_Xq8JBH3lgS4:APA91bGBHp53E-ZuXdlLBpRZohzqR9sazqcn3pwpEDG7JxkVi9MBtFDlCipzLpPCvD6LHEtds88ugGyCty7pEJVyx6tQYvzHVDCh7l3_7axpyriTBs5iv9E',
// // [],
// // '');
// // box.write(BoxName.statusDriverLocation, 'off');
// },
// icon: const Icon(
// FontAwesome5.grin_tears,
// size: 29,
// color: AppColor.blueColor,
// ),
// );
// }),
// ),f
AnimatedContainer(
duration: const Duration(microseconds: 200),
width: controller.widthMapTypeAndTraffic,
decoration: BoxDecoration(
color: AppColor.secondaryColor,
border: Border.all(color: AppColor.blueColor),
borderRadius: BorderRadius.circular(15)),
child: Builder(builder: (context) {
return IconButton(
onPressed: () async {
Get.to(() => const RegistrationView());
// box.write(BoxName.statusDriverLocation, 'off');
},
icon: const Icon(
FontAwesome5.grin_tears,
size: 29,
color: AppColor.blueColor,
),
);
}),
),
const SizedBox(
height: 5,

View File

@@ -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/services.dart';
import 'package:get/get.dart';
@@ -355,7 +48,7 @@ GetBuilder<MapDriverController> driverEndRideBar() {
),
_buildInfoColumn(
icon: Icons.money_sharp,
text: '${controller.paymentAmount} ${'\$'.tr}',
text: '${controller.paymentAmount} ${'SYP'.tr}',
label: 'Price'.tr,
),
],

View File

@@ -194,11 +194,10 @@ class GoogleDriverMap extends StatelessWidget {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: locationController.myLocation,
draggable: false, // Changed: لا يمكن سحب ماركر السائق
icon: controller.carIcon,
rotation: locationController.heading,
anchor: const Offset(
0.5, 0.5), // New: وضع نقطة ارتكاز الأيقونة في المنتصف
flat: true,
anchor: const Offset(0.5, 0.5),
icon: controller.carIcon,
),
Marker(
markerId: MarkerId('start'.tr),

View File

@@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import audio_session
import battery_plus
import device_info_plus
import file_selector_macos
import firebase_auth
@@ -35,6 +36,7 @@ import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))

View File

@@ -73,6 +73,22 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@@ -2114,6 +2130,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
upower:
dependency: transitive
description:
name: upower
sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf
url: "https://pub.dev"
source: hosted
version: "0.7.0"
url_launcher:
dependency: "direct main"
description:

View File

@@ -100,6 +100,7 @@ dependencies:
flutter_svg: ^2.2.0
lottie: ^3.3.1
flutter_staggered_animations: ^1.1.1
battery_plus: ^6.2.2
dev_dependencies:
flutter_test:
@@ -133,8 +134,6 @@ flutter:
- assets/
- assets/images/
- assets/fonts/
- assets/lang/
# - shorebird.yaml
fonts:
# - family: mohanad

View File

@@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <battery_plus/battery_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_auth/firebase_auth_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>
void RegisterPlugins(flutter::PluginRegistry* registry) {
BatteryPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseAuthPluginCApiRegisterWithRegistrar(

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
battery_plus
file_selector_windows
firebase_auth
firebase_core