fix marker rendering & modernize riding widgets for dark mode - 2026-04-11

This commit is contained in:
Hamza-Ayed
2026-04-11 01:14:09 +03:00
parent 3f03f25142
commit 454276d1e0
88 changed files with 50376 additions and 23310 deletions

3
.env
View File

@@ -112,4 +112,5 @@ V=P
W=T
X=D
Y=S
Z=M
Z=M
mapSaasKey=intaleq_secret_2026

View File

@@ -20,8 +20,8 @@ android {
ndkVersion "29.0.14033849"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
coreLibraryDesugaringEnabled true
}
externalNativeBuild {
@@ -33,7 +33,7 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = "17"
}
sourceSets {
main {

View File

@@ -31,6 +31,30 @@ subprojects {
}
subprojects {
project.evaluationDependsOn(':app')
// Sync Kotlin JVM Target for ALL subprojects (works at task-config time)
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "17"
}
}
}
// Force Java 17 on plugin subprojects AFTER they set their own defaults.
// Skip :app because it's already evaluated (via evaluationDependsOn) and already configured.
subprojects { sub ->
if (sub.name != 'app') {
sub.afterEvaluate {
if (sub.hasProperty('android')) {
sub.android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
}
}
}
}
tasks.register("clean", Delete) {

File diff suppressed because one or more lines are too long

1804
assets/style_dark.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,281 @@
import os
import re
import json
# Pre-defined mapping of English keys to standard Arabic translations.
# These represent the clean keys that were accidentally removed.
ARABIC_TRANS = {
"1 Passenger": "راكب واحد",
"2 Passengers": "راكبين",
"3 Passengers": "٣ ركاب",
"4 Passengers": "٤ ركاب",
"2. Attach Recorded Audio (Optional)": "٢. إرفاق تسجيل صوتي (اختياري)",
"Account": "الحساب",
"Actions": "الإجراءات",
"Active Users": "المستخدمين النشطين",
"Add a Stop": "إضافة نقطة توقف",
"Add a new waypoint stop": "إضافة نقطة توقف جديدة",
"After this period\\nYou can\\'t cancel!": "بعد هالوقت\\ا فيك تكنسل!",
"Age is": "العمر",
"Alert": "تنبيه",
"An OTP has been sent to your number.": "تم إرسال كود التأكيد لرقمك.",
"An error occurred": "حدث خطأ",
"Appearance": "المظهر",
"Are you sure you want to delete this file?": "متأكد بدك تحذف هالملف؟",
"Are you sure you want to logout?": "متأكد بدك تسجل خروج؟",
"Arrived": "وصلنا",
"Audio Recording": "تسجيل صوتي",
"Call": "اتصال",
"Call Support": "اتصل بالدعم",
"Call left": "مكالمات متبقية",
"Change Photo": "تغيير الصورة",
"Choose from Gallery": "اختر من المعرض",
"Choose from contact": "اختر من جهات الاتصال",
"Click to track the trip": "اضغط لتتبع المشوار",
"Close panel": "إغلاق اللوحة",
"Coming": "جايين",
"Complete Payment": "إتمام الدفع",
"Confirm Cancellation": "تأكيد الإلغاء",
"Confirm Pickup Location": "تأكيد موقع الانطلاق",
"Connection failed. Please try again.": "فشل الاتصال. جرب مرة تانية.",
"Could not create ride. Please try again.": "ما قدرنا نعمل مشوار. جرب مرة تانية.",
"Crop Photo": "قص الصورة",
"Dark Mode": "الوضع الليلي",
"Delete": "حذف",
"Delete Account": "حذف الحساب",
"Delete All": "حذف الكل",
"Delete All Recordings?": "حذف كل التسجيلات؟",
"Delete Recording?": "حذف التسجيل؟",
"Destination Set": "تم تحديد الوجهة",
"Distance": "المسافة",
"Double tap to open search or enter destination": "اضغط مرتين لتفتح البحث أو تدخل الوجهة",
"Double tap to set or change this waypoint on the map": "اضغط مرتين لتحديد أو تغيير هالنقطة عالخريطة",
"Drawing route on map...": "عم نرسم الطريق عالخريطة...",
"Driver Phone": "رقم الكابتن",
"Driver is Going To You": "الكابتن جاي لعندك",
"Emergency Mode Triggered": "تم تفعيل وضع الطوارئ",
"Emergency SOS": "طوارئ SOS",
"Enter the 5-digit code": "أدخل الكود المكون من ٥ أرقام",
"Enter your City": "أدخل مدينتك",
"Enter your Password": "أدخل كلمة السر",
"Failed to book trip: $e": "فشل حجز المشوار",
"Failed to get location": "فشل جلب الموقع",
"Failed to initiate payment. Please try again.": "فشل بدء الدفع. جرب مرة تانية.",
"Failed to send OTP": "فشل إرسال كود التأكيد",
"Failed to upload photo": "فشل رفع الصورة",
"Finished": "انتهى",
"Fixed Price": "سعر ثابت",
"General": "عام",
"Grant": "منح الإذن",
"Have a Promo Code?": "معك كود خصم؟",
"Hello! I\\'m inviting you to try Intaleq.": "أهلاً! بدعيك لتجربة تطبيق انطلق.",
"Hi ,I Arrive your site": "مرحباً، وصلت لموقعك",
"Hi, Where to": "مرحباً، لوين؟",
"Home": "الرئيسية",
"I am currently located at": "أنا حالياً موجود بـ",
"I'm Safe": "أنا بأمان",
"If you need to reach me, please contact the driver directly at": "إذا بدك تتواصل معي، حاكي الكابتن مباشرة على",
"Image Upload Failed": "فشل رفع الصورة",
"Intaleq Passenger": "راكب انطلق",
"Invite": "دعوة",
"Join a channel": "انضم للقناة",
"Last Name": "الاسم الأخير",
"Leave a detailed comment (Optional)": "اترك تعليق تفصيلي (اختياري)",
"Light Mode": "الوضع الفاتح",
"Listen": "استماع",
"Location": "الموقع",
"Location Received": "تم استلام الموقع",
"Logout": "تسجيل خروج",
"Map Error": "خطأ بالخريطة",
"Message": "رسالة",
"Move map to select destination": "حرك الخريطة لتحديد الوجهة",
"Move map to set start location": "حرك الخريطة لتحديد نقطة البداية",
"Move map to set stop": "حرك الخريطة لتحديد التوقف",
"Move map to your home location": "حرك الخريطة لموقع البيت",
"Move map to your pickup point": "حرك الخريطة لموقع الانطلاق",
"Move map to your work location": "حرك الخريطة لموقع الشغل",
"N/A": "غير متاح",
"No Notifications": "ما في إشعارات",
"No Recordings Found": "ما في تسجيلات",
"No Rides now!": "ما في مشاوير حالياً!",
"No contacts available": "ما في جهات اتصال",
"No i want": "لا بدي",
"No notification data found.": "ما في بيانات للإشعار.",
"No routes available for this destination.": "ما في طرق لهالوجهة.",
"No user found": "ما في مستخدم",
"No,I want": "لا، بدي",
"No.Iwant Cancel Trip.": "لا. بدي كنسل المشوار.",
"Now move the map to your pickup point": "الآن حرك الخريطة لمكان انطلاقك",
"Now set the pickup point for the other person": "الآن حدد مكان الانطلاق للشخص التاني",
"On Trip": "بالمشوار",
"Open": "فتح",
"Open destination search": "فتح بحث الوجهة",
"Open in Google Maps": "فتح بخرائط جوجل",
"Order VIP Canceld": "انلغى طلب VIP",
"Passenger": "الراكب",
"Passenger cancel order": "الراكب كنسل الطلب",
"Pay by MTN Wallet": "الدفع بمحفظة سيريتل كاش",
"Pay by Sham Cash": "الدفع بشام كاش",
"Pay by Syriatel Wallet": "الدفع بمحفظة سيريتل كاش",
"Pay with PayPal": "الدفع بـ PayPal",
"Phone": "الموبايل",
"Phone Number": "رقم الموبايل",
"Phone Number Check": "فحص رقم الموبايل",
"Phone number seems too short": "رقم الموبايل قصير كتير",
"Phone verified. Please complete registration.": "تأكد الرقم. يرجى إتمام التسجيل.",
"Pick destination on map": "اختر الوجهة عالخريطة",
"Pick location on map": "اختر الموقع عالخريطة",
"Pick on map": "اختر عالخريطة",
"Pick start point on map": "اختر نقطة البداية عالخريطة",
"Plan Your Route": "خطط مسارك",
"Please add contacts to your phone.": "يرجى إضافة أسماء لجوالك.",
"Please check your internet and try again.": "تأكد من النت وجرب مرة تانية.",
"Please enter a valid email.": "أدخل إيميل صحيح.",
"Please enter a valid phone number.": "أدخل رقم موبايل صحيح.",
"Please enter the number without the leading 0": "أدخل الرقم بدون 0 بالبداية",
"Please enter your phone number": "أدخل رقم موبايلك",
"Please go to Car now": "يا ريت تروح لعند السيارة هلا",
"Please select a reason first": "اختر السبب أول شي",
"Please set a valid SOS phone number.": "يا ريت تحط رقم طوارئ صح.",
"Please slow down": "يا ريت تخفف سرعة",
"Please wait while we prepare your trip.": "انتظر شوي عم نجهز المشوار.",
"Preferences": "التفضيلات",
"Profile photo updated": "تم تحديث صورة الغلاف",
"Quick Message": "رسالة سريعة",
"Rating is": "التقييم هو",
"Received empty route data.": "تم استلام بيانات طريق فارغة.",
"Record": "تسجيل",
"Record your trips to see them here.": "سجل مشاويرك لتشوفهم هون.",
"Rejected Orders Count": "عدد الطلبات المرفوضة",
"Remove waypoint": "حذف نقطة التوقف",
"Report": "الإبلاغ",
"Route and prices have been calculated successfully!": "تم حساب الطريق والأسعار بنجاح!",
"SOS": "نجدة SOS",
"Save": "حفظ",
"Save Changes": "حفظ التغييرات",
"Save Name": "حفظ الاسم",
"Search country": "البحث عن الدولة",
"Search for a starting point": "ابحث عن نقطة انطلاق",
"Select Appearance": "اختر المظهر",
"Select Education": "اختر مستوى التعليم",
"Select Gender": "اختر الجنس",
"Select This Ride": "اختر هالمشوار",
"Select betweeen types": "اختر بين الأنواع",
"Send Email": "إرسال إيميل",
"Send SOS": "إرسال نجدة",
"Send WhatsApp Message": "إرسال رسالة واتساب",
"Server Error": "خطأ بالسيرفر",
"Server error": "خطأ بالسيرفر",
"Server error. Please try again.": "خطأ بالسيرفر. جرب مرة تانية.",
"Set Destination": "تحديد الوجهة",
"Set Phone Number": "تعيين رقم الموبايل",
"Set as Home": "تحديد كالبيت",
"Set as Stop": "تحديد كنقطة توقف",
"Set as Work": "تحديد كالشغل",
"Share": "مشاركة",
"Share Trip": "مشاركة المشوار",
"Share your experience to help us improve...": "شاركنا تجربتك لنحسن خدمتنا...",
"Something went wrong. Please try again.": "صار غلط. جرب مرة تانية.",
"Speaking...": "عم يحكي...",
"Speed Over": "تجاوز السرعة",
"Start Point": "نقطة البداية",
"Stay calm. We are here to help.": "خليك هادي. نحن هون لمساعدتك.",
"Stop": "توقف",
"Submit Rating": "إرسال التقييم",
"Support & Info": "الدعم والمعلومات",
"System Default": "حسب النظام",
"Take a Photo": "التقاط صورة",
"Tap to apply your discount": "اضغط لتطبيق الخصم",
"Tap to search your destination": "اضغط للبحث عن وجهتك",
"The driver cancelled the trip.": "الكابتن كنسل المشوار.",
"This action cannot be undone.": "هالإجراء ما بيتراجع عنه.",
"This action is permanent and cannot be undone.": "هالإجراء دائم وما بيتراجع عنه.",
"This is for delivery or a motorcycle.": "هاد للتوصيل أو الدراجة النارية.",
"Time": "الوقت",
"To :": "إلى :",
"Top up Balance": "شحن الرصيد",
"Top up Balance to continue": "اشحن رصيدك لتكمل",
"Total Invites": "مجموع الدعوات",
"Total Price": "السعر الإجمالي",
"Trip booked successfully": "تم حجز المشوار بنجاح",
"Type your message...": "اكتب رسالتك...",
"Unknown Location": "موقع غير معروف",
"Update Name": "تحديث الاسم",
"Verified Passenger": "راكب موثق",
"View Map": "عرض الخريطة",
"Wait for the trip to start first": "استنى ليبلش المشوار أول",
"Waiting...": "بانتظار...",
"Warning": "تحذير",
"Waypoint has been set successfully": "تم تعيين نقطة التوقف بنجاح",
"We use location to get accurate and nearest driver for you": "منستخدم لوكيشنك لنلاقي أقرب كابتن إلك بدقة",
"WhatsApp": "واتساب",
"Why do you want to cancel?": "ليش بدك تكنسل؟",
"Yes": "إي",
"You should ideintify your gender for this type of trip!": "لازم تحدد جنسك لهالنوع من المشاوير!",
"You will choose one of above!": "لازم تختار واحد من فوق!",
"Your Rewards": "مكافآتك",
"Your complaint has been submitted.": "تم إرسال شكوتك.",
"and acknowledge our Privacy Policy.": "وأقر بسياسة الخصوصية الخاصة بنا.",
"as the driver.": "ككابتن.",
"cancelled": "تكنسل",
"due to a previous trip.": "عن مشوار قديم.",
"insert sos phone": "دخل رقم طوارئ",
"is driving a": "عم يسوق",
"min added to fare": "دقيقة نضافت للأجرة",
"phone not verified": "رقم الموبايل مو متأكد",
"to arrive you.": "ليوصلك.",
"unknown": "غير معروف",
"wait 1 minute to recive message": "استنى دقيقة لتستلم الرسالة",
"with license plate": "برقم اللوحة",
"witout zero": "بدون صفر",
"you must insert token code": "لازم تدخل الكود",
}
def inject():
# 1. Read the missing keys from JSON file outputted earlier
# We will just inject ALL these ARABIC_TRANS keys into the codebase if missing
file_path = 'lib/controller/local/translations.dart'
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Find locales using regex e.g. "ar": { or "ar_SA": { or "en": {
# It finds the locale name
pattern = re.compile(r'(\n\s*)"([a-zA-Z_]+)":\s*\{')
new_content = ""
last_idx = 0
for match in pattern.finditer(content):
# We append what we had before
new_content += content[last_idx:match.end()]
last_idx = match.end()
locale = match.group(2)
is_arabic = locale.startswith('ar')
# Determine the insertion string for this locale
addons = []
for key, ar_val in ARABIC_TRANS.items():
# If the exact key doesn't exist in the file (naively checking inside the whole file,
# or we can just inject avoiding duplicates if it is not in the remaining text section)
# A safer check: if f'"{key}"' not in content and f"'{key}'" not in content:
addon = ""
if is_arabic:
addon = f'\n "{key}": "{ar_val}",'
else:
addon = f'\n "{key}": "{key}",'
addons.append(addon)
new_content += "".join(addons)
new_content += content[last_idx:]
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content)
print(f"Successfully injected {len(ARABIC_TRANS)} missing keys into all locales.")
if __name__ == "__main__":
inject()

View File

@@ -176,6 +176,8 @@ PODS:
- RecaptchaInterop (101.0.0)
- record_ios (1.2.0):
- Flutter
- sensors_plus (0.0.1):
- Flutter
- share_plus (0.0.1):
- Flutter
- sign_in_with_apple (0.0.1):
@@ -286,6 +288,7 @@ DEPENDENCIES:
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
- record_ios (from `.symlinks/plugins/record_ios/ios`)
- sensors_plus (from `.symlinks/plugins/sensors_plus/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
@@ -389,6 +392,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/quick_actions_ios/ios"
record_ios:
:path: ".symlinks/plugins/record_ios/ios"
sensors_plus:
:path: ".symlinks/plugins/sensors_plus/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
sign_in_with_apple:
@@ -460,6 +465,7 @@ SPEC CHECKSUMS:
quick_actions_ios: 500fcc11711d9f646739093395c4ae8eec25f779
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
record_ios: 412daca2350b228e698fffcd08f1f94ceb1e3844
sensors_plus: 3c3bac724a2128c895623e03efd82cf0e94fd8e8
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0

View File

@@ -4,6 +4,7 @@ import 'package:Intaleq/controller/firebase/firbase_messge.dart';
import 'package:Intaleq/controller/firebase/local_notification.dart';
import 'package:Intaleq/controller/home/deep_link_controller.dart';
import 'package:Intaleq/controller/local/local_controller.dart';
import 'package:Intaleq/controller/functions/tts.dart';
/// This is the central dependency injection file for the app.
/// It uses GetX Bindings to make the app start faster and manage memory better.
@@ -35,5 +36,8 @@ class AppBindings extends Bindings {
// FirebaseMessagesController is also initialized on splash, but must persist its token and listeners.
Get.lazyPut(() => FirebaseMessagesController(), fenix: true);
// TextToSpeechController for global accessibility
Get.lazyPut(() => TextToSpeechController(), fenix: true);
}
}

View File

@@ -1,4 +1,4 @@
import 'package:Intaleq/main.dart';
// import 'package:Intaleq/main.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../env/env.dart';

View File

@@ -8,6 +8,7 @@ class BoxName {
static const String serverChosen = "serverChosen";
static const String security_check = "security_check";
static const String gender = "gender";
static const String themeMode = "themeMode";
static const String jwt = "jwt";
static const String lowEndMode = "lowEndMode";
static const String deviceFpEncrypted = "deviceFpEncrypted";

View File

@@ -1,23 +1,23 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
/// A class that holds the color palette for the 'Intaleq' app.
/// The palette is professionally designed to be modern, cohesive, and culturally
/// relevant, inspired by the Syrian flag and the app's brand identity.
class AppColor {
// --- Core Brand Colors (Inspired by the Syrian Flag) ---
/// **Primary Color:** A strong, modern green representing growth, safety, and movement.
/// **Primary Color:** The brand's signature Twitter Blue representing trust and modern communication.
/// Ideal for app bars, primary buttons, and major UI elements.
static const Color primaryColor = Color(0xFF108942);
static const Color primaryColor = Color(0xFF1DA1F2);
/// **Text/Write Color:** A very dark, near-black color for main text.
/// It's softer on the eyes than pure black, improving readability.
/// The variable name `writeColor` is kept as requested.
static const Color writeColor = Color(0xFF1A1A1A);
static Color get writeColor => Get.isDarkMode ? Colors.white : const Color(0xFF1A1A1A);
/// **Secondary Color:** Pure white, used for backgrounds to create a clean
/// and spacious look, ensuring content stands out.
static const Color secondaryColor = Colors.white;
static Color get secondaryColor => Get.isDarkMode ? const Color(0xFF1E1E1E) : Colors.white;
/// **Accent Color:** A vibrant, energetic red from the Syrian flag.
/// Perfect for calls-to-action, highlights, icons, and notifications.
@@ -27,7 +27,7 @@ class AppColor {
/// **Grey Color:** A neutral grey for secondary text, borders, dividers,
/// and disabled states.
static const Color grayColor = Color(0xFF8E8E93);
static Color get grayColor => Get.isDarkMode ? Colors.grey[400]! : const Color(0xFF8E8E93);
/// **Red Color (Error):** A clear, attention-grabbing red for error messages and alerts.
static const Color redColor = Color(0xFFD32F2F);
@@ -52,10 +52,15 @@ class AppColor {
/// **Twitter/X Color:** The official brand color for social login buttons.
/// **Twitter Blue:** The brand's signature blue color used for the drawer,
/// menu icons, and secondary actions (formerly Cyan Blue).
static Color get cyanBlue => const Color(0xFF1DA1F2);
/// **Blue Accent:** A softer, translucent version of the brand blue.
static Color get cyanAccent => const Color(0xFF1DA1F2).withOpacity(0.12);
// --- Utility Colors ---
/// **Accent Tint:** A transparent version of the red accent color.
/// Useful for subtle backgrounds on selected items or highlighted areas.
/// Replaces the old `deepPurpleAccent` to match the new brand palette.
static Color deepPurpleAccent = const Color(0xFFCE1126).withOpacity(0.1);
static Color get deepPurpleAccent => const Color(0xFFCE1126).withOpacity(0.1);
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:convert';
import 'package:crypto/crypto.dart';
@@ -12,7 +13,7 @@ class AC {
if (box.read(BoxName.apiKeyRun).toString() != 'run') {
var res = await CRUD().get(link: AppLink.getApiKey, payload: {});
var decod = jsonDecode(res);
print(decod);
Log.print(decod);
Map<String, dynamic> jsonData = {};
for (var i = 0; i < decod['message'].length; i++) {
String h = decod['message'][i]['hashed_key'].toString();
@@ -29,7 +30,7 @@ class AC {
jsonData[name] = value;
}
String jsonString = json.encode(jsonData);
print(jsonString);
Log.print(jsonString);
box.write(BoxName.apiKeyRun, 'run');
}
}
@@ -71,7 +72,7 @@ class AC {
f.add(i);
}
print(f);
// print(f);
Map<String, String> j = {};
j['birinci'] = f[4];
j['ikinci'] = f[2];

View File

@@ -11,7 +11,12 @@ class AppLink {
/// هذا الرابط خاص برحلات الركاب، ويستخدمه السيرفر الجانبي للرحلات (Ride Server Side) للتعامل مع عمليات إلغاء الرحلات وتحديث حالة الرحلات وغيرها من العمليات المتعلقة بالرحلات.
/// https://routesy.intaleq.xyz for syria
/// for jordan https://routesjo.intaleq.xyz
static String routesOsm = 'https://routesjo.intaleq.xyz';
static String routesOsm = 'https://routesy.intaleq.xyz';
static String mapSaasRoute = 'https://map-saas.intaleqapp.com/api/maps/route';
static String reverseGeocoding =
'https://map-saas.intaleqapp.com/api/geocoding/reverse';
static String searchGeocoding =
'https://map-saas.intaleqapp.com/api/geocoding/search';
///https://location.intaleq.xyz/intaleq/ride/location
///locationServerSide هو السيرفر الجانبي الخاص بموقع السائقين، حيث يتم إرسال تحديثات الموقع من التطبيق إلى هذا السيرفر، ومن ثم يقوم هذا السيرفر بتوزيع هذه التحديثات إلى الركاب المتصلين الذين يتابعون السائق في الوقت الحقيقي.

View File

@@ -5,7 +5,7 @@ import 'box_name.dart';
import 'colors.dart';
class AppStyle {
static TextStyle headTitle = TextStyle(
static TextStyle get headTitle => TextStyle(
fontWeight: FontWeight.bold,
fontSize: 36,
color: AppColor.accentColor,
@@ -13,57 +13,61 @@ class AppStyle {
// ?GoogleFonts.markaziText().fontFamily
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle headTitle2 = TextStyle(
static TextStyle get headTitle2 => TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle title = TextStyle(
static TextStyle get title => TextStyle(
fontWeight: FontWeight.normal,
fontSize: 16,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle subtitle = TextStyle(
static TextStyle get subtitle => TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle number = const TextStyle(
static TextStyle get number => TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: AppColor.writeColor,
fontFamily: 'digit');
static BoxDecoration boxDecoration = const BoxDecoration(
static BoxDecoration get boxDecoration => BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(2, 4)),
color: AppColor.accentColor.withOpacity(0.3),
blurRadius: 5,
offset: const Offset(2, 4)),
BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2))
color: AppColor.accentColor.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(-2, -2))
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.all(
borderRadius: const BorderRadius.all(
Radius.elliptical(15, 30),
));
static BoxDecoration boxDecoration1 = const BoxDecoration(
static BoxDecoration get boxDecoration1 => BoxDecoration(
boxShadow: [
BoxShadow(
color: Color.fromARGB(255, 237, 230, 230),
blurRadius: 5,
offset: Offset(2, 4)),
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4)),
BoxShadow(
color: Color.fromARGB(255, 242, 237, 237),
color: AppColor.primaryColor.withOpacity(0.02),
blurRadius: 5,
offset: Offset(-2, -2))
offset: const Offset(-2, -2))
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.all(
borderRadius: const BorderRadius.all(
Radius.elliptical(15, 30),
),
);

View File

@@ -1,4 +1,4 @@
import 'package:Intaleq/env/env.dart';
// import 'package:Intaleq/env/env.dart';
class TableName {
static const String placesFavorite = "placesFavorite";

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
@@ -11,25 +12,25 @@ Future<bool> verifyCertificateManually(
final certificate = socket.peerCertificate;
if (certificate == null) {
print("❌ لا يوجد شهادة.");
Log.print("❌ لا يوجد شهادة.");
return false;
}
final der = certificate.der;
final actualPin = base64.encode(sha256.convert(der).bytes);
print("📛 HOST: $host");
print("📜 Subject: ${certificate.subject}");
print("📜 Issuer: ${certificate.issuer}");
print("📅 Valid From: ${certificate.startValidity}");
print("📅 Valid To: ${certificate.endValidity}");
print(
Log.print("📛 HOST: $host");
Log.print("📜 Subject: ${certificate.subject}");
Log.print("📜 Issuer: ${certificate.issuer}");
Log.print("📅 Valid From: ${certificate.startValidity}");
Log.print("📅 Valid To: ${certificate.endValidity}");
Log.print(
"🔐 Server Pin: $actualPin${actualPin == expectedPin ? '✅ MATCH' : '❌ MISMATCH'}");
socket.destroy();
return actualPin == expectedPin;
} catch (e) {
print("❌ خطأ أثناء الاتصال أو الفحص: $e");
Log.print("❌ خطأ أثناء الاتصال أو الفحص: $e");
return false;
}
}
@@ -37,4 +38,4 @@ Future<bool> verifyCertificateManually(
/// تحويل المفتاح العام إلى بصمة SHA-256
List<int> sha256Convert(Uint8List der) {
return sha256.convert(der).bytes;
}
}

View File

@@ -1,4 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -8,6 +7,7 @@ import 'package:url_launcher/url_launcher.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../functions/crud.dart';
import '../../onbording_page.dart';
import 'login_controller.dart';
@@ -38,7 +38,9 @@ class GoogleSignInHelper {
// silent login if possible
try {
await _signIn.attemptLightweightAuthentication();
} catch (_) {}
} catch (e) {
Log.print("Error: $e");
}
}
/// ✅ تسجيل دخول عادي
@@ -46,16 +48,14 @@ class GoogleSignInHelper {
try {
final user =
await _signIn.authenticate(scopeHint: const ['email', 'profile']);
if (user != null) {
_lastUser = user;
await _handleSignUp(user);
_lastUser = user;
await _handleSignUp(user);
// اطبع القيم (للتأكد)
print("Google ID: ${user.id}");
print("Email: ${user.email}");
print("Name: ${user.displayName}");
print("Photo: ${user.photoUrl}");
}
// اطبع القيم (للتأكد)
Log.print("Google ID: ${user.id}");
Log.print("Email: ${user.email}");
Log.print("Name: ${user.displayName}");
Log.print("Photo: ${user.photoUrl}");
return user;
} on PlatformException catch (e) {
if (e.code == 'sign_in_required') {

View File

@@ -119,11 +119,11 @@ class PhoneAuthHelper {
if (response != 'failure') {
final data = (response);
// Log.print('data: ${data}');
Log.print('data: ${data}');
if (data['status'] == 'success') {
final isRegistered = data['message']['isRegistered'] ?? false;
// Log.print('isRegistered: ${isRegistered}');
Log.print('isRegistered: ${isRegistered}');
if (isRegistered) {
// ✅ المستخدم موجود مسبقاً -> تسجيل دخول مباشر
@@ -148,18 +148,14 @@ class PhoneAuthHelper {
static Future<void> _addTokens() async {
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
await CRUD()
.post(link: "${AppLink.server}/ride/firebase/add.php", payload: {
'token': (box.read(BoxName.tokenFCM.toString())),
'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint
});
await CRUD()
.post(link: "${AppLink.paymentServer}/ride/firebase/add.php", payload: {
var res = await CRUD()
// this for register and login //
.post(link: "${AppLink.server}/ride/firebase/addToken.php", payload: {
'token': (box.read(BoxName.tokenFCM.toString())),
'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint
});
Log.print('res token: ${res}');
}
static Future<void> registerUser({

View File

@@ -75,7 +75,7 @@ class FirebaseMessagesController extends GetxController {
// 🔹 الاشتراك في topic
await fcmToken
.subscribeToTopic("passengers"); // أو "users" حسب نوع المستخدم
print("Subscribed to 'passengers' topic ✅");
Log.print("Subscribed to 'passengers' topic ✅");
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// If the app is in the background or terminated, show a system tray message
@@ -132,7 +132,7 @@ class FirebaseMessagesController extends GetxController {
// 🔥 فك التشفير: تحويل الـ String إلى Map
driverInfoMap = jsonDecode(rawJson);
} catch (e) {
print("❌ Error decoding FCM driver_info: $e");
Log.print("❌ Error decoding FCM driver_info: $e");
}
}
@@ -209,7 +209,7 @@ class FirebaseMessagesController extends GetxController {
try {
driverList = jsonDecode(rawData) as List<dynamic>;
} catch (e) {
print("❌ Error decoding DriverList: $e");
Log.print("❌ Error decoding DriverList: $e");
}
}
@@ -485,7 +485,7 @@ class FirebaseMessagesController extends GetxController {
// // token: driverList[0].toString(),
// // remoteID: driverList[2].toString(),
// // ));
// } catch (e) {}
// } catch (e) { Log.print("Error occurred: $e"); }
// } else if (message.notification!.title! == 'Call Income from Driver'.tr) {
// try {
// var myListString = message.data['DriverList'];
@@ -504,7 +504,7 @@ class FirebaseMessagesController extends GetxController {
// // token: driverList[0].toString(),
// // remoteID: driverList[2].toString(),
// // ));
// } catch (e) {}
// } catch (e) { Log.print("Error occurred: $e"); }
// } else if (message.notification!.title! == 'Call End'.tr) {
// try {
// var myListString = message.data['DriverList'];
@@ -518,7 +518,7 @@ class FirebaseMessagesController extends GetxController {
// }
// // Assuming GetMaterialApp is initialized and context is valid for navigation
// // Get.off(const CallPage());
// } catch (e) {}
// } catch (e) { Log.print("Error occurred: $e"); }
// } else if (message.notification!.title! == 'Driver Cancelled Your Trip') {
// // Get.snackbar(
// // 'You will be pay the cost to driver or we will get it from you on next trip'
@@ -569,13 +569,13 @@ class FirebaseMessagesController extends GetxController {
snackPosition: SnackPosition.TOP,
titleText: Text(
'Applied'.tr,
style: const TextStyle(color: AppColor.redColor),
style: TextStyle(color: AppColor.redColor),
),
messageText: Text(
'Driver Applied the Ride for You'.tr,
style: AppStyle.title,
),
icon: const Icon(Icons.approval),
icon: Icon(Icons.approval, color: AppColor.primaryColor),
shouldIconPulse: true,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
@@ -659,7 +659,7 @@ class DriverTipWidget extends StatelessWidget {
Toast.show(
context,
'${'Tip is '.tr}${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
AppColor.blueColor);
AppColor.cyanBlue);
controller.update();
},
child: Container(
@@ -678,7 +678,7 @@ class DriverTipWidget extends StatelessWidget {
Toast.show(
context,
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
AppColor.blueColor);
AppColor.cyanBlue);
controller.update();
},
child: Container(
@@ -697,7 +697,7 @@ class DriverTipWidget extends StatelessWidget {
Toast.show(
context,
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
AppColor.blueColor);
AppColor.cyanBlue);
controller.update();
},
child: Container(
@@ -716,7 +716,7 @@ class DriverTipWidget extends StatelessWidget {
Toast.show(
context,
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
AppColor.blueColor);
AppColor.cyanBlue);
controller.update();
},
child: Container(

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -13,7 +14,7 @@ class _LiveActivityScreenState extends State<LiveActivityScreen> {
try {
await platform.invokeMethod('startLiveActivity');
} on PlatformException catch (e) {
print("Failed to start Live Activity: '${e.message}'.");
Log.print("Failed to start Live Activity: '${e.message}'.");
}
}
@@ -21,7 +22,7 @@ class _LiveActivityScreenState extends State<LiveActivityScreen> {
try {
await platform.invokeMethod('updateLiveActivity', {"progress": progress});
} on PlatformException catch (e) {
print("Failed to update Live Activity: '${e.message}'.");
Log.print("Failed to update Live Activity: '${e.message}'.");
}
}
@@ -29,7 +30,7 @@ class _LiveActivityScreenState extends State<LiveActivityScreen> {
try {
await platform.invokeMethod('endLiveActivity');
} on PlatformException catch (e) {
print("Failed to end Live Activity: '${e.message}'.");
Log.print("Failed to end Live Activity: '${e.message}'.");
}
}
@@ -56,4 +57,4 @@ class _LiveActivityScreenState extends State<LiveActivityScreen> {
),
);
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:async';
import 'dart:io';
@@ -38,7 +39,7 @@ class NotificationController extends GetxController {
settings: initializationSettings);
tz.initializeTimeZones();
print('Notifications initialized');
Log.print('Notifications initialized');
}
// Displays a notification with the given title and message
@@ -63,7 +64,7 @@ class NotificationController extends GetxController {
NotificationDetails(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.show(
id: 0, title: title, body: message, notificationDetails: details);
print('Notification shown: $title - $message');
Log.print('Notification shown: $title - $message');
}
// /Users/hamzaaleghwairyeen/development/App/ride 2/lib/controller/firebase/local_notification.dart
@@ -93,9 +94,9 @@ class NotificationController extends GetxController {
// if (Platform.isAndroid) {
// if (await Permission.scheduleExactAlarm.isDenied) {
// if (await Permission.scheduleExactAlarm.request().isGranted) {
// print('SCHEDULE_EXACT_ALARM permission granted');
// Log.print('SCHEDULE_EXACT_ALARM permission granted');
// } else {
// print('SCHEDULE_EXACT_ALARM permission denied');
// Log.print('SCHEDULE_EXACT_ALARM permission denied');
// return;
// }
// }
@@ -116,7 +117,7 @@ class NotificationController extends GetxController {
// day, 20, 0, title, message, details, day * 1000 + 3); // Unique ID
// }
// print('Notifications scheduled successfully for the next 7 days');
// Log.print('Notifications scheduled successfully for the next 7 days');
// }
void scheduleNotificationsForSevenDays(
String title, String message, String tone) async {
@@ -142,9 +143,9 @@ class NotificationController extends GetxController {
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
Log.print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
Log.print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
@@ -180,12 +181,12 @@ class NotificationController extends GetxController {
// Mark this notification ID as scheduled in GetStorage
box.write('notification_$notificationId', true);
} else {
print('Notification with ID $notificationId is already scheduled.');
Log.print('Notification with ID $notificationId is already scheduled.');
}
}
}
print('Notifications scheduled successfully for the next 7 days');
Log.print('Notifications scheduled successfully for the next 7 days');
}
void scheduleNotificationsForTimeSelected(
@@ -212,9 +213,9 @@ class NotificationController extends GetxController {
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
Log.print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
Log.print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
@@ -237,7 +238,7 @@ class NotificationController extends GetxController {
2, // Unique ID for 30-minute before notification
);
print('Notifications scheduled successfully for the time selected');
Log.print('Notifications scheduled successfully for the time selected');
}
Future<void> _scheduleNotificationForTimeVIP(
@@ -262,13 +263,13 @@ class NotificationController extends GetxController {
.subtract(const Duration(minutes: 10))
.isBefore(now)) {
// If the 10 minutes before the scheduled time is in the past, don't schedule
print(
Log.print(
'Scheduled time minus 10 minutes is in the past. Skipping notification.');
return; // Skip this notification
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledTZDateTime');
Log.print('Current time (Cairo): $now');
Log.print('Scheduling notification for: $scheduledTZDateTime');
await _flutterLocalNotificationsPlugin.zonedSchedule(
id: notificationId, // Unique ID for each notification
@@ -283,7 +284,7 @@ class NotificationController extends GetxController {
null, // Don't repeat automatically; we handle manually
);
print('Notification scheduled successfully for: $scheduledTZDateTime');
Log.print('Notification scheduled successfully for: $scheduledTZDateTime');
}
Future<void> _scheduleNotificationForTime(
@@ -314,8 +315,8 @@ class NotificationController extends GetxController {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledDate');
Log.print('Current time (Cairo): $now');
Log.print('Scheduling notification for: $scheduledDate');
await _flutterLocalNotificationsPlugin.zonedSchedule(
id: notificationId, // Unique ID for each notification
@@ -330,6 +331,6 @@ class NotificationController extends GetxController {
null, // Don't repeat automatically; we handle 7 days manually
);
print('Notification scheduled successfully for: $scheduledDate');
Log.print('Notification scheduled successfully for: $scheduledDate');
}
}
}

View File

@@ -1,6 +1,8 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:get/get.dart'; // للترجمة .tr
import 'package:get/get.dart';
import '../../print.dart'; // للترجمة .tr
class NotificationService {
static const String _serverUrl =
@@ -50,14 +52,15 @@ class NotificationService {
);
if (response.statusCode == 200) {
print('✅ Notification sent successfully.');
// print('Response: ${response.body}');
Log.print('✅ Notification sent successfully.');
// Log.print('Response: ${response.body}');
} else {
print('❌ Failed to send notification. Code: ${response.statusCode}');
print('Error Body: ${response.body}');
Log.print(
'❌ Failed to send notification. Code: ${response.statusCode}');
Log.print('Error Body: ${response.body}');
}
} catch (e) {
print('❌ Error sending notification: $e');
Log.print('❌ Error sending notification: $e');
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:io';
// import 'package:flutter_sound/flutter_sound.dart';
@@ -24,7 +25,7 @@ class AudioRecorderController extends GetxController {
await audioPlayer.setAsset(sound);
audioPlayer.play();
} catch (e) {
print("Error playing sound: $e");
Log.print("Error playing sound: $e");
}
}
@@ -163,4 +164,4 @@ class AudioRecorderController extends GetxController {
recorder.dispose();
super.onClose();
}
}
}

View File

@@ -65,7 +65,9 @@ class CRUD {
'details': details,
},
);
} catch (e) {}
} catch (e) {
Log.print("Error occurred: $e");
}
}
// ─────────────────────────────────────────────────────────────
@@ -414,7 +416,9 @@ class CRUD {
} else {
String errorBody = await response.stream.bytesToString();
}
} catch (e) {}
} catch (e) {
Log.print("Error occurred: $e");
}
}
Future<dynamic> getAgoraToken({
@@ -558,7 +562,7 @@ class CRUD {
var request = http.Request('POST', Uri.parse(link));
request.bodyFields = payload!;
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
await request.send();
}
Future<dynamic> postFromDialogue({
@@ -596,7 +600,7 @@ class CRUD {
final Uri verificationUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/Verifications');
final response = await http.post(
await http.post(
verificationUri,
headers: {
'Authorization':
@@ -647,18 +651,26 @@ class CRUD {
}
}
Future<dynamic> delete({
required String endpoint,
required String id,
Future<dynamic> getMapSaas({
required String link,
}) async {
var url = Uri.parse('$endpoint/$id');
var response = await http.delete(
url,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
return json.decode(response.body);
var url = Uri.parse(link);
try {
var response = await http.get(
url,
headers: {
'Content-Type': 'application/json',
'x-api-key': Env.mapSaasKey,
},
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
}
Log.print('MapSaas Error: ${response.statusCode} - ${response.body}');
return null;
} catch (e) {
Log.print('MapSaas Exception: $e');
return null;
}
}
}

View File

@@ -78,7 +78,7 @@ class DeviceInfoPlus {
};
deviceDataList.add(deviceData);
}
} catch (e) {}
} catch (e) { Log.print("Error occurred: $e"); }
return deviceDataList;
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
@@ -41,7 +42,7 @@ Future<void> makePhoneCall(String phoneNumber) async {
}
} catch (e) {
// طباعة الخطأ في حال الفشل التام
print("Error launching call: $e");
Log.print("Error launching call: $e");
}
}
@@ -104,4 +105,4 @@ void launchCommunication(
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {}
}
}

View File

@@ -20,7 +20,7 @@ Future<void> checkForUpdate(BuildContext context) async {
final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.buildNumber;
final version = packageInfo.version;
print('currentVersion is : $currentVersion');
Log.print('currentVersion is : $currentVersion');
// Fetch the latest version from your server
String latestVersion = box.read(BoxName.package);
box.write(BoxName.packagInfo, version);
@@ -238,11 +238,11 @@ class SecurityHelper {
debugPrint("checkForIssues: $checkForIssues");
debugPrint("isDevMode: $isDevMode");
debugPrint("isTampered: $isTampered");
debugPrint("Bundle ID: $bundleId"); // Print the bundle ID
debugPrint("Bundle ID: $bundleId"); //Log.print the bundle ID
// Check for security risks and potentially show a warning
if (isJailBroken || isRealDevice == false || isTampered) {
// print("security_warning".tr); //using easy_localization
// Log.print("security_warning".tr); //using easy_localization
// Use a more robust approach to show a warning, like a dialog:
_showSecurityWarning();
} else {
@@ -309,7 +309,7 @@ class SecurityHelper {
await storage.deleteAll();
await box.erase();
exit(0); // Exit the app
print('exit');
Log.print('exit');
}
}
@@ -349,7 +349,7 @@ class DeviceHelper {
final String encryptedFp =
EncryptionHelper.instance.encryptData(fingerprint);
box.write(BoxName.deviceFpEncrypted, encryptedFp);
// print(EncryptionHelper.instance.encryptData(fingerprint));
//Log.print(EncryptionHelper.instance.encryptData(fingerprint));
return encryptedFp;
} catch (e) {
throw Exception('Failed to generate device fingerprint');

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -15,7 +16,7 @@ class SecurityChecks {
.invokeMethod('isNativeRooted'); // Invoke the native method
return result;
} on PlatformException catch (e) {
print("Failed to check security status: ${e.message}");
Log.print("Failed to check security status: ${e.message}");
return true; // Treat platform errors as a compromised device (for safety)
}
}
@@ -46,7 +47,7 @@ class SecurityChecks {
box.write(BoxName.security_check, 'passed');
print("Device is secure.");
Log.print("Device is secure.");
}
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:async';
import 'package:app_links/app_links.dart';
import 'package:get/get.dart';
@@ -18,7 +19,7 @@ class DeepLinkController extends GetxController {
Future<void> initDeepLinks() async {
// الاستماع للروابط والتطبيق يعمل في الخلفية
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
print('🔗 Received deep link (Stream): $uri');
Log.print('🔗 Received deep link (Stream): $uri');
rawDeepLink.value = uri.toString();
});
@@ -26,11 +27,11 @@ class DeepLinkController extends GetxController {
try {
final initialUri = await _appLinks.getInitialLink();
if (initialUri != null) {
print('🔗 Received initial deep link (Cold Start): $initialUri');
Log.print('🔗 Received initial deep link (Cold Start): $initialUri');
rawDeepLink.value = initialUri.toString();
}
} catch (e) {
print('Error getting initial link: $e');
Log.print('Error getting initial link: $e');
}
}
@@ -39,4 +40,4 @@ class DeepLinkController extends GetxController {
_linkSubscription?.cancel();
super.onClose();
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:io';
import 'dart:convert';
import 'package:live_activities/live_activities.dart';
@@ -41,7 +42,7 @@ class IosLiveActivityService {
activityModel,
);
} catch (e) {
print("❌ Live Activity Start Error: $e");
Log.print("❌ Live Activity Start Error: $e");
}
}
@@ -70,4 +71,4 @@ class IosLiveActivityService {
await _liveActivitiesPlugin.endActivity(_activityId!);
_activityId = null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -129,9 +129,9 @@ ${'Download the Intaleq app now and enjoy your ride!'.tr}
// **IMPROVEMENT**: Provide feedback if some contacts were filtered out.
if (contactsWithPhones < totalContactsOnDevice) {
Get.snackbar('Contacts Loaded'.tr,
'${'Showing'.tr} $contactsWithPhones ${'of'.tr} $totalContactsOnDevice ${'contacts. Others were hidden because they don\'t have a phone number.'.tr}',
snackPosition: SnackPosition.BOTTOM);
// Get.snackbar('Contacts Loaded'.tr,
// '${'Showing'.tr} $contactsWithPhones ${'of'.tr} $totalContactsOnDevice ${'contacts. Others were hidden because they don\'t have a phone number.'.tr}',
// snackPosition: SnackPosition.BOTTOM);
}
} else {
Get.snackbar('No contacts found'.tr,

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:async';
import 'dart:math';
import 'package:Intaleq/controller/functions/crud.dart';
@@ -169,7 +170,7 @@ class SplashScreenController extends GetxController
box.write(BoxName.packagInfo, info.version);
update();
} catch (e) {
print("Could not get package info: $e");
Log.print("Could not get package info: $e");
}
}
@@ -225,4 +226,4 @@ class SplashScreenController extends GetxController
_glowController.dispose();
super.onClose();
}
}
}

View File

@@ -9,96 +9,46 @@ class LocaleController extends GetxController {
Locale? language;
String countryCode = '';
ThemeData appTheme = lightThemeEnglish;
ThemeMode themeMode = ThemeMode.system;
void changeLang(String langcode) {
Locale locale;
switch (langcode) {
case "ar":
locale = const Locale("ar");
appTheme = lightThemeArabic;
box.write(BoxName.lang, 'ar');
break;
case "ar-main":
locale = const Locale("ar-main");
appTheme = lightThemeArabic;
box.write(BoxName.lang, 'ar-main');
break;
case "en":
locale = const Locale("en");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'en');
break;
case "tr":
locale = const Locale("tr");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'tr');
break;
case "fr":
locale = const Locale("fr");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'fr');
break;
case "it":
locale = const Locale("it");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'it');
break;
case "de":
locale = const Locale("de");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'de');
break;
case "el":
locale = const Locale("el");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'el');
break;
case "es":
locale = const Locale("es");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'es');
break;
case "fa":
locale = const Locale("fa");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'fa');
break;
case "zh":
locale = const Locale("zh");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'zh');
break;
case "ru":
locale = const Locale("ru");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'ru');
break;
case "hi":
locale = const Locale("hi");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'hi');
break;
case "ar-ma":
locale = const Locale("ar-ma");
appTheme = lightThemeArabic;
box.write(BoxName.lang, 'ar-ma');
break;
case "ar-gulf":
locale = const Locale("ar-gulf");
appTheme = lightThemeArabic;
box.write(BoxName.lang, 'ar-gulf');
break;
default:
locale = Locale(Get.deviceLocale!.languageCode);
box.write(BoxName.lang, Get.deviceLocale!.languageCode);
appTheme = lightThemeEnglish;
break;
Locale newLocale;
ThemeData newTheme;
bool isArabic = langcode.startsWith('ar');
if (isArabic) {
newLocale = const Locale("ar");
newTheme = Get.isDarkMode ? darkThemeArabic : lightThemeArabic;
} else {
newLocale = const Locale("en");
newTheme = Get.isDarkMode ? darkThemeEnglish : lightThemeEnglish;
}
box.write(BoxName.lang, langcode);
Get.changeTheme(appTheme);
Get.updateLocale(locale);
language = newLocale;
Get.changeTheme(newTheme);
Get.updateLocale(newLocale);
update();
}
void changeThemeMode(ThemeMode mode) {
themeMode = mode;
Get.changeThemeMode(mode);
// Explicitly update ThemeData to ensure immediate font and color changes
bool goDark = mode == ThemeMode.dark ||
(mode == ThemeMode.system && Get.isPlatformDarkMode);
bool isArabic = (language?.languageCode ?? 'en').startsWith('ar');
ThemeData newTheme;
if (isArabic) {
newTheme = goDark ? darkThemeArabic : lightThemeArabic;
} else {
newTheme = goDark ? darkThemeEnglish : lightThemeEnglish;
}
Get.changeTheme(newTheme);
box.write(BoxName.themeMode, mode.toString());
update();
}
@@ -111,95 +61,18 @@ class LocaleController extends GetxController {
box.write(BoxName.lang, storedLang);
}
String? storedTheme = box.read(BoxName.themeMode);
if (storedTheme != null) {
if (storedTheme == ThemeMode.light.toString()) {
themeMode = ThemeMode.light;
} else if (storedTheme == ThemeMode.dark.toString()) {
themeMode = ThemeMode.dark;
} else {
themeMode = ThemeMode.system;
}
}
changeLang(storedLang);
super.onInit();
}
}
// class LocaleController extends GetxController {
// Locale? language;
// String countryCode = '';
// void restartApp() {
// runApp(const MyApp());
// }
// ThemeData appTheme = themeEnglish;
// changeLang(String langcode) {
// Locale locale;
// switch (langcode) {
// case "ar":
// locale = const Locale("ar");
// appTheme = themeArabic;
// break;
// case "en":
// locale = const Locale("en");
// appTheme = themeEnglish;
// break;
// case "tr":
// locale = const Locale("tr");
// appTheme = themeEnglish;
// break;
// case "fr":
// locale = const Locale("fr");
// appTheme = themeEnglish;
// break;
// case "it":
// locale = const Locale("it");
// appTheme = themeEnglish;
// break;
// case "de":
// locale = const Locale("de");
// appTheme = themeEnglish;
// break;
// case "el":
// locale = const Locale("el");
// appTheme = themeEnglish;
// break;
// case "es":
// locale = const Locale("es");
// appTheme = themeEnglish;
// break;
// case "fa":
// locale = const Locale("fa");
// appTheme = themeArabic;
// break;
// case "zh":
// locale = const Locale("zh");
// appTheme = themeEnglish;
// break;
// case "ru":
// locale = const Locale("ru");
// appTheme = themeEnglish;
// break;
// case "hi":
// locale = const Locale("hi");
// appTheme = themeEnglish;
// break;
// default:
// locale = Locale(Get.deviceLocale!.languageCode);
// appTheme = themeEnglish;
// break;
// }
// box.write(BoxName.lang, langcode);
// Get.changeTheme(appTheme);
// Get.updateLocale(locale);
// restartApp();
// update();
// }
// @override
// void onInit() {
// String? storedLang = box.read(BoxName.lang);
// if (storedLang == null) {
// // Use device language if no language is stored
// storedLang = Get.deviceLocale!.languageCode;
// box.write(BoxName.lang, storedLang);
// }
// changeLang(storedLang);
// super.onInit();
// }
// }

View File

@@ -1,5 +1,6 @@
library intl_phone_field;
import 'package:Intaleq/print.dart';
import 'dart:async';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
@@ -77,7 +78,7 @@ class IntlPhoneField extends StatefulWidget {
/// to the [focusNode]:
///
/// ```dart
/// focusNode.addListener(() { print(myFocusNode.hasFocus); });
/// focusNode.addListener(() { Log.print(myFocusNode.hasFocus); });
/// ```
///
/// If null, this widget will create its own [FocusNode].
@@ -518,4 +519,4 @@ class _IntlPhoneFieldState extends State<IntlPhoneField> {
enum IconPosition {
leading,
trailing,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -295,8 +295,8 @@ class PaymentController extends GetxController {
)),
allowsDelayedPaymentMethods: true,
customerEphemeralKeySecret: Stripe.merchantIdentifier,
appearance: const PaymentSheetAppearance(
shapes: PaymentSheetShape(borderRadius: 12),
appearance: PaymentSheetAppearance(
shapes: const PaymentSheetShape(borderRadius: 12),
colors: PaymentSheetAppearanceColors(
background: AppColor.secondaryColor,
),
@@ -691,8 +691,8 @@ class PaymentController extends GetxController {
// final passengerID = box.read(BoxName.passengerID).toString();
// final formattedAmount = double.parse(amount).toStringAsFixed(0);
// print("🚀 بدء عملية دفع MTN");
// print(
// Log.print("🚀 بدء عملية دفع MTN");
// Log.print(
// "📦 Payload: passengerID: $passengerID, amount: $formattedAmount, phone: $phone");
// // التحقق بالبصمة (اختياري) + حماية من الـ await
@@ -704,7 +704,7 @@ class PaymentController extends GetxController {
// );
// if (!didAuth) {
// if (Get.isDialogOpen == true) Get.back();
// print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
// Log.print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
// return;
// }
// }
@@ -720,8 +720,8 @@ class PaymentController extends GetxController {
// },
// );
// // print("✅ استجابة الخادم (mtn_start_payment.php):");
// // print(responseData);
// // Log.print("✅ استجابة الخادم (mtn_start_payment.php):");
// // Log.print(responseData);
// Log.print('responseData: ${responseData}');
// // فحص الاستجابة بقوة
@@ -745,7 +745,7 @@ class PaymentController extends GetxController {
// final operationNumber = messageData["operationNumber"].toString();
// final guid = messageData["guid"].toString();
// // print(
// // Log.print(
// // "📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid");
// // أغلق السبينر قبل إظهار حوار OTP
@@ -780,10 +780,10 @@ class PaymentController extends GetxController {
// ).then((res) => otpInput = (res ?? "") as String);
// if (otpInput.isEmpty) {
// print("❌ لم يتم إدخال OTP");
// Log.print("❌ لم يتم إدخال OTP");
// return;
// }
// print("🔐 تم إدخال OTP: $otpInput");
// Log.print("🔐 تم إدخال OTP: $otpInput");
// // سبينر أثناء التأكيد
// Get.dialog(const Center(child: CircularProgressIndicator()),
@@ -804,7 +804,7 @@ class PaymentController extends GetxController {
// if (Get.isDialogOpen == true) Get.back();
// // print("✅ استجابة mtn_confirm.php:");
// // Log.print("✅ استجابة mtn_confirm.php:");
// // Log.print('confirmRes: ${confirmRes}');
// final ok = (confirmRes is Map && confirmRes['status'] == 'success');
@@ -820,9 +820,9 @@ class PaymentController extends GetxController {
// Get.defaultDialog(title: "❌ فشل", content: Text(errorMsg.tr));
// }
// } catch (e, s) {
// print("🔥 خطأ أثناء الدفع عبر MTN:");
// print(e);
// print(s);
// Log.print("🔥 خطأ أثناء الدفع عبر MTN:");
// Log.print(e);
// Log.print(s);
// if (Get.isDialogOpen == true) Get.back();
// Get.defaultDialog(
// title: 'حدث خطأ',
@@ -853,8 +853,8 @@ class PaymentController extends GetxController {
final passengerId = box.read(BoxName.passengerID).toString();
final formattedAmount = double.parse(amount).toStringAsFixed(0);
print("🚀 Syriatel payment start");
print(
Log.print("🚀 Syriatel payment start");
Log.print(
"📦 Payload => passengerId:$passengerId amount:$formattedAmount phone:$phone");
// مصادقة حيوية (اختياري)
@@ -865,7 +865,7 @@ class PaymentController extends GetxController {
);
if (!ok) {
_closeAnyDialog();
print("❌ User did not authenticate");
Log.print("❌ User did not authenticate");
return;
}
}
@@ -881,7 +881,7 @@ class PaymentController extends GetxController {
},
);
print("✅ Server response (start): $startRaw");
Log.print("✅ Server response (start): $startRaw");
// تحويل الاستجابة إلى Map
late final Map<String, dynamic> startRes;
@@ -901,7 +901,7 @@ class PaymentController extends GetxController {
final messageData = startRes['message'] as Map<String, dynamic>;
final transactionID = messageData['transactionID'].toString();
print("📄 transactionID: $transactionID");
Log.print("📄 transactionID: $transactionID");
//
// 2) اطلب من المستخدم إدخال OTP عبر Get.dialog (بدون context)
_closeAnyDialog(); // أغلق اللودينغ أولاً
@@ -929,10 +929,10 @@ class PaymentController extends GetxController {
);
if (otp == null || otp.isEmpty) {
print("❌ OTP not provided");
Log.print("❌ OTP not provided");
return;
}
print("🔐 OTP: $otp");
Log.print("🔐 OTP: $otp");
await _showLoading();
@@ -947,7 +947,7 @@ class PaymentController extends GetxController {
_closeAnyDialog(); // أغلق اللودينغ
print("✅ Response (confirm): $confirmRaw");
Log.print("✅ Response (confirm): $confirmRaw");
late final Map<String, dynamic> confirmRes;
if (confirmRaw is Map<String, dynamic>) {
@@ -971,7 +971,7 @@ class PaymentController extends GetxController {
);
}
} catch (e, s) {
print("🔥 Error during Syriatel Wallet payment:\n$e\n$s");
Log.print("🔥 Error during Syriatel Wallet payment:\n$e\n$s");
_closeAnyDialog();
Get.defaultDialog(
title: 'حدث خطأ',
@@ -1013,7 +1013,7 @@ class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) async {
print('Ecash Driver WebView URL Finished: $url');
Log.print('Ecash Driver WebView URL Finished: $url');
await Get.find<PaymentController>().getPassengerWallet();
// هنا نتحقق فقط من أن المستخدم عاد إلى صفحة النجاح
// لا حاجة لاستدعاء أي API هنا، فالـ Webhook يقوم بكل العمل

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
@@ -23,7 +24,7 @@ class _EcashPaymentScreenState extends State<EcashPaymentScreen> {
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (url) {
print('Ecash WebView URL Finished: $url');
Log.print('Ecash WebView URL Finished: $url');
// ✅ هنا نتحقق فقط من أن المستخدم عاد إلى صفحة النجاح
// هذه الصفحة هي التي حددناها في `APP_REDIRECT_URL_SUCCESS` في ملف PHP
@@ -96,4 +97,4 @@ class _EcashPaymentScreenState extends State<EcashPaymentScreen> {
body: WebViewWidget(controller: _controller),
);
}
}
}

View File

@@ -42,36 +42,34 @@ ThemeData lightThemeEnglish = ThemeData(
ThemeData darkThemeEnglish = ThemeData(
brightness: Brightness.dark,
fontFamily: "SFPro",
primaryColor: AppColor.primaryColor,
scaffoldBackgroundColor: const Color(0xFF121212),
cardColor: const Color(0xFF1E1E1E),
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
displaySmall: AppStyle.title.copyWith(color: Colors.white),
displayLarge: AppStyle.headTitle.copyWith(color: Colors.white),
displayMedium: AppStyle.headTitle2.copyWith(color: Colors.white),
bodyLarge: AppStyle.title.copyWith(color: Colors.white70),
bodyMedium: AppStyle.subtitle.copyWith(color: Colors.white60),
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
primarySwatch: Colors.green,
dialogTheme: const DialogThemeData(
backgroundColor: Color(0xFF1E1E1E),
contentTextStyle: TextStyle(color: Colors.white),
titleTextStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
appBarTheme: AppBarTheme(
appBarTheme: const AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
color: Color(0xFF121212),
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
iconTheme: IconThemeData(
color: Colors.white,
),
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);
@@ -114,35 +112,33 @@ ThemeData lightThemeArabic = ThemeData(
ThemeData darkThemeArabic = ThemeData(
brightness: Brightness.dark,
fontFamily: 'SFArabic',
primaryColor: AppColor.primaryColor,
scaffoldBackgroundColor: const Color(0xFF121212),
cardColor: const Color(0xFF1E1E1E),
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
displaySmall: AppStyle.title.copyWith(color: Colors.white),
displayLarge: AppStyle.headTitle.copyWith(color: Colors.white),
displayMedium: AppStyle.headTitle2.copyWith(color: Colors.white),
bodyLarge: AppStyle.title.copyWith(color: Colors.white70),
bodyMedium: AppStyle.subtitle.copyWith(color: Colors.white60),
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
primarySwatch: Colors.green,
dialogTheme: const DialogThemeData(
backgroundColor: Color(0xFF1E1E1E),
contentTextStyle: TextStyle(color: Colors.white),
titleTextStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
appBarTheme: AppBarTheme(
appBarTheme: const AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
color: Color(0xFF121212),
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
iconTheme: IconThemeData(
color: Colors.white,
),
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);

4
lib/env/env.dart vendored
View File

@@ -2,7 +2,7 @@ import 'package:envied/envied.dart';
part 'env.g.dart';
@Envied()
@Envied(path: '.env')
abstract class Env {
@EnviedField(varName: 'basicAuthCredentials', obfuscate: true)
static final String basicAuthCredentials = _Env.basicAuthCredentials;
@@ -339,4 +339,6 @@ abstract class Env {
@EnviedField(varName: 'keyOfApp', obfuscate: true)
static final String keyOfApp = _Env.keyOfApp;
@EnviedField(varName: 'mapSaasKey', obfuscate: true)
static final String mapSaasKey = _Env.mapSaasKey;
}

26238
lib/env/env.g.dart vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:flutter/services.dart';
class WidgetManager {
@@ -7,7 +8,7 @@ class WidgetManager {
try {
await platform.invokeMethod('updateWidget');
} on PlatformException catch (e) {
print("Failed to update widget: '${e.message}'.");
Log.print("Failed to update widget: '${e.message}'.");
}
}
}
@@ -15,4 +16,4 @@ class WidgetManager {
// Example usage:
void updateHomeScreenWidget() {
WidgetManager.updateWidget();
}
}

View File

@@ -20,8 +20,10 @@ import 'constant/info.dart';
import 'controller/home/ios_live_activity_service.dart';
import 'controller/local/local_controller.dart';
import 'controller/local/translations.dart';
import 'controller/themes/themes.dart';
import 'firebase_options.dart';
import 'models/db_sql.dart';
import 'print.dart';
import 'splash_screen_page.dart';
// -- Global instances for easy access --
@@ -33,7 +35,7 @@ DbSql sql = DbSql.instance;
@pragma('vm:entry-point')
Future<void> backgroundMessageHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
print("Handling a background message: ${message.messageId}");
Log.print("Handling a background message: ${message.messageId}");
}
void main() {
@@ -89,7 +91,13 @@ class MyApp extends StatelessWidget {
translations: MyTranslation(),
debugShowCheckedModeBanner: false,
locale: localController.language,
theme: localController.appTheme,
theme: localController.language?.languageCode.startsWith('ar') == true
? lightThemeArabic
: lightThemeEnglish,
darkTheme: localController.language?.languageCode.startsWith('ar') == true
? darkThemeArabic
: darkThemeEnglish,
themeMode: localController.themeMode,
// --- [CRITICAL] ---
// initialBinding tells GetX to run AppBindings once at the start.

View File

@@ -1,146 +1,139 @@
import 'dart:convert';
import 'package:Intaleq/constant/table_names.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:Intaleq/main.dart';
class DbSql {
static final DbSql instance = DbSql._();
static Database? _database;
DbSql._();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
// Helper to read data as a list of maps from GetStorage
List<Map<String, dynamic>> _readTable(String table) {
String? dataString = box.read(table);
if (dataString == null) return [];
try {
List<dynamic> parsed = jsonDecode(dataString);
return parsed.map((e) => Map<String, dynamic>.from(e)).toList();
} catch (e) {
return [];
}
}
Future<Database> _initDatabase() async {
String path = join(await getDatabasesPath(), 'my_database.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.carLocations}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
driver_id TEXT,
latitude REAL,
longitude REAL,
created_at TEXT,
updated_at TEXT
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.placesFavorite}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
latitude REAL,
longitude REAL,
name TEXT UNIQUE,
rate TEXT,
createdAt TEXT
)
''');
// await db.execute('DROP TABLE IF EXISTS ${TableName.recentLocations}');
await db.execute('''
CREATE TABLE ${TableName.recentLocations}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
latitude REAL,
longitude REAL,
name TEXT,
rate TEXT,
createdAt TEXT
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.driverOrdersRefuse}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT UNIQUE,
created_at TEXT,
driver_id TEXT
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.rideLocation}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT ,
created_at TEXT,
lat TEXT,
lng TEXT
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.faceDetectTimes}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
faceDetectTimes INTEGER
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.captainNotification}(
id INTEGER PRIMARY KEY AUTOINCREMENT,
faceDetectTimes INTEGER
)
''');
},
);
// Helper to write data back to GetStorage
void _writeTable(String table, List<Map<String, dynamic>> data) {
box.write(table, jsonEncode(data));
}
Future<List<Map<String, dynamic>>> getAllData(String table) async {
Database db = await instance.database;
return await db.query(table);
return _readTable(table);
}
Future<List<Map<String, dynamic>>> getCustomQuery(String query) async {
Database db = await instance.database;
return await db.rawQuery(query);
String q = query.toLowerCase();
String targetTable = TableName.recentLocations; // Default target
// Determine the table
if (q.contains(TableName.recentLocations.toLowerCase())) {
targetTable = TableName.recentLocations;
} else if (q.contains(TableName.carLocations.toLowerCase())) {
targetTable = TableName.carLocations;
} else if (q.contains(TableName.placesFavorite.toLowerCase())) {
targetTable = TableName.placesFavorite;
}
List<Map<String, dynamic>> data = _readTable(targetTable);
// Apply DISTINCT logic if needed based on query
if (q.contains('distinct latitude, longitude, name, rate')) {
// Manual distinct by name and location
Map<String, Map<String, dynamic>> distinctMap = {};
for (var item in data) {
String key = '${item['latitude']}_${item['longitude']}_${item['name']}';
if (!distinctMap.containsKey(key)) {
distinctMap[key] = item;
}
}
data = distinctMap.values.toList();
}
// Apply ORDER BY createdAt DESC logic
if (q.contains('order by createdat desc')) {
data.sort((a, b) {
String dateA = a['createdAt'] ?? '';
String dateB = b['createdAt'] ?? '';
return dateB.compareTo(dateA);
});
}
return data;
}
Future<int> insertData(Map<String, dynamic> map, String table) async {
Database db = await instance.database;
return await db.insert(table, map);
List<Map<String, dynamic>> data = _readTable(table);
// Generate simple ID
int newId = data.isEmpty ? 1 : (data.last['id'] as int? ?? 0) + 1;
Map<String, dynamic> newMap = Map<String, dynamic>.from(map);
newMap['id'] = newId;
data.add(newMap);
_writeTable(table, data);
return newId;
}
Future<int> insertMapLocation(Map<String, dynamic> map, String table) async {
Database db = await instance.database;
// Check if the record already exists (based on latitude, longitude, and name)
var existing = await db.query(
table,
where: 'latitude = ? AND longitude = ? AND name = ?',
whereArgs: [map['latitude'], map['longitude'], map['name']],
);
if (existing.isNotEmpty) {
// If record exists, update the createdAt field with the current timestamp
var updatedMap = Map<String, dynamic>.from(map);
updatedMap['createdAt'] =
DateTime.now().toIso8601String(); // Update timestamp
return await db.update(
table,
updatedMap,
where: 'id = ?',
whereArgs: [existing.first['id']], // Update the existing row
);
List<Map<String, dynamic>> data = _readTable(table);
// Check if exists
int existingIndex = data.indexWhere((element) =>
element['latitude'] == map['latitude'] &&
element['longitude'] == map['longitude'] &&
element['name'] == map['name']);
if (existingIndex != -1) {
// Update
data[existingIndex] = Map<String, dynamic>.from(data[existingIndex]);
data[existingIndex]['createdAt'] = DateTime.now().toIso8601String();
for (var key in map.keys) {
if (key != 'latitude' && key != 'longitude' && key != 'name') {
data[existingIndex][key] = map[key];
}
}
_writeTable(table, data);
return data[existingIndex]['id'] as int? ?? 1;
} else {
// If record doesn't exist, insert new record with the current timestamp
map['createdAt'] = DateTime.now().toIso8601String();
return await db.insert(table, map);
// Insert
int newId = data.isEmpty ? 1 : ((data.last['id'] as int?) ?? 0) + 1;
Map<String, dynamic> newMap = Map<String, dynamic>.from(map);
newMap['id'] = newId;
newMap['createdAt'] = DateTime.now().toIso8601String();
data.add(newMap);
_writeTable(table, data);
return newId;
}
}
Future<int> updateData(Map<String, dynamic> map, String table, int id) async {
Database db = await instance.database;
return await db.update(table, map, where: 'id = ?', whereArgs: [id]);
List<Map<String, dynamic>> data = _readTable(table);
int index = data.indexWhere((element) => element['id'] == id);
if (index != -1) {
data[index] = {...data[index], ...map};
_writeTable(table, data);
return 1;
}
return 0;
}
Future<int> deleteData(String table, int id) async {
Database db = await instance.database;
return await db.delete(table, where: 'id = ?', whereArgs: [id]);
List<Map<String, dynamic>> data = _readTable(table);
int initialLength = data.length;
data.removeWhere((element) => element['id'] == id);
if (data.length < initialLength) {
_writeTable(table, data);
return 1;
}
return 0;
}
Future<int> deleteAllData(String table) async {
Database db = await instance.database;
return await db.delete(table);
_writeTable(table, []);
return 1;
}
}

View File

@@ -4,7 +4,7 @@ class Log {
Log._();
static void print(String value, {StackTrace? stackTrace}) {
// developer.log(value, name: 'LOG', stackTrace: stackTrace);
developer.log(value, name: 'LOG', stackTrace: stackTrace);
}
static Object? inspect(Object? object) {

View File

@@ -0,0 +1,81 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:sensors_plus/sensors_plus.dart';
import '../print.dart';
class EmergencySignalService {
static final EmergencySignalService instance = EmergencySignalService._();
EmergencySignalService._();
StreamSubscription<AccelerometerEvent>? _accelerometerSubscription;
DateTime? _lastShakeTime;
int _shakeCount = 0;
// Custom thresholds for shaking (force required)
final double _shakeThresholdGravity = 2.7;
final int _shakeSlopTimeMs = 500;
final int _shakeCountResetTimeMs = 3000;
final int _targetShakes = 5;
VoidCallback? _onEmergencyTriggered;
/// Starts listening to phone movement
void startListening(VoidCallback onEmergencyTriggered) {
_onEmergencyTriggered = onEmergencyTriggered;
if (_accelerometerSubscription != null) return;
_accelerometerSubscription = accelerometerEvents.listen((event) {
double x = event.x;
double y = event.y;
double z = event.z;
// Calculate the gForce using pythagorean theorem
double gX = x / 9.80665;
double gY = y / 9.80665;
double gZ = z / 9.80665;
// Overall gForce
double gForce = sqrt(gX * gX + gY * gY + gZ * gZ);
if (gForce > _shakeThresholdGravity) {
final now = DateTime.now();
// Ignore shakes that are too close to each other
if (_lastShakeTime != null &&
now.difference(_lastShakeTime!).inMilliseconds < _shakeSlopTimeMs) {
return;
}
// Reset the counter if elapsed more than the reset window
if (_lastShakeTime != null &&
now.difference(_lastShakeTime!).inMilliseconds >
_shakeCountResetTimeMs) {
_shakeCount = 0;
}
_lastShakeTime = now;
_shakeCount++;
if (kDebugMode) {
Log.print("🚨 Shake detected! Count: $_shakeCount");
}
if (_shakeCount >= _targetShakes) {
_shakeCount = 0; // Reset counter
if (_onEmergencyTriggered != null) {
_onEmergencyTriggered!();
}
}
}
});
}
void stopListening() {
_accelerometerSubscription?.cancel();
_accelerometerSubscription = null;
_shakeCount = 0;
}
}

View File

@@ -0,0 +1,106 @@
import 'dart:io';
import 'package:get/get.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:get_storage/get_storage.dart';
import 'dart:math' as math;
import '../../main.dart';
import '../print.dart';
class OfflineMapService {
static final OfflineMapService instance = OfflineMapService._();
OfflineMapService._();
final _offlineRegionName = "UserRegion";
bool _isDownloading = false;
LatLng? _lastDownloadedCenter;
/// Calculate bounding box for a given center and radius in km
LatLngBounds _calculateBounds(LatLng center, double radiusKm) {
const double earthRadius = 6371.0;
// Latitude degrees per km
double latDelta = (radiusKm / earthRadius) * (180 / math.pi);
// Longitude degrees per km at given latitude
double lngDelta = (radiusKm / earthRadius) *
(180 / math.pi) /
math.cos(center.latitude * math.pi / 180);
return LatLngBounds(
southwest:
LatLng(center.latitude - latDelta, center.longitude - lngDelta),
northeast:
LatLng(center.latitude + latDelta, center.longitude + lngDelta),
);
}
/// Downloads a specified radius around a coordinate
Future<void> downloadRegion(LatLng center,
{double radiusKm = 10.0,
double minZoom = 6.0,
double maxZoom = 15.0}) async {
if (_isDownloading) return;
// Avoid re-downloading if the user hasn't moved significantly (e.g. > 5km)
if (_lastDownloadedCenter != null) {
double distance = _calculateDistance(center, _lastDownloadedCenter!);
if (distance < 5.0) return; // skip if close to previously downloaded
}
_isDownloading = true;
try {
final bounds = _calculateBounds(center, radiusKm);
// Select style based on current theme
final String styleStr =
Get.isDarkMode ? "assets/style_dark.json" : "assets/style.json";
// iOS native crash guard: MLNTilePyramidOfflineRegion does not support relative asset URLs.
// We skip native offline registration on iOS if using local assets to ensure stability.
if (Platform.isIOS && !styleStr.startsWith('http')) {
Log.print(
" Skipping native offline registration on iOS for asset-based style to prevent crash.");
return;
}
final regionDefinition = OfflineRegionDefinition(
bounds: bounds,
mapStyleUrl: styleStr,
minZoom: minZoom,
maxZoom: maxZoom,
);
// We'll update the last downloaded center immediately
_lastDownloadedCenter = center;
// MapLibre standard API for offline downloads
await downloadOfflineRegion(regionDefinition, metadata: {
'name': '$_offlineRegionName-${center.latitude}-${center.longitude}',
'downloadDate': DateTime.now().toIso8601String(),
});
// Reassurance log for the user
Log.print("📍 Map Ready: Service is utilizing local tile cache.");
Log.print(
"✅ Offline Map Cached for Region: $center (radius: ${radiusKm}km, style: $styleStr)");
} catch (e) {
Log.print("⚠️ Offline Map Download Failed: $e");
} finally {
_isDownloading = false;
}
}
/// Helper to calculate distance in km
double _calculateDistance(LatLng p1, LatLng p2) {
var p = 0.017453292519943295;
var c = math.cos;
var a = 0.5 -
c((p2.latitude - p1.latitude) * p) / 2 +
c(p1.latitude * p) *
c(p2.latitude * p) *
(1 - c((p2.longitude - p1.longitude) * p)) /
2;
return 12742 * math.asin(math.sqrt(a));
}
}

View File

@@ -1,6 +1,8 @@
import 'dart:io';
import 'package:flutter/services.dart';
import '../print.dart';
/// خدمة التحكم بوضع النافذة العائمة (Picture-in-Picture) على أندرويد.
/// تُستدعى عند بدء الرحلة لتفعيل PiP تلقائياً عند خروج المستخدم من التطبيق.
class PipService {
@@ -23,7 +25,7 @@ class PipService {
try {
await _channel.invokeMethod('enablePip');
} catch (e) {
print('PiP enable error: \$e');
Log.print('PiP enable error: \$e');
}
}
@@ -33,7 +35,7 @@ class PipService {
try {
await _channel.invokeMethod('disablePip');
} catch (e) {
print('PiP disable error: \$e');
Log.print('PiP disable error: \$e');
}
}
@@ -44,7 +46,7 @@ class PipService {
final result = await _channel.invokeMethod<bool>('enterPip');
return result ?? false;
} catch (e) {
print('PiP enter error: \$e');
Log.print('PiP enter error: \$e');
return false;
}
}

View File

@@ -18,10 +18,10 @@ class SplashScreen extends StatelessWidget {
// ألوان الـ colorize — سيان كهربائي → أبيض → ذهبي عنبري
const colorizeColors = [
Color(0xFF00D4FF),
Color(0xFF1DA1F2),
Colors.white,
Color(0xFFFFB700),
Color(0xFF00D4FF),
Color(0xFF1DA1F2),
];
return SafeArea(
@@ -44,7 +44,7 @@ class SplashScreen extends StatelessWidget {
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(colors: [
const Color(0xFF00D4FF).withOpacity(0.11),
const Color(0xFF1DA1F2).withOpacity(0.11),
Colors.transparent,
]),
),
@@ -92,7 +92,7 @@ class SplashScreen extends StatelessWidget {
child: CustomPaint(
painter: _OrbitalRingPainter(
radius: 100,
dotColor: const Color(0xFF00D4FF),
dotColor: const Color(0xFF1DA1F2),
lineOpacity: 0.22,
dotSize: 5.5,
),
@@ -128,10 +128,10 @@ class SplashScreen extends StatelessWidget {
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFF00D4FF),
color: const Color(0xFF1DA1F2),
boxShadow: [
BoxShadow(
color: const Color(0xFF00D4FF)
color: const Color(0xFF1DA1F2)
.withOpacity(0.25 +
controller.glowAnimation.value *
0.35),
@@ -151,7 +151,7 @@ class SplashScreen extends StatelessWidget {
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF00D4FF)
color: const Color(0xFF1DA1F2)
.withOpacity(0.08 +
controller.glowAnimation.value *
0.10),
@@ -202,11 +202,11 @@ class SplashScreen extends StatelessWidget {
decoration: BoxDecoration(
border: Border.all(
color:
const Color(0xFF00D4FF).withOpacity(0.35),
const Color(0xFF1DA1F2).withOpacity(0.35),
width: 1,
),
borderRadius: BorderRadius.circular(20),
color: const Color(0xFF00D4FF).withOpacity(0.06),
color: const Color(0xFF1DA1F2).withOpacity(0.06),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -219,13 +219,13 @@ class SplashScreen extends StatelessWidget {
height: 6,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFF00D4FF)
color: const Color(0xFF1DA1F2)
.withOpacity(0.5 +
controller.glowAnimation.value *
0.5),
boxShadow: [
BoxShadow(
color: const Color(0xFF00D4FF)
color: const Color(0xFF1DA1F2)
.withOpacity(controller
.glowAnimation.value *
0.6),
@@ -242,7 +242,7 @@ class SplashScreen extends StatelessWidget {
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: const Color(0xFF00D4FF)
color: const Color(0xFF1DA1F2)
.withOpacity(0.85),
letterSpacing: 1.4,
),
@@ -346,12 +346,12 @@ class _GlowProgressBar extends StatelessWidget {
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [
Color(0xFF0052FF),
Color(0xFF00D4FF),
Color(0xFF1DA1F2),
]),
borderRadius: BorderRadius.circular(2),
boxShadow: [
BoxShadow(
color: const Color(0xFF00D4FF).withOpacity(0.55),
color: const Color(0xFF1DA1F2).withOpacity(0.55),
blurRadius: 8,
spreadRadius: 1,
),
@@ -369,7 +369,7 @@ class _GridPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final linePaint = Paint()
..color = const Color(0xFF00D4FF).withOpacity(0.04)
..color = const Color(0xFF1DA1F2).withOpacity(0.04)
..strokeWidth = 0.5;
const spacing = 36.0;
@@ -383,7 +383,7 @@ class _GridPainter extends CustomPainter {
// نقاط التقاطع
final dotPaint = Paint()
..color = const Color(0xFF00D4FF).withOpacity(0.07)
..color = const Color(0xFF1DA1F2).withOpacity(0.07)
..style = PaintingStyle.fill;
for (double y = 0; y < size.height; y += spacing) {
@@ -407,7 +407,7 @@ class _OrbitalRingPainter extends CustomPainter {
const _OrbitalRingPainter({
this.radius = 95,
this.dotColor = const Color(0xFF00D4FF),
this.dotColor = const Color(0xFF1DA1F2),
this.lineOpacity = 0.25,
this.dotSize = 5.5,
this.dashCount = 0,
@@ -420,7 +420,7 @@ class _OrbitalRingPainter extends CustomPainter {
if (dashCount > 0) {
// حلقة متقطعة (dashed)
final dashPaint = Paint()
..color = const Color(0xFF00D4FF).withOpacity(lineOpacity)
..color = const Color(0xFF1DA1F2).withOpacity(lineOpacity)
..strokeWidth = 1
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
@@ -442,7 +442,7 @@ class _OrbitalRingPainter extends CustomPainter {
} else {
// حلقة متصلة
final ringPaint = Paint()
..color = const Color(0xFF00D4FF).withOpacity(lineOpacity)
..color = const Color(0xFF1DA1F2).withOpacity(lineOpacity)
..strokeWidth = 1
..style = PaintingStyle.stroke;
canvas.drawCircle(center, radius, ringPaint);

View File

@@ -173,8 +173,8 @@ class RateDriverFromPassenger extends StatelessWidget {
decoration: InputDecoration(
labelText: 'Enter your Note'.tr,
hintText: 'Type something...'.tr,
prefixIcon: const Icon(
Icons.rate_review), // Add an icon as a prefix
prefixIcon: Icon(
Icons.rate_review, color: AppColor.grayColor), // Add an icon as a prefix
suffixIcon: IconButton(
icon: const Icon(
Icons.clear,
@@ -186,20 +186,20 @@ class RateDriverFromPassenger extends StatelessWidget {
),
border:
const OutlineInputBorder(), // Add a border around the input field
enabledBorder: const OutlineInputBorder(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
Colors.blue), // Customize the border color
AppColor.grayColor.withOpacity(0.5)), // Customize the border color
),
focusedBorder: const OutlineInputBorder(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors
.green), // Customize the border color when focused
color: AppColor
.greenColor), // Customize the border color when focused
),
errorBorder: const OutlineInputBorder(
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors
.red), // Customize the border color when there's an error
color: AppColor
.redColor), // Customize the border color when there's an error
),
),
),

View File

@@ -39,10 +39,10 @@ class RatingDriverBottomSheet extends StatelessWidget {
radius: 30,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'),
onBackgroundImageError: (exception, stackTrace) => const Icon(
onBackgroundImageError: (exception, stackTrace) => Icon(
Icons.person,
size: 30,
color: AppColor.blueColor),
color: AppColor.cyanBlue),
),
const SizedBox(height: 16),
@@ -60,7 +60,7 @@ class RatingDriverBottomSheet extends StatelessWidget {
'Your valuable feedback helps us improve our service quality.'
.tr,
style: AppStyle.title
.copyWith(color: Colors.grey.shade600, fontSize: 14),
.copyWith(color: AppColor.grayColor, fontSize: 14),
textAlign: TextAlign.center,
),
@@ -119,9 +119,9 @@ class RatingDriverBottomSheet extends StatelessWidget {
hintText:
'Share your experience to help us improve...'.tr,
prefixIcon:
const Icon(Icons.rate_review, color: Colors.blueGrey),
Icon(Icons.rate_review, color: AppColor.grayColor),
suffixIcon: IconButton(
icon: const Icon(Icons.clear, color: AppColor.redColor),
icon: Icon(Icons.clear, color: AppColor.redColor),
onPressed: () {
controller.comment.clear();
},
@@ -131,8 +131,8 @@ class RatingDriverBottomSheet extends StatelessWidget {
borderRadius: BorderRadius.circular(12)),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide:
const BorderSide(color: Colors.blueGrey, width: 1),
borderSide:
BorderSide(color: AppColor.grayColor.withOpacity(0.5), width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),

View File

@@ -54,8 +54,7 @@ class LoginPage extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.policy_outlined,
size: 80, color: AppColor.primaryColor),
Icon(Icons.policy_outlined, size: 80, color: AppColor.primaryColor),
const SizedBox(height: 20),
Text("passenger agreement".tr,
textAlign: TextAlign.center, style: AppStyle.headTitle2),
@@ -71,9 +70,9 @@ class LoginPage extends StatelessWidget {
.tr),
TextSpan(
text: 'Terms of Use'.tr,
style: const TextStyle(
style: TextStyle(
decoration: TextDecoration.underline,
color: AppColor.blueColor,
color: AppColor.cyanBlue,
fontWeight: FontWeight.bold),
recognizer: TapGestureRecognizer()
..onTap = () {
@@ -193,7 +192,7 @@ class LoginPage extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.location_on, size: 60, color: AppColor.primaryColor),
Icon(Icons.location_on, size: 60, color: AppColor.primaryColor),
const SizedBox(height: 20),
Text(
'Enable Location Access'.tr,

View File

@@ -23,8 +23,8 @@ class RegisterPage extends StatelessWidget {
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Container(
decoration: const BoxDecoration(
boxShadow: [
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
offset: Offset(3, 3),
color: AppColor.accentColor,

View File

@@ -58,7 +58,7 @@ class SmsSignupEgypt extends StatelessWidget {
registerController.phoneController.text =
phone.completeNumber.toString();
Log.print(' phone.number: ${phone.number}');
print(
Log.print(
"Formatted phone number: ${registerController.phoneController.text}");
},
validator: (phone) {

View File

@@ -36,7 +36,7 @@ class ContactUsPage extends StatelessWidget {
child: Image.asset('assets/images/logo.gif')),
IconButton(
onPressed: () async {
Get.put(TextToSpeechController()).speakText(
Get.find<TextToSpeechController>().speakText(
'Intaleq is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Intaleq, you enjoy quality, safety, and peace of mind—every time you ride.'
.tr);
},

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/profile/invit_controller.dart';
import '../../../print.dart';
@@ -12,16 +13,16 @@ class ShareAppPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.systemBackground,
backgroundColor: AppColor.secondaryColor,
appBar: AppBar(
backgroundColor: CupertinoColors.systemBackground,
backgroundColor: AppColor.secondaryColor,
elevation: 0,
title: Text(
'Invite'.tr,
style: const TextStyle(color: CupertinoColors.label),
style: AppStyle.headTitle2.copyWith(fontSize: 20),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColor.blueColor),
icon: Icon(Icons.arrow_back_ios, color: AppColor.cyanBlue),
onPressed: () => Get.back(),
),
),
@@ -51,7 +52,7 @@ class ShareAppPage extends StatelessWidget {
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
color: AppColor.grayColor.withOpacity(0.08),
borderRadius: BorderRadius.circular(12),
),
child: Column(
@@ -60,8 +61,8 @@ class ShareAppPage extends StatelessWidget {
"Share this code with your friends and earn rewards when they use it!"
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
style: TextStyle(
color: AppColor.grayColor,
fontSize: 13,
),
),
@@ -116,8 +117,9 @@ class ShareAppPage extends StatelessWidget {
Widget _buildPhoneInput() {
return Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
color: AppColor.grayColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.grayColor.withOpacity(0.1)),
),
child: Row(
children: [
@@ -125,13 +127,16 @@ class ShareAppPage extends StatelessWidget {
child: CupertinoTextField.borderless(
controller: controller.invitePhoneController,
placeholder: 'Enter phone'.tr,
padding: const EdgeInsets.all(12),
placeholderStyle: TextStyle(
color: AppColor.grayColor.withOpacity(0.5), fontSize: 16),
style: TextStyle(color: AppColor.writeColor, fontSize: 16),
padding: const EdgeInsets.all(14),
keyboardType: TextInputType.phone,
),
),
CupertinoButton(
child: const Icon(CupertinoIcons.person_badge_plus,
color: AppColor.blueColor),
child: Icon(CupertinoIcons.person_badge_plus,
color: AppColor.cyanBlue),
onPressed: () async {
await controller.pickContacts();
Log.print('contacts: ${controller.contacts}');
@@ -169,16 +174,16 @@ class ShareAppPage extends StatelessWidget {
],
),
child: CupertinoButton(
color: AppColor.blueColor,
color: AppColor.primaryColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInviteToPassenger,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
color: AppColor.secondaryColor,
),
),
),
@@ -198,15 +203,15 @@ class ShareAppPage extends StatelessWidget {
],
),
child: CupertinoButton(
color: AppColor.blueColor,
color: AppColor.cyanBlue,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
color: AppColor.secondaryColor,
),
),
onPressed: () async {
@@ -227,8 +232,8 @@ class ShareAppPage extends StatelessWidget {
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
style: TextStyle(
color: AppColor.grayColor,
fontSize: 17,
),
),
@@ -258,8 +263,9 @@ class ShareAppPage extends StatelessWidget {
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
color: AppColor.grayColor.withOpacity(0.08),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.grayColor.withOpacity(0.05)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -267,10 +273,10 @@ class ShareAppPage extends StatelessWidget {
Text(
invitation['passengerName']
.toString(), // Handle null or missing data
style: const TextStyle(
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
color: AppColor.writeColor,
),
),
const SizedBox(height: 8),
@@ -278,18 +284,18 @@ class ShareAppPage extends StatelessWidget {
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
backgroundColor: AppColor.grayColor.withOpacity(0.1),
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
AlwaysStoppedAnimation<Color>(AppColor.primaryColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 2 ${'Trip'.tr}', // Show trips completed
style: const TextStyle(
style: TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
color: AppColor.grayColor,
),
),
],
@@ -302,18 +308,19 @@ class ShareAppPage extends StatelessWidget {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
color: AppColor.grayColor.withOpacity(0.08),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.grayColor.withOpacity(0.05)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Your Rewards".tr,
style: const TextStyle(
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
color: AppColor.writeColor,
),
),
const SizedBox(height: 16),
@@ -342,8 +349,8 @@ class ShareAppPage extends StatelessWidget {
children: [
Text(
label,
style: const TextStyle(
color: CupertinoColors.label,
style: TextStyle(
color: AppColor.writeColor,
fontSize: 15,
),
),
@@ -362,7 +369,9 @@ class ShareAppPage extends StatelessWidget {
void _showContactsDialog(BuildContext context) {
Get.defaultDialog(
backgroundColor: AppColor.secondaryColor,
title: 'Choose from contact'.tr,
titleStyle: TextStyle(color: AppColor.writeColor),
content: SizedBox(
height: 400,
width: 400,
@@ -409,10 +418,10 @@ class ShareAppPage extends StatelessWidget {
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
color: AppColor.secondaryColor,
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5),
color: AppColor.grayColor.withOpacity(0.1),
),
),
),
@@ -425,16 +434,16 @@ class ShareAppPage extends StatelessWidget {
children: [
Text(
contact['name'],
style: const TextStyle(
color: CupertinoColors.label,
style: TextStyle(
color: AppColor.writeColor,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
Text(
(contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
style: TextStyle(
color: AppColor.grayColor,
fontSize: 15,
),
),
@@ -442,9 +451,9 @@ class ShareAppPage extends StatelessWidget {
),
),
// Chevron icon for selection
const Icon(
Icon(
CupertinoIcons.chevron_forward,
color: CupertinoColors.systemGrey2,
color: AppColor.grayColor.withOpacity(0.5),
),
],
),

View File

@@ -152,8 +152,8 @@ class CancelRidePageShow extends StatelessWidget {
decoration: BoxDecoration(
color: AppColor.redColor,
borderRadius: BorderRadius.circular(15)),
child: const Padding(
padding: EdgeInsets.all(3),
child: Padding(
padding: const EdgeInsets.all(3),
child: Icon(
Icons.clear,
size: 40,

View File

@@ -340,28 +340,31 @@ class ApplyOrderWidget extends StatelessWidget {
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
color: Get.isDarkMode ? Colors.grey[850] : const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.grey.withOpacity(0.3)),
border: Border.all(
color: Get.isDarkMode
? Colors.white10
: Colors.grey.withOpacity(0.3)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
plateNumber,
style: const TextStyle(
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 18, // تصغير الرقم
fontSize: 18,
fontWeight: FontWeight.w900,
color: Colors.black87,
color: AppColor.writeColor,
letterSpacing: 1.5,
),
),
const Text("SYR",
Text("SYR",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.black54)),
color: AppColor.writeColor.withOpacity(0.6))),
],
),
);

View File

@@ -35,9 +35,9 @@ class CancelRidePageWidget extends StatelessWidget {
return Container(
height: Get.height * 0.7, // ارتفاع مناسب
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(25)),
),
child: GetBuilder<MapPassengerController>(
builder: (controller) => Column(
@@ -82,7 +82,7 @@ class CancelRidePageWidget extends StatelessWidget {
: FontWeight.normal,
color: isSelected
? AppColor.primaryColor
: Colors.black87,
: AppColor.writeColor,
fontSize: 15),
),
trailing: isSelected
@@ -104,7 +104,9 @@ class CancelRidePageWidget extends StatelessWidget {
decoration: InputDecoration(
hintText: "Please write the reason...".tr,
filled: true,
fillColor: Colors.grey[100],
fillColor: Get.isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
@@ -149,7 +151,7 @@ class CancelRidePageWidget extends StatelessWidget {
child: TextButton(
onPressed: () => Get.back(),
child: Text("Don't Cancel".tr,
style: TextStyle(color: Colors.grey[600])),
style: TextStyle(color: AppColor.grayColor)),
),
),
],

View File

@@ -60,7 +60,7 @@ List<CarType> carTypes = [
// ─────────────────────────────────────────────────────────────────────────────
class CarDetailsTypeToChoose extends StatelessWidget {
CarDetailsTypeToChoose({super.key});
final textToSpeechController = Get.put(TextToSpeechController());
final textToSpeechController = Get.find<TextToSpeechController>();
void _prepareCarTypes(MapPassengerController controller) {
if (controller.distance > 23) {
@@ -157,8 +157,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
// ── Promo Code & Actions ─────────────────────────────
_buildPromoButton(context, controller),
SizedBox(
height: MediaQuery.of(context).padding.bottom + 10),
SizedBox(height: MediaQuery.of(context).padding.bottom + 10),
],
),
),
@@ -199,11 +198,13 @@ class CarDetailsTypeToChoose extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.grey.shade100,
color: Get.isDarkMode
? Colors.white.withOpacity(0.08)
: Colors.grey.shade100,
shape: BoxShape.circle,
),
child: Icon(Icons.close_rounded,
size: 18, color: Colors.grey.shade600),
size: 18, color: AppColor.grayColor),
),
),
],
@@ -215,8 +216,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
// Distance chip
_buildStatChip(
icon: Icons.route_rounded,
value:
'${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
value: '${controller.distance.toStringAsFixed(1)} ${'KM'.tr}',
color: AppColor.primaryColor,
),
const SizedBox(width: 8),
@@ -313,11 +313,12 @@ class CarDetailsTypeToChoose extends StatelessWidget {
],
)
: null,
color: isSelected ? null : Colors.white,
color: isSelected ? null : AppColor.secondaryColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color:
isSelected ? AppColor.primaryColor : Colors.grey.shade200,
color: isSelected
? AppColor.primaryColor
: AppColor.grayColor.withOpacity(0.2),
width: isSelected ? 2.0 : 1.0,
),
boxShadow: [
@@ -355,8 +356,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
),
],
),
child:
const Icon(Icons.check, size: 11, color: Colors.white),
child: const Icon(Icons.check, size: 11, color: Colors.white),
),
),
@@ -389,7 +389,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
fontSize: 13,
color: isSelected
? AppColor.primaryColor
: Colors.grey.shade800,
: AppColor.writeColor,
),
maxLines: 1,
),
@@ -399,16 +399,17 @@ class CarDetailsTypeToChoose extends StatelessWidget {
// Price tag
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isSelected
? AppColor.primaryColor
: Colors.grey.shade50,
: AppColor.writeColor.withOpacity(0.05),
borderRadius: BorderRadius.circular(10),
border: isSelected
? null
: Border.all(color: Colors.grey.shade200),
: Border.all(
color: AppColor.grayColor.withOpacity(0.2)),
),
child: FittedBox(
child: Text(
@@ -416,8 +417,9 @@ class CarDetailsTypeToChoose extends StatelessWidget {
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color:
isSelected ? Colors.white : Colors.grey.shade700,
color: isSelected
? Colors.white
: AppColor.writeColor.withOpacity(0.8),
),
),
),
@@ -446,8 +448,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
onTap: () => _showPromoCodeDialog(context, controller),
borderRadius: BorderRadius.circular(14),
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
@@ -730,8 +731,8 @@ class CarDetailsTypeToChoose extends StatelessWidget {
const SizedBox(height: 6),
// Price badge in dialog
Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 6),
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
@@ -754,15 +755,20 @@ class CarDetailsTypeToChoose extends StatelessWidget {
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.grey.shade50,
color: Get.isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey.shade50,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: Colors.grey.shade100),
border: Border.all(
color: Get.isDarkMode
? Colors.white10
: Colors.grey.shade100),
),
child: Text(
_getCarDescription(mapPassengerController, carType),
textAlign: TextAlign.center,
style: AppStyle.subtitle.copyWith(
color: Colors.grey.shade700,
color: AppColor.writeColor.withOpacity(0.8),
fontSize: 14,
height: 1.5,
),
@@ -778,12 +784,12 @@ class CarDetailsTypeToChoose extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
side: BorderSide(color: Colors.grey.shade200),
side: BorderSide(
color: AppColor.grayColor.withOpacity(0.2)),
),
),
child: Text('Back'.tr,
style:
TextStyle(color: Colors.grey.shade600)),
style: TextStyle(color: AppColor.grayColor)),
),
),
const SizedBox(width: 12),
@@ -810,11 +816,11 @@ class CarDetailsTypeToChoose extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
color: AppColor.secondaryColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(20),
color: Colors.black.withAlpha(30),
blurRadius: 15,
offset: const Offset(0, 5),
),

View File

@@ -59,7 +59,7 @@ class CashConfirmPageShown extends StatelessWidget {
// زر الإغلاق (كان معلقاً في الكود القديم، تم تفعيله هنا)
IconButton(
onPressed: () => controller.changeCashConfirmPageShown(),
icon: const Icon(Icons.close, color: AppColor.writeColor),
icon: Icon(Icons.close, color: AppColor.writeColor),
),
],
),

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'dart:async';
import 'package:flutter/material.dart';
@@ -131,7 +132,7 @@ class _SearchFieldState extends State<_SearchField> {
hintText: widget.controller.hintTextDestinationPoint,
hintStyle: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
prefixIcon:
const Icon(Icons.search, color: AppColor.primaryColor),
Icon(Icons.search, color: AppColor.primaryColor),
// --- [إصلاح] تم استبدال Obx بشرط بسيط لأن `setState` يعيد بناء الواجهة الآن ---
suffixIcon: widget
.controller.placeDestinationController.text.isNotEmpty
@@ -383,20 +384,20 @@ Widget _buildQuickActionButton({
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: AppColor.blueColor.withOpacity(0.1),
color: AppColor.cyanBlue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: AppColor.blueColor.withOpacity(0.3)),
border: Border.all(color: AppColor.cyanBlue.withOpacity(0.3)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.blueColor),
Icon(icon, color: AppColor.cyanBlue),
const SizedBox(height: 4.0),
Text(
text,
textAlign: TextAlign.center,
style: AppStyle.title.copyWith(
color: AppColor.blueColor, fontWeight: FontWeight.w500),
color: AppColor.cyanBlue, fontWeight: FontWeight.w500),
),
],
),
@@ -451,7 +452,7 @@ void _handleQuickAction(
controller.showBottomSheet1();
} catch (e) {
// Handle error if parsing fails
print("Error handling quick action: $e");
Log.print("Error handling quick action: $e");
Toast.show(Get.context!, "Failed to get location".tr, AppColor.redColor);
}
}
}

View File

@@ -35,7 +35,7 @@ GetBuilder<MapPassengerController> formSearchPlacesStart() {
hintStyle:
AppStyle.subtitle.copyWith(color: Colors.grey[600]),
prefixIcon:
const Icon(Icons.search, color: AppColor.primaryColor),
Icon(Icons.search, color: AppColor.primaryColor),
suffixIcon: controller.placeStartController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear, color: Colors.grey[400]),

View File

@@ -17,7 +17,7 @@ GetBuilder<MapPassengerController> formSearchPlaces(int index) {
padding: const EdgeInsets.all(16),
child: Container(
decoration:
const BoxDecoration(color: AppColor.secondaryColor),
BoxDecoration(color: AppColor.secondaryColor),
child: TextField(
decoration: InputDecoration(
border: const OutlineInputBorder(

View File

@@ -1,8 +1,10 @@
import 'package:Intaleq/print.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
import 'package:Intaleq/services/offline_map_service.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
@@ -30,7 +32,7 @@ class GoogleMapPassengerWidget extends StatelessWidget {
attributionButtonMargins: null,
onMapCreated: controller.onMapCreated,
onStyleLoadedCallback: () => controller.onStyleLoaded(),
styleString: "assets/style.json",
styleString: Get.isDarkMode ? "assets/style_dark.json" : "assets/style.json",
// ✅ Performance: Smoother zoom limits for low-end devices
minMaxZoomPreference: controller.lowPerf
@@ -51,10 +53,14 @@ class GoogleMapPassengerWidget extends StatelessWidget {
if (controller.mapController != null) {
final position = controller.mapController!.cameraPosition;
if (position != null) {
print('✅ onCameraIdle targeted: ${position.target}');
Log.print('✅ onCameraIdle targeted: ${position.target}');
// 1. Always update current view target (for pickers)
controller
.updateCurrentLocationFromCamera(position.target);
// 2. Cache explicitly when panning around
// Optional: Limit this to only cache smaller regions (1km) so it doesn't overload on fast panning
OfflineMapService.instance.downloadRegion(position.target, radiusKm: 1.0);
}
}
},
@@ -80,9 +86,9 @@ class GoogleMapPassengerWidget extends StatelessWidget {
colorText: AppColor.redColor,
duration: const Duration(seconds: 5),
backgroundColor: AppColor.secondaryColor,
icon: const Icon(Icons.error, color: AppColor.redColor),
icon: Icon(Icons.error, color: AppColor.redColor),
titleText: Text('Error'.tr,
style: const TextStyle(color: AppColor.redColor)),
style: TextStyle(color: AppColor.redColor)),
messageText: Text(
'We Are Sorry That we dont have cars in your Location!'
.tr,
@@ -99,4 +105,4 @@ class GoogleMapPassengerWidget extends StatelessWidget {
),
);
}
}
}

View File

@@ -17,7 +17,6 @@ import '../navigation/navigation_view.dart';
// --- الدالة الرئيسية بالتصميم الجديد ---
GetBuilder<MapPassengerController> leftMainMenuIcons() {
Get.put(TextToSpeechController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned(
// تم تعديل الموضع ليتناسب مع التصميم الجديد

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -148,7 +149,9 @@ class MainBottomMenuMap extends StatelessWidget {
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.cardShadow,
border: Border.all(
color: Colors.white.withOpacity(0.65),
color: Get.isDarkMode
? Colors.white.withOpacity(0.15)
: Colors.white.withOpacity(0.65),
width: 1.2,
),
),
@@ -216,123 +219,129 @@ class _CollapsedView extends StatelessWidget {
const SizedBox(height: 16),
// ── Main interactive search card ─────────────────────────────────────
Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusInner),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
child: Row(
children: [
// Animated search icon with glow
AnimatedContainer(
duration: _D.medium,
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: _D.primaryGradient(),
borderRadius: BorderRadius.circular(_D.radiusPill),
boxShadow: _D.glowShadow(AppColor.primaryColor),
Semantics(
button: true,
label: 'Open destination search'.tr,
hint: 'Double tap to open search or enter destination'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusInner),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
child: Row(
children: [
// Animated search icon with glow
AnimatedContainer(
duration: _D.medium,
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: _D.primaryGradient(),
borderRadius: BorderRadius.circular(_D.radiusPill),
boxShadow: _D.glowShadow(AppColor.primaryColor),
),
child: const Icon(
Icons.search_rounded,
color: Colors.white,
size: 22,
),
),
child: const Icon(
Icons.search_rounded,
color: Colors.white,
size: 22,
),
),
const SizedBox(width: 16),
const SizedBox(width: 16),
// Dynamic text content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Where to'.tr} ',
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.grey.shade700,
// Dynamic text content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
children: [
TextSpan(
text: '${'Where to'.tr} ',
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.grey.shade700,
),
),
),
TextSpan(
text: firstName,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w800,
fontSize: 16.5,
color: AppColor.primaryColor,
letterSpacing: -0.3,
TextSpan(
text: firstName,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w800,
fontSize: 16.5,
color: AppColor.primaryColor,
letterSpacing: -0.3,
),
),
),
const TextSpan(text: '؟'),
],
),
),
const SizedBox(height: 2),
if (!controller.noCarString)
AnimatedOpacity(
duration: _D.fast,
opacity: 1,
child: Text(
'Tap to search your destination'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 12,
color: Colors.grey.shade500,
fontWeight: FontWeight.w400,
),
const TextSpan(text: '؟'),
],
),
),
],
),
),
// Elegant expand indicator
Container(
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primaryColor.withOpacity(0.12),
AppColor.primaryColor.withOpacity(0.06),
const SizedBox(height: 2),
if (!controller.noCarString)
AnimatedOpacity(
duration: _D.fast,
opacity: 1,
child: Text(
'Tap to search your destination'.tr,
style: AppStyle.subtitle.copyWith(
fontSize: 12,
color: Colors.grey.shade500,
fontWeight: FontWeight.w400,
),
),
),
],
),
borderRadius: BorderRadius.circular(_D.radiusPill),
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.25),
width: 1,
),
// Elegant expand indicator
Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primaryColor.withOpacity(0.12),
AppColor.primaryColor.withOpacity(0.06),
],
),
borderRadius: BorderRadius.circular(_D.radiusPill),
border: Border.all(
color: AppColor.primaryColor.withOpacity(0.25),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedRotation(
duration: _D.fast,
turns: 0,
child: Icon(
Icons.keyboard_arrow_up_rounded,
color: AppColor.primaryColor,
size: 20,
),
),
const SizedBox(width: 4),
Text(
'Open'.tr,
style: TextStyle(
color: AppColor.primaryColor,
fontSize: 12.5,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
),
),
],
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedRotation(
duration: _D.fast,
turns: 0,
child: Icon(
Icons.keyboard_arrow_up_rounded,
color: AppColor.primaryColor,
size: 20,
),
),
const SizedBox(width: 4),
Text(
'Open'.tr,
style: TextStyle(
color: AppColor.primaryColor,
fontSize: 12.5,
fontWeight: FontWeight.w700,
letterSpacing: 0.3,
),
),
],
),
),
],
],
),
),
),
),
@@ -438,30 +447,34 @@ class _ExpandedView extends StatelessWidget {
),
const Spacer(),
// Elegant close button
Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusPill),
child: AnimatedContainer(
duration: _D.fast,
width: 38,
height: 38,
decoration: BoxDecoration(
color: Colors.grey.shade100,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.shade200.withOpacity(0.5),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
Icons.keyboard_arrow_down_rounded,
size: 24,
color: Colors.grey.shade600,
Semantics(
button: true,
label: 'Close panel'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: controller.changeMainBottomMenuMap,
borderRadius: BorderRadius.circular(_D.radiusPill),
child: AnimatedContainer(
duration: _D.fast,
width: 38,
height: 38,
decoration: BoxDecoration(
color: Colors.grey.shade100,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.shade200.withOpacity(0.5),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
Icons.keyboard_arrow_down_rounded,
size: 24,
color: Colors.grey.shade600,
),
),
),
),
@@ -586,6 +599,13 @@ class _ExpandedView extends StatelessWidget {
),
const SizedBox(width: 12),
Expanded(
child: Semantics(
button: true,
label: isSet
? '${'Waypoint'.tr} $wpName'
: '${'Stop'.tr} ${index + 1}',
hint: 'Double tap to set or change this waypoint on the map'
.tr,
child: GestureDetector(
onTap: () {
controller.changeMainBottomMenuMap();
@@ -607,52 +627,60 @@ class _ExpandedView extends StatelessWidget {
overflow: TextOverflow.ellipsis,
),
),
),
)),
// Map button with hover effect simulation
GestureDetector(
onTap: () {
controller.changeMainBottomMenuMap();
controller.startPickingWaypointOnMap(index);
},
child: AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: accent.withOpacity(0.25),
width: 1,
Semantics(
button: true,
label: 'Pick location on map'.tr,
child: GestureDetector(
onTap: () {
controller.changeMainBottomMenuMap();
controller.startPickingWaypointOnMap(index);
},
child: AnimatedContainer(
duration: _D.fast,
width: 34,
height: 34,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: accent.withOpacity(0.25),
width: 1,
),
),
child: Icon(
Icons.map_outlined,
color: accent,
size: 17,
),
),
child: Icon(
Icons.map_outlined,
color: accent,
size: 17,
),
),
),
// Remove button with subtle animation
GestureDetector(
onTap: () => controller.removeMenuWaypoint(index),
child: AnimatedContainer(
duration: _D.fast,
width: 28,
height: 28,
decoration: BoxDecoration(
color: Colors.red.shade50,
shape: BoxShape.circle,
border: Border.all(
color: Colors.red.shade100,
width: 1,
Semantics(
button: true,
label: 'Remove waypoint'.tr,
child: GestureDetector(
onTap: () => controller.removeMenuWaypoint(index),
child: AnimatedContainer(
duration: _D.fast,
width: 28,
height: 28,
decoration: BoxDecoration(
color: Colors.red.shade50,
shape: BoxShape.circle,
border: Border.all(
color: Colors.red.shade100,
width: 1,
),
),
child: Icon(
Icons.close_rounded,
color: Colors.red.shade400,
size: 15,
),
),
child: Icon(
Icons.close_rounded,
color: Colors.red.shade400,
size: 15,
),
),
),
@@ -669,69 +697,73 @@ class _ExpandedView extends StatelessWidget {
isDotDashed: true,
showTopLine: true,
showBottomLine: true,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => controller.addMenuWaypoint(),
borderRadius: BorderRadius.circular(_D.radiusInner),
child: AnimatedContainer(
duration: _D.fast,
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: Colors.orange.shade200,
style: BorderStyle.solid,
width: 1.5,
),
gradient: LinearGradient(
colors: [
Colors.orange.shade50.withOpacity(0.6),
Colors.orange.shade50.withOpacity(0.3),
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_location_alt_outlined,
color: Colors.orange.shade500,
size: 18,
child: Semantics(
button: true,
label: 'Add a new waypoint stop'.tr,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => controller.addMenuWaypoint(),
borderRadius: BorderRadius.circular(_D.radiusInner),
child: AnimatedContainer(
duration: _D.fast,
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_D.radiusInner),
border: Border.all(
color: Colors.orange.shade200,
style: BorderStyle.solid,
width: 1.5,
),
const SizedBox(width: 10),
Text(
'Add a Stop'.tr,
style: TextStyle(
color: Colors.orange.shade700,
fontSize: 13.5,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
gradient: LinearGradient(
colors: [
Colors.orange.shade50.withOpacity(0.6),
Colors.orange.shade50.withOpacity(0.3),
],
),
const SizedBox(width: 10),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.orange.shade200,
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_location_alt_outlined,
color: Colors.orange.shade500,
size: 18,
),
child: Text(
'+5 ${'min'.tr}',
const SizedBox(width: 10),
Text(
'Add a Stop'.tr,
style: TextStyle(
color: Colors.orange.shade700,
fontSize: 10.5,
fontWeight: FontWeight.w700,
fontSize: 13.5,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
],
const SizedBox(width: 10),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.orange.shade200,
width: 1,
),
),
child: Text(
'+5 ${'min'.tr}',
style: TextStyle(
color: Colors.orange.shade700,
fontSize: 10.5,
fontWeight: FontWeight.w700,
),
),
),
],
),
),
),
),
@@ -990,7 +1022,9 @@ class _MapPickerOverlay extends StatelessWidget {
' ${controller.pickingWaypointIndex + 1}'.tr;
}
if (controller.passengerStartLocationFromMap) {
return 'Move map to your pickup point'.tr;
return controller.isAnotherOreder
? 'Now set the pickup point for the other person'.tr
: 'Move map to your pickup point'.tr;
} else if (controller.startLocationFromMap) {
return 'Move map to set start location'.tr;
} else if (controller.workLocationFromMap) {
@@ -1055,7 +1089,9 @@ class _MapPickerOverlay extends StatelessWidget {
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.glowShadow(modeColor, intensity: 0.5),
border: Border.all(
color: Colors.white.withOpacity(0.35),
color: Get.isDarkMode
? Colors.white.withOpacity(0.15)
: Colors.white.withOpacity(0.35),
width: 1,
),
),
@@ -1105,7 +1141,9 @@ class _MapPickerOverlay extends StatelessWidget {
borderRadius: BorderRadius.circular(_D.radiusCard),
boxShadow: _D.cardShadow,
border: Border.all(
color: Colors.white.withOpacity(0.7),
color: Get.isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white.withOpacity(0.7),
width: 1.3,
),
),
@@ -1339,24 +1377,15 @@ class _MapPickerOverlay extends StatelessWidget {
controller.newMyLocation.latitude,
controller.newMyLocation.longitude,
);
print(
Log.print(
'🌐 MAP PICKER CENTER: ${currentCameraPosition.latitude}, ${currentCameraPosition.longitude}');
print(
Log.print(
'✅ _onConfirmTap confirmed coordinates: ${currentCameraPosition.latitude}, ${currentCameraPosition.longitude}');
if (controller.isPickingWaypoint && controller.pickingWaypointIndex >= 0) {
final int wpIndex = controller.pickingWaypointIndex;
controller.setMenuWaypointFromMap(wpIndex, currentCameraPosition);
Get.snackbar(
'Stop ${wpIndex + 1} Set'.tr,
'Waypoint has been set successfully'.tr,
backgroundColor: Colors.orange.shade600,
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 2),
margin: const EdgeInsets.all(12),
borderRadius: 12,
);
mySnackbarSuccess('Waypoint has been set successfully'.tr);
return;
}
@@ -1403,10 +1432,7 @@ class _MapPickerOverlay extends StatelessWidget {
controller.workLocationFromMap = false;
controller.isPickerShown = false;
controller.update();
Get.snackbar('Work Saved'.tr, '',
backgroundColor: AppColor.greenColor,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM);
mySnackbarSuccess('Work Saved'.tr);
return;
}
@@ -1418,10 +1444,7 @@ class _MapPickerOverlay extends StatelessWidget {
controller.homeLocationFromMap = false;
controller.isPickerShown = false;
controller.update();
Get.snackbar('Home Saved'.tr, '',
backgroundColor: AppColor.greenColor,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM);
mySnackbarSuccess('Home Saved'.tr);
return;
}
@@ -1432,6 +1455,7 @@ class _MapPickerOverlay extends StatelessWidget {
controller.placesDestination = [];
controller.placeDestinationController.clear();
controller.passengerStartLocationFromMap = true;
controller.isPickerShown = true; // ✅ Keep picker UI open for pickup selection
controller.update();
try {
@@ -1450,7 +1474,9 @@ class _MapPickerOverlay extends StatelessWidget {
)),
);
}
} catch (_) {}
} catch (e) {
Log.print("Error occurred: $e");
}
Get.snackbar(
'Destination Set'.tr,

View File

@@ -22,14 +22,17 @@ import '../HomePage/share_app_page.dart';
import '../setting_page.dart';
import '../profile/passenger_profile_page.dart';
// ─── ألوان النظام ───────────────────────────────────────────────────────────
const _kBg = Color(0xFF060B18);
const _kBgSurface = Color(0xFF0D1525);
const _kCyan = Color(0xFF00D4FF);
// ─── ألوان النظام (Integrated with AppColor) ──────────────────────────────────
Color get _kCyan => AppColor.cyanBlue;
Color get _kBg =>
Get.isDarkMode ? const Color(0xFF060B18) : AppColor.secondaryColor;
Color get _kBgSurface => Get.isDarkMode
? const Color(0xFF0D1525)
: AppColor.secondaryColor.withOpacity(0.9);
const _kAmber = Color(0xFFFFB700);
const _kBorder = Color(0x1A00D4FF);
const _kText = Colors.white;
const _kTextMuted = Color(0xFF7A8FA8);
Color get _kBorder => _kCyan.withOpacity(0.15);
Color get _kText => AppColor.writeColor;
Color get _kTextMuted => AppColor.grayColor;
class MapMenuWidget extends StatelessWidget {
const MapMenuWidget({super.key});
@@ -259,8 +262,7 @@ class MapMenuWidget extends StatelessWidget {
border:
Border.all(color: _kCyan.withOpacity(0.35), width: 1.5),
),
child:
const Icon(Icons.person_rounded, color: _kCyan, size: 28),
child: Icon(Icons.person_rounded, color: _kCyan, size: 28),
),
// نقطة الحضور
Positioned(
@@ -291,7 +293,7 @@ class MapMenuWidget extends StatelessWidget {
children: [
Text(
box.read(BoxName.name) ?? 'Guest',
style: const TextStyle(
style: TextStyle(
color: _kText,
fontSize: 17,
fontWeight: FontWeight.w700,
@@ -305,13 +307,13 @@ class MapMenuWidget extends StatelessWidget {
Container(
width: 5,
height: 5,
decoration: const BoxDecoration(
color: _kCyan, shape: BoxShape.circle),
decoration:
BoxDecoration(color: _kCyan, shape: BoxShape.circle),
),
const SizedBox(width: 6),
Text(
"Intaleq Passenger".tr,
style: const TextStyle(
style: TextStyle(
color: _kTextMuted,
fontSize: 12,
letterSpacing: 0.4,
@@ -426,7 +428,7 @@ class _QuickBtn extends StatelessWidget {
const SizedBox(height: 6),
Text(
label,
style: const TextStyle(
style: TextStyle(
color: _kTextMuted,
fontSize: 11,
letterSpacing: 0.4,
@@ -518,7 +520,7 @@ class _MenuGridPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = const Color(0xFF00D4FF).withOpacity(0.025)
..color = AppColor.cyanBlue.withOpacity(0.04)
..strokeWidth = 0.5;
const spacing = 36.0;
for (double y = 0; y < size.height; y += spacing) {

View File

@@ -127,9 +127,9 @@ class MyCreditCardWidget extends StatelessWidget {
builder: (controller) => Container(
height: Get.height * .4,
width: Get.width * .9,
decoration: const BoxDecoration(
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: BorderRadius.all(Radius.circular(15)),
borderRadius: const BorderRadius.all(Radius.circular(15)),
gradient: LinearGradient(colors: [
AppColor.secondaryColor,
// AppColor.blueColor,
@@ -139,7 +139,7 @@ class MyCreditCardWidget extends StatelessWidget {
// AppColor.redColor,
// AppColor.yellowColor
]),
boxShadow: [
boxShadow: const [
BoxShadow(
spreadRadius: 3,
offset: Offset(3, 3),

View File

@@ -25,15 +25,15 @@ class PickerAnimtionContainerFormPlaces extends StatelessWidget {
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
height: controller.heightPickerContainer,
decoration: const BoxDecoration(
boxShadow: [
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: AppColor.accentColor, offset: Offset(2, 2)),
BoxShadow(
color: AppColor.accentColor, offset: Offset(-2, -2))
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.only(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
)),

View File

@@ -132,7 +132,7 @@ class PointsPageForRider extends StatelessWidget {
},
)
: IconButton(
icon: const Icon(
icon: Icon(
Icons.close,
color: AppColor.secondaryColor,
),
@@ -264,7 +264,7 @@ void showAddLocationDialog(BuildContext context, int index) {
'Add Location'.tr,
style: AppStyle.title,
),
const Icon(
Icon(
Icons.clear,
color: AppColor.secondaryColor,
)
@@ -301,7 +301,7 @@ class AppBarPointsPageForRider extends StatelessWidget {
Container(
child: Row(
children: [
const CircleAvatar(
CircleAvatar(
backgroundColor: AppColor.primaryColor,
maxRadius: 15,
child: Icon(
@@ -319,7 +319,7 @@ class AppBarPointsPageForRider extends StatelessWidget {
],
),
),
const Icon(
Icon(
Icons.clear,
color: AppColor.secondaryColor,
)

View File

@@ -42,14 +42,16 @@ class RideBeginPassenger extends StatelessWidget {
right: 0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: Get.isDarkMode
? Colors.black.withOpacity(0.4)
: Colors.black.withOpacity(0.1),
blurRadius: 20,
spreadRadius: 2,
offset: const Offset(0, -3),
@@ -67,7 +69,7 @@ class RideBeginPassenger extends StatelessWidget {
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
color: AppColor.grayColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
),
@@ -80,8 +82,10 @@ class RideBeginPassenger extends StatelessWidget {
const SizedBox(height: 12),
// خط فاصل خفيف
const Divider(
height: 1, thickness: 0.5, color: Color(0xFFEEEEEE)),
Divider(
height: 1,
thickness: 0.5,
color: AppColor.grayColor.withOpacity(0.2)),
const SizedBox(height: 12),
@@ -129,10 +133,10 @@ class RideBeginPassenger extends StatelessWidget {
Flexible(
child: Text(
controller.driverName,
style: const TextStyle(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.black87,
color: AppColor.writeColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -153,7 +157,7 @@ class RideBeginPassenger extends StatelessWidget {
Flexible(
child: Text(
'${controller.model}',
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
style: TextStyle(fontSize: 12, color: AppColor.grayColor),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -162,8 +166,9 @@ class RideBeginPassenger extends StatelessWidget {
padding:
const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: Colors.grey[100],
border: Border.all(color: Colors.black12),
color: AppColor.writeColor.withOpacity(0.05),
border: Border.all(
color: AppColor.grayColor.withOpacity(0.2)),
borderRadius: BorderRadius.circular(4),
),
child: Text(
@@ -200,7 +205,7 @@ class RideBeginPassenger extends StatelessWidget {
),
),
Text('SYP',
style: TextStyle(fontSize: 9, color: Colors.grey[600])),
style: TextStyle(fontSize: 9, color: AppColor.grayColor)),
],
),
),
@@ -287,8 +292,8 @@ class RideBeginPassenger extends StatelessWidget {
_compactBtn(
icon: Icons.info_outline_rounded,
label: 'Report'.tr,
color: Colors.grey[700]!,
bgColor: Colors.grey[200]!,
color: AppColor.grayColor,
bgColor: AppColor.writeColor.withOpacity(0.1),
onTap: () => Get.to(() => ComplaintPage()),
),
],
@@ -322,7 +327,7 @@ class RideBeginPassenger extends StatelessWidget {
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey[700],
color: AppColor.grayColor,
fontWeight: FontWeight.w500),
),
],

View File

@@ -50,18 +50,20 @@ class RideFromStartApp extends StatelessWidget {
bottom: 0, // ملتصق بالأسفل تماماً
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: const BoxDecoration(
color: Colors.white, // خلفية بيضاء نظيفة
decoration: BoxDecoration(
color: AppColor.secondaryColor, // خلفية متفاعلة
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
boxShadow: [
BoxShadow(
color: Colors.black12,
color: Get.isDarkMode
? Colors.black.withOpacity(0.4)
: Colors.black12,
blurRadius: 15.0,
spreadRadius: 5.0,
offset: Offset(0, -5),
offset: const Offset(0, -5),
),
],
),
@@ -76,7 +78,7 @@ class RideFromStartApp extends StatelessWidget {
height: 4,
margin: const EdgeInsets.only(bottom: 15),
decoration: BoxDecoration(
color: Colors.grey[300],
color: AppColor.grayColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
),
),
@@ -112,7 +114,7 @@ class RideFromStartApp extends StatelessWidget {
style: AppStyle.title.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
color: AppColor.writeColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -129,12 +131,15 @@ class RideFromStartApp extends StatelessWidget {
fontSize: 13, fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
Container(width: 1, height: 12, color: Colors.grey),
Container(
width: 1,
height: 12,
color: AppColor.grayColor.withOpacity(0.3)),
const SizedBox(width: 8),
Text(
"$carType - $carModel",
style: AppStyle.title.copyWith(
fontSize: 13, color: Colors.grey[600]),
fontSize: 13, color: AppColor.grayColor),
),
],
),
@@ -266,22 +271,22 @@ class RideFromStartApp extends StatelessWidget {
return Column(
children: [
Icon(icon,
color: AppColor.secondaryColor,
color: AppColor.primaryColor,
size: 22), // افترضت أن السكندري لون داكن، أو استخدم Primary
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 15,
color: Colors.black87,
color: AppColor.writeColor,
),
),
Text(
label,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
color: AppColor.grayColor,
),
),
],
@@ -292,7 +297,7 @@ class RideFromStartApp extends StatelessWidget {
return Container(
height: 30,
width: 1,
color: Colors.grey[300],
color: AppColor.grayColor.withOpacity(0.2),
);
}

View File

@@ -210,7 +210,7 @@ class CupertinoDriverListWidget extends StatelessWidget {
Get.back();
showDateTimePickerDialog(driver);
}));
print('${'Selected driver'.tr}: ${driver['NAME']}');
Log.print('${'Selected driver'.tr}: ${driver['NAME']}');
// Get.back(); // Close the dialog
},
),

View File

@@ -207,7 +207,7 @@ class PassengerWallet extends StatelessWidget {
subtitle: Text(subtitle.tr,
style: AppStyle.subtitle
.copyWith(color: AppColor.writeColor.withOpacity(0.6))),
trailing: const Icon(Icons.arrow_forward_ios_rounded,
trailing: Icon(Icons.arrow_forward_ios_rounded,
size: 16, color: AppColor.writeColor),
);
}

View File

@@ -1,3 +1,4 @@
import 'package:Intaleq/print.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/functions/encrypt_decrypt.dart';
import 'package:flutter/cupertino.dart';
@@ -457,7 +458,7 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
);
if (!didAuthenticate) {
print("❌ User did not authenticate with biometrics");
Log.print("❌ User did not authenticate with biometrics");
return;
}
}
@@ -495,4 +496,4 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
),
),
);
}
}

View File

@@ -16,8 +16,12 @@ import '../../../controller/functions/tts.dart';
import '../../../controller/home/decode_polyline_isolate.dart';
import '../../../main.dart';
import '../../../print.dart';
import 'dart:ui';
class NavigationController extends GetxController {
import '../../../services/offline_map_service.dart';
class NavigationController extends GetxController
with GetSingleTickerProviderStateMixin {
// ==========================================================================
// ── Tunables ──────────────────────────────────────────────────────────────
// ==========================================================================
@@ -57,11 +61,17 @@ class NavigationController extends GetxController {
/// Updated every tick via angle-aware lerp to eliminate snap/jitter.
double _smoothedHeading = 0.0;
// Animation for smooth tracking
AnimationController? _animController;
LatLng? _oldLoc;
LatLng? _targetLoc;
double currentSpeed = 0.0; // km/h
double totalDistance = 0.0; // metres accumulated this session
// MapLibre objects
Symbol? carSymbol;
Symbol? originSymbol;
Symbol? destinationSymbol;
Line? remainingRouteLine;
Line? traveledRouteLine;
@@ -155,14 +165,31 @@ class NavigationController extends GetxController {
@override
void onInit() {
super.onInit();
_animController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 1000));
_animController!.addListener(() {
if (_oldLoc != null && _targetLoc != null && _mapReady) {
final t = _animController!.value;
final lat = lerpDouble(_oldLoc!.latitude, _targetLoc!.latitude, t)!;
final lng = lerpDouble(_oldLoc!.longitude, _targetLoc!.longitude, t)!;
myLocation = LatLng(lat, lng);
if (isStyleLoaded) {
_updateCarMarker();
if (_fullRouteCoordinates.isNotEmpty && _cameraLockedToUser) {
animateCameraToPosition(myLocation!,
bearing: _smoothedHeading,
zoom: _targetZoom,
tilt: _targetTilt);
}
}
}
});
_initialize();
}
Future<void> _initialize() async {
await _getCurrentLocationAndStartUpdates();
if (!Get.isRegistered<TextToSpeechController>()) {
Get.put(TextToSpeechController());
}
}
@override
@@ -171,6 +198,7 @@ class NavigationController extends GetxController {
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
_debounce?.cancel();
_animController?.dispose();
mapController?.dispose();
placeDestinationController.dispose();
@@ -294,7 +322,10 @@ class NavigationController extends GetxController {
}
_lastDistanceLocation = newLoc;
myLocation = newLoc;
_oldLoc = myLocation ?? newLoc;
_targetLoc = newLoc;
_animController?.forward(from: 0.0);
_lastProcessedLocation = newLoc;
heading = position.heading;
@@ -306,23 +337,22 @@ class NavigationController extends GetxController {
currentSpeed = position.speed * 3.6;
if (isStyleLoaded) _updateCarMarker();
// Initial visual update if map is fresh
if (isStyleLoaded && myLocation == null) _updateCarMarker();
if (_fullRouteCoordinates.isNotEmpty) {
if (_cameraLockedToUser) {
animateCameraToPosition(myLocation!,
bearing: _smoothedHeading, zoom: _targetZoom, tilt: _targetTilt);
}
_updateTraveledPolylineSmart(myLocation!);
_checkNavigationStep(myLocation!);
_updateTraveledPolylineSmart(newLoc);
_checkNavigationStep(newLoc);
_recomputeETA();
// ── Off-route auto-recalculate ─────────────────────────────────────
_checkOffRoute(myLocation!);
_checkOffRoute(newLoc);
}
update();
} catch (_) {}
} catch (e) {
Log.print("Error occurred: $e");
}
}
// ==========================================================================
@@ -498,14 +528,14 @@ class NavigationController extends GetxController {
geometry: myLocation,
iconImage: 'car_icon',
iconSize: 1.0,
iconRotate: _smoothedHeading, // ← use smoothed heading
iconRotate: _smoothedHeading,
));
} else {
mapController!.updateSymbol(
carSymbol!,
SymbolOptions(
geometry: myLocation,
iconRotate: _smoothedHeading, // ← use smoothed heading
iconRotate: _smoothedHeading,
),
);
}
@@ -532,6 +562,50 @@ class NavigationController extends GetxController {
);
}
/// Safe wrapper for animateCamera Bounds to prevent native std::domain_error crash on iOS.
Future<void> _safeAnimateCameraBounds(LatLngBounds? bounds,
{double left = 60,
double top = 60,
double right = 60,
double bottom = 60}) async {
if (bounds == null || mapController == null) return;
try {
// Ensure the coordinates are valid (at least a small span)
final latSpan =
(bounds.northeast.latitude - bounds.southwest.latitude).abs();
final lngSpan =
(bounds.northeast.longitude - bounds.southwest.longitude).abs();
if (latSpan < 0.0001 && lngSpan < 0.0001) {
Log.print(
'⚠️ _safeAnimateCameraBounds: Point-sized bounds, zooming to center.');
mapController
?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 16));
return;
}
// Small delay for view stabilization
await Future.delayed(const Duration(milliseconds: 200));
await mapController?.animateCamera(
CameraUpdate.newLatLngBounds(
bounds,
left: left,
top: top,
right: right,
bottom: bottom,
),
);
} catch (e) {
Log.print('❌ _safeAnimateCameraBounds CRASH PREVENTED in Nav: $e');
try {
await mapController
?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 14));
} catch (_) {}
}
}
void onUserPanned() {
_cameraLockedToUser = false;
update();
@@ -625,38 +699,90 @@ class NavigationController extends GetxController {
// ==========================================================================
Future<void> getRoute(LatLng origin, LatLng destination) async {
// ── Routing Decision: Normal Points -> SaaS, Multi-Stop -> OSRM ──
// Note: NavigationController usually handles the active trip (normal points).
final Map<String, String> queryParams = {
'fromLat': origin.latitude.toString(),
'fromLng': origin.longitude.toString(),
'toLat': destination.latitude.toString(),
'toLng': destination.longitude.toString(),
};
final saasUri =
Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams);
// Fallback OSRM URL
final coords = "${origin.longitude},${origin.latitude};"
"${destination.longitude},${destination.latitude}";
final url =
final osrmUrl =
"$_routeApiBaseUrl/$coords?steps=true&overview=full&geometries=polyline";
try {
final response = await http.get(Uri.parse(url));
// 1. Try SaaS first
http.Response response = await http.get(saasUri, headers: {
'x-api-key': 'intaleq_secret_2026',
});
bool useSaaS = response.statusCode == 200;
if (!useSaaS) {
Log.print("⚠️ SaaS Route failed. Falling back to OSRM...");
response = await http.get(Uri.parse(osrmUrl));
}
if (response.statusCode != 200) {
mySnackbarWarning('تعذر الاتصال بخدمة التوجيه.');
return;
}
final data = jsonDecode(response.body);
if (data['code'] != 'Ok' || (data['routes'] as List).isEmpty) {
final bool isSaaS = useSaaS;
// ── 2. Data Extraction Logic ──────────────────────────────────
String pointsString = "";
dynamic mainRoute;
if (isSaaS) {
pointsString = data['points']?.toString() ?? "";
mainRoute = data; // SaaS structure is top-level
} else {
if (data['code'] != 'Ok' || (data['routes'] as List).isEmpty) {
mySnackbarWarning('لم يتم العثور على مسار.');
return;
}
mainRoute = data['routes'][0];
pointsString = mainRoute['geometry']?.toString() ?? "";
}
if (pointsString.isEmpty) {
mySnackbarWarning('لم يتم العثور على مسار.');
return;
}
final route = data['routes'][0];
_fullRouteCoordinates = await compute<String, List<LatLng>>(
decodePolylineIsolate, route['geometry'].toString());
decodePolylineIsolate, pointsString);
_lastTraveledIndexInFullRoute = 0;
if (isStyleLoaded) _updatePolylinesSets([], _fullRouteCoordinates);
final legs = route['legs'] as List;
if (legs.isNotEmpty) {
routeSteps = List<Map<String, dynamic>>.from(legs[0]['steps'] as List);
// ── Offline Cache: Ensure destination area is stored in memory/disk ───
if (_fullRouteCoordinates.isNotEmpty) {
OfflineMapService.instance
.downloadRegion(_fullRouteCoordinates.last, radiusKm: 2.0);
}
// Handle legs/steps & totals
final legs = mainRoute['legs'] as List?;
if (legs != null && legs.isNotEmpty) {
routeSteps = List<Map<String, dynamic>>.from(legs[0]['steps'] as List);
_routeTotalDistanceM = (legs[0]['distance'] as num).toDouble();
_routeTotalDurationS = (legs[0]['duration'] as num).toDouble();
} else {
// Fallback for SaaS which might have top-level distance/duration
routeSteps = [];
_routeTotalDistanceM = (mainRoute['distance'] as num).toDouble();
_routeTotalDurationS = (mainRoute['duration'] as num).toDouble();
}
if (_routeTotalDistanceM > 0) {
totalDistanceRemaining = _routeTotalDistanceM > 1000
? "${(_routeTotalDistanceM / 1000).toStringAsFixed(1)} كم"
: "${_routeTotalDistanceM.toStringAsFixed(0)} م";
@@ -665,8 +791,6 @@ class NavigationController extends GetxController {
estimatedTimeRemaining = minutes > 60
? "${(minutes / 60).floor()} س ${minutes % 60} د"
: "$minutes د";
} else {
routeSteps = [];
}
for (final step in routeSteps) {
@@ -689,20 +813,18 @@ class NavigationController extends GetxController {
Get.find<TextToSpeechController>().speakText(currentInstruction);
}
// ── 5. Camera Update (Safe) ───────────────────────────────────
if (_fullRouteCoordinates.length >= 2) {
final bounds = _boundsFromLatLngList(_fullRouteCoordinates);
final bounds =
data['bbox'] != null && (data['bbox'] as List).length == 4
? LatLngBounds(
southwest: LatLng(data['bbox'][1], data['bbox'][0]),
northeast: LatLng(data['bbox'][3], data['bbox'][2]),
)
: _boundsFromLatLngList(_fullRouteCoordinates);
final latDiff =
(bounds.northeast.latitude - bounds.southwest.latitude).abs();
final lngDiff =
(bounds.northeast.longitude - bounds.southwest.longitude).abs();
if (latDiff > 0.0001 || lngDiff > 0.0001) {
mapController?.animateCamera(CameraUpdate.newLatLngBounds(bounds,
bottom: 220, top: 150, left: 50, right: 50));
} else {
animateCameraToPosition(_fullRouteCoordinates.first, zoom: 15.0);
}
await _safeAnimateCameraBounds(bounds,
bottom: 220, top: 150, left: 50, right: 50);
}
update();
@@ -741,6 +863,7 @@ class NavigationController extends GetxController {
await clearRoute(isNewRoute: true);
if (isStyleLoaded && mapController != null) {
// Destination Marker (B)
destinationSymbol = await mapController!.addSymbol(SymbolOptions(
geometry: destination,
iconImage: 'dest_icon',
@@ -748,6 +871,15 @@ class NavigationController extends GetxController {
textField: infoWindowTitle,
textOffset: const Offset(0, 2),
));
// Start Marker (A)
if (myLocation != null) {
originSymbol = await mapController!.addSymbol(SymbolOptions(
geometry: myLocation,
iconImage: 'start_icon',
iconSize: 1.0,
));
}
}
if (myLocation != null) await getRoute(myLocation!, destination);
@@ -777,6 +909,10 @@ class NavigationController extends GetxController {
await mapController!.removeSymbol(destinationSymbol!);
destinationSymbol = null;
}
if (originSymbol != null && mapController != null) {
await mapController!.removeSymbol(originSymbol!);
originSymbol = null;
}
if (remainingRouteLine != null && mapController != null) {
await mapController!.removeLine(remainingRouteLine!);
remainingRouteLine = null;
@@ -807,8 +943,11 @@ class NavigationController extends GetxController {
Future<void> _loadCustomIcons() async {
if (mapController == null) return;
final carBytes = await rootBundle.load('assets/images/car.png');
final startBytes = await rootBundle.load('assets/images/A.png');
final destBytes = await rootBundle.load('assets/images/b.png');
await mapController!.addImage('car_icon', carBytes.buffer.asUint8List());
await mapController!.addImage('start_icon', startBytes.buffer.asUint8List());
await mapController!.addImage('dest_icon', destBytes.buffer.asUint8List());
}

View File

@@ -7,12 +7,22 @@ import 'package:maplibre_gl/maplibre_gl.dart';
import 'navigation_controller.dart';
// ─── Brand colours ───────────────────────────────────────────────────────────
const Color _kBlue = Color(0xFF1A73E8);
const Color _kBlueDark = Color(0xFF0D47A1);
const Color _kSurface = Color(0xFFFFFFFF);
const Color _kText = Color(0xFF1C1C1E);
const Color _kSubtext = Color(0xFF6B7280);
const Color _kGreen = Color(0xFF34A853);
// ─── Theme-aware Brand colours ──────────────────────────────────────────────
Color get _kBlue => const Color(0xFF1A73E8);
Color get _kBlueDark => const Color(0xFF0D47A1);
Color get _kSurface =>
Get.isDarkMode ? const Color(0xFF1E1E1E) : const Color(0xFFFFFFFF);
Color get _kText =>
Get.isDarkMode ? const Color(0xFFF5F5F7) : const Color(0xFF1C1C1E);
Color get _kSubtext =>
Get.isDarkMode ? Colors.white60 : const Color(0xFF6B7280);
Color get _kGreen => const Color(0xFF34A853);
Color get _kGlassSurface => Get.isDarkMode
? Colors.black.withOpacity(0.7)
: Colors.white.withOpacity(0.92);
Color get _kGlassBorder => Get.isDarkMode
? Colors.white.withOpacity(0.12)
: Colors.white.withOpacity(0.5);
class NavigationView extends StatelessWidget {
const NavigationView({super.key});
@@ -33,7 +43,9 @@ class NavigationView extends StatelessWidget {
onMapCreated: c.onMapCreated,
onStyleLoadedCallback: c.onStyleLoaded,
onMapLongClick: c.onMapLongPressed,
styleString: "assets/style.json",
styleString: Get.isDarkMode
? "assets/style_dark.json"
: "assets/style.json",
initialCameraPosition: CameraPosition(
target: c.myLocation ?? const LatLng(33.5138, 36.2765),
zoom: 16.0,
@@ -106,7 +118,7 @@ class _SearchBar extends StatelessWidget {
controller: controller.placeDestinationController,
onChanged: controller.onSearchChanged,
textInputAction: TextInputAction.search,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
color: _kText,
fontWeight: FontWeight.w500),
@@ -167,8 +179,10 @@ class _SearchResults extends StatelessWidget {
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: controller.placesDestination.length,
separatorBuilder: (_, __) =>
Divider(height: 1, color: Colors.grey[100], indent: 56),
separatorBuilder: (_, __) => Divider(
height: 1,
color: Get.isDarkMode ? Colors.white12 : Colors.grey[100],
indent: 56),
itemBuilder: (_, i) {
final place = controller.placesDestination[i];
final dist = place['distanceKm'] as double?;
@@ -187,8 +201,8 @@ class _SearchResults extends StatelessWidget {
color: _kBlue.withOpacity(0.08),
shape: BoxShape.circle,
),
child: const Icon(Icons.place_rounded,
color: _kBlue, size: 18),
child:
Icon(Icons.place_rounded, color: _kBlue, size: 18),
),
const SizedBox(width: 12),
Expanded(
@@ -196,7 +210,7 @@ class _SearchResults extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(place['name'] ?? '',
style: const TextStyle(
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.5,
color: _kText),
@@ -222,7 +236,7 @@ class _SearchResults extends StatelessWidget {
),
child: Text(
'${dist.toStringAsFixed(1)} كم',
style: const TextStyle(
style: TextStyle(
color: _kBlue,
fontSize: 12,
fontWeight: FontWeight.w600),
@@ -259,11 +273,14 @@ class _TurnBanner extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(12, 10, 12, 0),
child: Container(
decoration: BoxDecoration(
color: _kBlueDark,
color: Get.isDarkMode
? Colors.grey[900]?.withOpacity(0.95)
: _kBlueDark,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: _kBlueDark.withOpacity(0.35),
color: (Get.isDarkMode ? Colors.black : _kBlueDark)
.withOpacity(0.35),
blurRadius: 20,
offset: const Offset(0, 6)),
],
@@ -274,14 +291,14 @@ class _TurnBanner extends StatelessWidget {
children: [
// Turn arrow icon
Container(
width: 52,
height: 52,
width: 64,
height: 64,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.circular(16),
),
child: const Icon(Icons.turn_right_rounded,
color: Colors.white, size: 30),
color: Colors.white, size: 40),
),
const SizedBox(width: 14),
@@ -293,16 +310,16 @@ class _TurnBanner extends StatelessWidget {
Text(
controller.distanceToNextStep,
style: const TextStyle(
color: Colors.white70,
fontSize: 13,
fontWeight: FontWeight.w500),
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600),
),
const SizedBox(height: 2),
const SizedBox(height: 4),
Text(
controller.currentInstruction,
style: const TextStyle(
color: Colors.white,
fontSize: 19,
fontSize: 26,
fontWeight: FontWeight.bold,
height: 1.2),
maxLines: 2,
@@ -429,7 +446,7 @@ class _RouteSummaryCard extends StatelessWidget {
left: 0,
right: 0,
child: Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
color: _kSurface,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
boxShadow: [
@@ -520,20 +537,20 @@ class _InfoPill extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: color.withOpacity(0.08),
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: color.withOpacity(0.2)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: color, size: 15),
const SizedBox(width: 5),
Icon(icon, color: color, size: 22),
const SizedBox(width: 8),
Text(label,
style: TextStyle(
color: color, fontSize: 13.5, fontWeight: FontWeight.w700)),
color: color, fontSize: 18, fontWeight: FontWeight.w800)),
],
),
);
@@ -577,22 +594,27 @@ class _NavigationHUD extends StatelessWidget {
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFFF8F9FA),
color: Get.isDarkMode
? Colors.white.withOpacity(0.05)
: const Color(0xFFF8F9FA),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.withOpacity(0.15)),
border: Border.all(
color: Get.isDarkMode
? Colors.white10
: Colors.grey.withOpacity(0.15)),
),
child: Row(
children: [
Icon(Icons.arrow_forward_rounded,
size: 15, color: _kSubtext),
const SizedBox(width: 8),
size: 20, color: _kSubtext),
const SizedBox(width: 10),
Expanded(
child: Text(
controller.nextInstruction,
style: TextStyle(
color: _kSubtext,
fontSize: 13,
fontWeight: FontWeight.w500),
color: _kText,
fontSize: 16,
fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -636,13 +658,13 @@ class _NavigationHUD extends StatelessWidget {
child: Row(
children: [
const Icon(Icons.stop_rounded,
color: Colors.redAccent, size: 16),
const SizedBox(width: 5),
color: Colors.redAccent, size: 24),
const SizedBox(width: 6),
const Text('إيقاف',
style: TextStyle(
color: Colors.redAccent,
fontSize: 13,
fontWeight: FontWeight.w700)),
fontSize: 18,
fontWeight: FontWeight.bold)),
],
),
),
@@ -669,44 +691,61 @@ class _SpeedBadge extends StatelessWidget {
final bool fast = kmh > 100;
return Positioned(
bottom: MediaQuery.of(context).padding.bottom + 130,
left: 14,
child: Container(
width: 62,
height: 62,
decoration: BoxDecoration(
color: fast ? const Color(0xFFD93025) : _kSurface,
shape: BoxShape.circle,
border: Border.all(
color: fast ? Colors.red.withOpacity(0.3) : Colors.grey[200]!,
width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.12),
blurRadius: 12,
offset: const Offset(0, 4)),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$kmh',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: fast ? Colors.white : _kText,
height: 1),
bottom: MediaQuery.of(context).padding.bottom + 150,
left: 16,
child: Stack(
alignment: Alignment.center,
children: [
// Circular progress mimicking a speedometer
SizedBox(
width: 86,
height: 86,
child: CircularProgressIndicator(
value: (kmh / 140.0)
.clamp(0.0, 1.0), // Assuming 140 is max speed shown
strokeWidth: 6,
backgroundColor: Get.isDarkMode
? Colors.white10
: Colors.grey.withOpacity(0.3),
valueColor: AlwaysStoppedAnimation<Color>(
fast ? Colors.redAccent : _kBlue),
),
Text(
'كم/س',
style: TextStyle(
fontSize: 9,
color: fast ? Colors.white70 : _kSubtext,
fontWeight: FontWeight.w500),
),
Container(
width: 74,
height: 74,
decoration: BoxDecoration(
color: fast ? const Color(0xFFD93025) : _kSurface,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 16,
offset: const Offset(0, 6)),
],
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$kmh',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900,
color: fast ? Colors.white : _kText,
height: 1),
),
Text(
'كم/س',
style: TextStyle(
fontSize: 13,
color: fast ? Colors.white70 : _kSubtext,
fontWeight: FontWeight.w600),
),
],
),
),
],
),
);
}
@@ -735,7 +774,7 @@ class _LoadingOverlay extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(_kBlue),
strokeWidth: 3,
),
@@ -777,14 +816,14 @@ class _GlassCard extends StatelessWidget {
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.92),
color: _kGlassSurface,
borderRadius: BorderRadius.circular(borderRadius),
border: Border.all(color: Colors.white.withOpacity(0.5)),
border: Border.all(color: _kGlassBorder),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.07),
color: Colors.black.withOpacity(Get.isDarkMode ? 0.4 : 0.07),
blurRadius: 16,
offset: const Offset(0, 4)),
offset: const Offset(0, 8)),
],
),
padding: padding,

View File

@@ -8,13 +8,11 @@ import 'package:http/http.dart' as http;
import 'package:Intaleq/constant/api_key.dart';
import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/links.dart';
import 'package:Intaleq/controller/profile/profile_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:Intaleq/views/widgets/mycircular.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/log_out.dart';
// ─────────────────────────────────────────────────────────────────────────────
@@ -134,7 +132,7 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
color: AppColor.grayColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(2)),
),
const SizedBox(height: 8),
@@ -182,14 +180,14 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF4F6F9),
backgroundColor: AppColor.secondaryColor.withOpacity(0.96),
appBar: AppBar(
title: Text('My Profile'.tr,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 20)),
backgroundColor: const Color(0xFFF4F6F9),
style: AppStyle.headTitle2.copyWith(fontSize: 20)),
backgroundColor: AppColor.secondaryColor,
elevation: 0,
centerTitle: true,
iconTheme: const IconThemeData(color: Colors.black87),
iconTheme: IconThemeData(color: AppColor.writeColor),
),
body: GetBuilder<ProfileController>(
builder: (controller) {
@@ -373,7 +371,7 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
),
child: CircleAvatar(
radius: 52,
backgroundColor: Colors.grey[100],
backgroundColor: AppColor.secondaryColor,
child: ClipOval(
child: _isUploadingImage
? const SizedBox(
@@ -445,7 +443,8 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
decoration: BoxDecoration(
color: AppColor.primaryColor,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.5),
border:
Border.all(color: AppColor.secondaryColor, width: 2.5),
boxShadow: [
BoxShadow(
color: AppColor.primaryColor.withOpacity(0.4),
@@ -470,10 +469,10 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
children: [
Text(
fullName.isEmpty ? 'Passenger'.tr : fullName,
style: const TextStyle(
fontSize: 22,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black87,
color: AppColor.writeColor,
),
),
const SizedBox(width: 6),
@@ -497,8 +496,8 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
Text(
email,
style: TextStyle(
fontSize: 13,
color: Colors.grey[500],
fontSize: 14,
color: AppColor.grayColor,
fontWeight: FontWeight.w500),
),
],
@@ -546,18 +545,18 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 4, bottom: 10),
padding: const EdgeInsets.only(left: 8, bottom: 12),
child: Row(
children: [
Icon(sectionIcon, size: 14, color: Colors.grey[500]),
const SizedBox(width: 6),
Icon(sectionIcon, size: 15, color: AppColor.grayColor),
const SizedBox(width: 8),
Text(
title.toUpperCase(),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Colors.grey[500],
letterSpacing: 1.3,
fontSize: 15,
fontWeight: FontWeight.bold,
color: AppColor.grayColor,
letterSpacing: 1.2,
),
),
],
@@ -565,11 +564,13 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
),
Container(
decoration: BoxDecoration(
color: Colors.white,
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
color: Get.isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.04),
blurRadius: 12,
offset: const Offset(0, 4),
),
@@ -611,30 +612,34 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.5,
color: Colors.black87)),
fontSize: 16,
color: AppColor.writeColor)),
if (subtitle.isNotEmpty) ...[
const SizedBox(height: 3),
const SizedBox(height: 4),
Text(subtitle,
style:
TextStyle(color: Colors.grey[500], fontSize: 12.5)),
TextStyle(color: AppColor.grayColor, fontSize: 13)),
],
],
),
),
trailing ??
Icon(Icons.arrow_forward_ios_rounded,
size: 13, color: Colors.grey[350]),
size: 14, color: AppColor.grayColor.withOpacity(0.5)),
],
),
),
);
}
Widget _divider() =>
Divider(height: 1, thickness: 1, indent: 72, color: Colors.grey[100]);
Widget _divider() => Divider(
height: 1,
thickness: 1,
indent: 72,
endIndent: 16,
color: AppColor.grayColor.withOpacity(0.1));
// ─── Bottom Sheets ───────────────────────────────────────────────────────────
@@ -808,7 +813,8 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
Expanded(
child: Text(
'This action is permanent and cannot be undone.'.tr,
style: TextStyle(color: Colors.red[700], fontSize: 13),
style: TextStyle(
color: Colors.red.withOpacity(0.8), fontSize: 13),
),
),
],
@@ -838,7 +844,7 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
side: BorderSide(color: Colors.grey[300]!),
),
child: Text('Cancel'.tr,
style: TextStyle(color: Colors.grey[600])),
style: TextStyle(color: AppColor.grayColor)),
),
),
const SizedBox(width: 12),
@@ -895,27 +901,29 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
return TextField(
controller: controller,
keyboardType: type,
style: const TextStyle(fontSize: 15, color: Colors.black87),
style: TextStyle(fontSize: 16, color: AppColor.writeColor),
decoration: InputDecoration(
labelText: label,
hintText: hint,
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 13),
prefixIcon: Icon(icon, size: 20, color: Colors.grey[500]),
hintStyle:
TextStyle(color: AppColor.grayColor.withOpacity(0.5), fontSize: 14),
prefixIcon: Icon(icon, size: 20, color: AppColor.grayColor),
filled: true,
fillColor: const Color(0xFFF8F9FA),
fillColor: AppColor.secondaryColor,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(13),
borderSide: BorderSide(color: Colors.grey[200]!)),
borderSide:
BorderSide(color: AppColor.grayColor.withOpacity(0.15))),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(13),
borderSide: BorderSide(color: Colors.grey[200]!)),
borderSide:
BorderSide(color: AppColor.grayColor.withOpacity(0.15))),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(13),
borderSide:
const BorderSide(color: AppColor.primaryColor, width: 1.5)),
labelStyle: TextStyle(color: Colors.grey[500], fontSize: 13),
borderSide: BorderSide(color: AppColor.primaryColor, width: 1.5)),
labelStyle: TextStyle(color: AppColor.grayColor, fontSize: 14),
),
);
}
@@ -971,7 +979,8 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
title: Text(label,
style: TextStyle(
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected ? AppColor.primaryColor : Colors.black87)),
fontSize: 16,
color: isSelected ? AppColor.primaryColor : AppColor.writeColor)),
);
}
}
@@ -1001,9 +1010,9 @@ class _SheetWrapper extends StatelessWidget {
24,
MediaQuery.of(context).viewInsets.bottom + 28,
),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(26)),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(26)),
),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
@@ -1018,7 +1027,7 @@ class _SheetWrapper extends StatelessWidget {
height: 4,
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: Colors.grey[250],
color: AppColor.grayColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(2),
),
),
@@ -1038,10 +1047,10 @@ class _SheetWrapper extends StatelessWidget {
const SizedBox(width: 12),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
color: AppColor.writeColor,
),
),
],

View File

@@ -1,4 +1,7 @@
import 'package:Intaleq/controller/home/home_page_controller.dart';
import 'package:Intaleq/controller/local/local_controller.dart';
import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Intaleq/views/lang/languages.dart';
@@ -32,16 +35,14 @@ class SettingPage extends StatelessWidget {
Get.lazyPut(() => HomePageController());
return Scaffold(
backgroundColor:
const Color(0xFFF5F5F7), // A slightly off-white background
backgroundColor: AppColor.secondaryColor.withOpacity(0.94),
appBar: AppBar(
title: Text('Setting'.tr,
style: const TextStyle(
color: Colors.black87, fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
style: AppStyle.headTitle2.copyWith(fontSize: 20)),
backgroundColor: AppColor.secondaryColor,
elevation: 0.5,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black87),
icon: Icon(Icons.arrow_back_ios_new, color: AppColor.writeColor),
onPressed: () => Get.back(),
),
),
@@ -59,14 +60,27 @@ class SettingPage extends StatelessWidget {
subtitle: 'To change Language the App'.tr,
onTap: () => Get.to(() => const Language()),
),
// const Divider(height: 1, indent: 68, endIndent: 16),
// _buildSettingsTile(
// icon: Icons.map_outlined,
// color: Colors.green,
// title: 'Change Country'.tr,
// subtitle: 'You can change the Country to get all features'.tr,
// onTap: () => Get.to(() => const CountryPickerFromSetting()),
// ),
Divider(
height: 1,
indent: 68,
endIndent: 16,
color: AppColor.grayColor.withOpacity(0.1)),
GetBuilder<LocaleController>(
builder: (localeController) {
return _buildSettingsTile(
icon: Icons.palette_outlined,
color: Colors.deepPurpleAccent,
title: 'Appearance'.tr,
subtitle: (localeController.themeMode == ThemeMode.system
? 'System Default'.tr
: localeController.themeMode == ThemeMode.dark
? 'Dark Mode'.tr
: 'Light Mode'.tr)
.tr,
onTap: () => _showThemeSheet(context, localeController),
);
},
),
],
),
const SizedBox(height: 24),
@@ -136,7 +150,7 @@ class SettingPage extends StatelessWidget {
child: Text(
title,
style: TextStyle(
color: Colors.grey[700],
color: AppColor.grayColor,
fontWeight: FontWeight.bold,
fontSize: 15,
),
@@ -144,11 +158,73 @@ class SettingPage extends StatelessWidget {
);
}
void _showThemeSheet(BuildContext context, LocaleController controller) {
final options = [
{'label': 'System Default'.tr, 'mode': ThemeMode.system},
{'label': 'Light Mode'.tr, 'mode': ThemeMode.light},
{'label': 'Dark Mode'.tr, 'mode': ThemeMode.dark},
];
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 4,
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: AppColor.grayColor.withOpacity(0.3),
borderRadius: BorderRadius.circular(2),
),
),
Text('Select Appearance'.tr,
style: AppStyle.headTitle2.copyWith(fontSize: 18)),
const SizedBox(height: 20),
...options.map((opt) {
final isSelected = controller.themeMode == opt['mode'];
return ListTile(
title: Text(opt['label'] as String,
style: TextStyle(
color: isSelected
? AppColor.primaryColor
: AppColor.writeColor,
fontWeight:
isSelected ? FontWeight.bold : FontWeight.normal)),
trailing: isSelected
? Icon(Icons.check_circle, color: AppColor.primaryColor)
: null,
onTap: () {
Get.back();
controller.changeThemeMode(opt['mode'] as ThemeMode);
},
);
}).toList(),
const SizedBox(height: 20),
],
),
),
);
}
Widget _buildSettingsCard({required List<Widget> children}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
)
],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -175,10 +251,14 @@ class SettingPage extends StatelessWidget {
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: AppColor.writeColor)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
style: TextStyle(color: AppColor.grayColor, fontSize: 13)),
trailing:
Icon(Icons.chevron_right, color: AppColor.grayColor.withOpacity(0.5)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
);
}
@@ -201,9 +281,12 @@ class SettingPage extends StatelessWidget {
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: AppColor.writeColor)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
style: TextStyle(color: AppColor.grayColor, fontSize: 13)),
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF007AFF), // iOS-like blue

View File

@@ -6,14 +6,14 @@ import 'mydialoug.dart';
class MyCircleContainer extends StatelessWidget {
final Widget child;
final Color backgroundColor;
final Color borderColor;
final Color? backgroundColor;
final Color? borderColor;
MyCircleContainer({
Key? key,
required this.child,
this.backgroundColor = AppColor.secondaryColor,
this.borderColor = AppColor.accentColor,
this.backgroundColor,
this.borderColor,
}) : super(key: key);
final controller = Get.put(CircleController());
@@ -40,9 +40,9 @@ class MyCircleContainer extends StatelessWidget {
height: controller.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: controller.backgroundColor,
color: controller.isAccented ? AppColor.accentColor : (backgroundColor ?? AppColor.secondaryColor),
border: Border.all(
color: borderColor,
color: borderColor ?? AppColor.accentColor,
width: 1,
),
),
@@ -53,12 +53,10 @@ class MyCircleContainer extends StatelessWidget {
}
class CircleController extends GetxController {
Color backgroundColor = AppColor.secondaryColor;
bool isAccented = false;
double size = 40;
void changeColor() {
backgroundColor = backgroundColor == AppColor.secondaryColor
? AppColor.accentColor
: AppColor.secondaryColor;
isAccented = !isAccented;
size = 60;
update();
}

View File

@@ -26,18 +26,18 @@ class IconWidgetMenu extends StatelessWidget {
children: [
Container(
width: 40,
decoration: const BoxDecoration(
decoration: BoxDecoration(
color: AppColor.secondaryColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.secondaryColor,
offset: Offset(-2, -2),
offset: const Offset(-2, -2),
blurRadius: 0,
spreadRadius: 0,
blurStyle: BlurStyle.outer,
),
BoxShadow(
const BoxShadow(
color: AppColor.accentColor,
offset: Offset(3, 3),
blurRadius: 0,

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../../constant/style.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
class MyCircularProgressIndicatorWithTimer extends StatefulWidget {
final Color backgroundColor;
@@ -8,7 +9,7 @@ class MyCircularProgressIndicatorWithTimer extends StatefulWidget {
const MyCircularProgressIndicatorWithTimer({
Key? key,
this.backgroundColor = Colors.white,
this.backgroundColor = Colors.transparent,
required this.isLoading,
}) : super(key: key);
@@ -88,7 +89,7 @@ class _MyCircularProgressIndicatorWithTimerState
height: 200,
decoration: BoxDecoration(
color: widget.backgroundColor == Colors.transparent
? Colors.white.withOpacity(0.96)
? AppColor.secondaryColor.withOpacity(0.96)
: widget.backgroundColor,
shape: BoxShape.circle,
boxShadow: [
@@ -110,7 +111,9 @@ class _MyCircularProgressIndicatorWithTimerState
child: CircularProgressIndicator(
value: progress,
strokeWidth: 6,
backgroundColor: Colors.grey.shade100,
backgroundColor: Get.isDarkMode
? Colors.grey.shade800
: Colors.grey.shade100,
valueColor: AlwaysStoppedAnimation<Color>(timerColor),
strokeCap: StrokeCap.round,
),
@@ -139,9 +142,8 @@ class _MyCircularProgressIndicatorWithTimerState
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color: _isLowTime
? Colors.orangeAccent
: Colors.blueGrey.shade800,
color:
_isLowTime ? Colors.orangeAccent : AppColor.writeColor,
letterSpacing: 1.2,
fontFeatures: const [FontFeature.tabularFigures()],
),

View File

@@ -9,16 +9,13 @@ class MyScafolld extends StatelessWidget {
super.key,
required this.title,
required this.body,
this.action = const Icon(
Icons.clear,
color: AppColor.secondaryColor,
),
this.action,
required this.isleading,
});
final String title;
final List<Widget> body;
final Widget action;
final Widget? action;
final bool isleading;
@override
@@ -33,13 +30,18 @@ class MyScafolld extends StatelessWidget {
onPressed: () {
Get.back();
},
icon: const Icon(
icon: Icon(
Icons.arrow_back_ios_new,
color: AppColor.primaryColor,
),
)
: const SizedBox(),
actions: [action],
actions: [
action ?? Icon(
Icons.clear,
color: AppColor.secondaryColor,
)
],
title: Text(
title,
style: AppStyle.title.copyWith(fontSize: 30),

View File

@@ -62,8 +62,8 @@ class _GlassCard extends StatelessWidget {
borderRadius: BorderRadius.circular(_DC.radius),
gradient: LinearGradient(
colors: [
Colors.white.withOpacity(0.95),
Colors.white.withOpacity(0.88),
AppColor.secondaryColor.withOpacity(0.95),
AppColor.secondaryColor.withOpacity(0.88),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
@@ -82,7 +82,7 @@ class _GlassCard extends StatelessWidget {
),
],
border: Border.all(
color: Colors.white.withOpacity(0.6),
color: AppColor.secondaryColor.withOpacity(0.6),
width: 1.2,
),
),
@@ -247,7 +247,7 @@ class _SpeakButtonState extends State<_SpeakButton>
setState(() => _speaking = true);
_pulse.repeat(reverse: true);
final tts = Get.put(TextToSpeechController());
final tts = Get.find<TextToSpeechController>();
for (final t in widget.texts) {
await tts.speakText(t);
}
@@ -367,7 +367,7 @@ class MyDialog extends GetxController {
fontSize: 18,
fontWeight: FontWeight.w700,
letterSpacing: -0.4,
color: Colors.black87,
color: AppColor.writeColor,
),
),
@@ -467,7 +467,7 @@ class MyDialogContent extends GetxController {
fontSize: 17,
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
color: Colors.black87,
color: AppColor.writeColor,
),
),
),

View File

@@ -1815,6 +1815,22 @@ packages:
relative: true
source: path
version: "1.0.0"
sensors_plus:
dependency: "direct main"
description:
name: sensors_plus
sha256: "6898cd4490ffc27fea4de5976585e92fae55355175d46c6c3b3d719d42f9e230"
url: "https://pub.dev"
source: hosted
version: "5.0.1"
sensors_plus_platform_interface:
dependency: transitive
description:
name: sensors_plus_platform_interface
sha256: bc472d6cfd622acb4f020e726433ee31788b038056691ba433fec80e448a094f
url: "https://pub.dev"
source: hosted
version: "1.2.0"
share_plus:
dependency: "direct main"
description:
@@ -1917,7 +1933,7 @@ packages:
source: hosted
version: "1.10.2"
sqflite:
dependency: "direct main"
dependency: transitive
description:
name: sqflite
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03

View File

@@ -17,7 +17,6 @@ dependencies:
firebase_core: ^4.1.1
flutter_local_notifications: ^20.1.0
google_maps_flutter: ^2.5.0
sqflite: ^2.3.0
path: ^1.8.3
intl: ^0.20.2
http: ^1.2.2
@@ -29,6 +28,7 @@ dependencies:
animated_text_kit: ^4.2.2
flutter_secure_storage: ^10.0.0-beta.4
geolocator: ^14.0.2
sensors_plus: ^5.0.1
flutter_paypal: ^0.2.0
google_fonts: ^8.0.2
flutter_launcher_icons: ^0.14.2

23632
translations_head.dart Normal file

File diff suppressed because it is too large Load Diff