fix marker rendering & modernize riding widgets for dark mode - 2026-04-11
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
1804
assets/style_dark.json
Normal file
File diff suppressed because it is too large
Load Diff
281
inject_missing_translations.py
Normal file
281
inject_missing_translations.py
Normal 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!": "بعد هالوقت\\nما فيك تكنسل!",
|
||||
"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()
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 هو السيرفر الجانبي الخاص بموقع السائقين، حيث يتم إرسال تحديثات الموقع من التطبيق إلى هذا السيرفر، ومن ثم يقوم هذا السيرفر بتوزيع هذه التحديثات إلى الركاب المتصلين الذين يتابعون السائق في الوقت الحقيقي.
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:Intaleq/env/env.dart';
|
||||
// import 'package:Intaleq/env/env.dart';
|
||||
|
||||
class TableName {
|
||||
static const String placesFavorite = "placesFavorite";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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') {
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class DeviceInfoPlus {
|
||||
};
|
||||
deviceDataList.add(deviceData);
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) { Log.print("Error occurred: $e"); }
|
||||
|
||||
return deviceDataList;
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -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
@@ -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 يقوم بكل العمل
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
4
lib/env/env.dart
vendored
@@ -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
26238
lib/env/env.g.dart
vendored
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
81
lib/services/emergency_signal_service.dart
Normal file
81
lib/services/emergency_signal_service.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
106
lib/services/offline_map_service.dart
Normal file
106
lib/services/offline_map_service.dart
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import '../navigation/navigation_view.dart';
|
||||
|
||||
// --- الدالة الرئيسية بالتصميم الجديد ---
|
||||
GetBuilder<MapPassengerController> leftMainMenuIcons() {
|
||||
Get.put(TextToSpeechController());
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => Positioned(
|
||||
// تم تعديل الموضع ليتناسب مع التصميم الجديد
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
)),
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()],
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
18
pubspec.lock
18
pubspec.lock
@@ -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
|
||||
|
||||
@@ -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
23632
translations_head.dart
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user