This commit is contained in:
Hamza-Ayed
2024-12-17 01:14:39 +03:00
100 changed files with 17652 additions and 3855 deletions

5
.env
View File

@@ -5,6 +5,7 @@ accountSIDTwillo=QFx0qy456juj3839xuy2194q629q1fj0y7XrXlBl
serverAPI=QQQQobSrrFi:QVQ87xU7zwCvmZzZdaxuS2f23Y4mz7MzyOzr8od2br6KYyeFaTVLG3K3hx5ZaUyx7eYvAYpAVdKk-286NTRi3zs9iSOnXtXRIxswg3KecBmsl3VxJ9wO-vIpwu4Pv7dkHkXniuxMSDgWXrXlBl serverAPI=QQQQobSrrFi:QVQ87xU7zwCvmZzZdaxuS2f23Y4mz7MzyOzr8od2br6KYyeFaTVLG3K3hx5ZaUyx7eYvAYpAVdKk-286NTRi3zs9iSOnXtXRIxswg3KecBmsl3VxJ9wO-vIpwu4Pv7dkHkXniuxMSDgWXrXlBl
mapAPIKEY=QOmqZsFsutLDCtZCRIUAZAkB5v6AMkKEPMbJGa3XrXlBl mapAPIKEY=QOmqZsFsutLDCtZCRIUAZAkB5v6AMkKEPMbJGa3XrXlBl
twilloRecoveryCode=CAU79DHPH1BE9PUH4ETXTSXZXrXlBl twilloRecoveryCode=CAU79DHPH1BE9PUH4ETXTSXZXrXlBl
apiKeyHere=g_WNUb5L-7-F8oHpUmgIzH7ETeH9xZ8RwGG9_G8zX9A
authTokenTwillo=70u98ju0214xx4q0u74028u021u4qu65XrXlBl authTokenTwillo=70u98ju0214xx4q0u74028u021u4qu65XrXlBl
chatGPTkey=zg-4C26q4SYBKQeHZDqkWowC9XrxgUEfUy9JRw2rm6Q2adb3kjwXrXlBl chatGPTkey=zg-4C26q4SYBKQeHZDqkWowC9XrxgUEfUy9JRw2rm6Q2adb3kjwXrXlBl
transactionCloude=Qhcwilomqcoib:QVO_JNYED2XWA26YXKC2TP:YK1DVH6SJB31N3PE1UXrXlBl transactionCloude=Qhcwilomqcoib:QVO_JNYED2XWA26YXKC2TP:YK1DVH6SJB31N3PE1UXrXlBl
@@ -18,9 +19,9 @@ serverPHP=https://api.sefer.live/sefer
seferAlexandriaServer=https://seferalexandria.site/sefer seferAlexandriaServer=https://seferalexandria.site/sefer
seferPaymentServer=https://seferpw.shop/sefer seferPaymentServer=https://seferpw.shop/sefer
seferCairoServer=https://sefer.click/sefer seferCairoServer=https://sefer.click/sefer
seferGizaServer=https://sefergiza.site/sefer seferGizaServer=https://gizasefer.online/sefer
whatappID=369939736211879 whatappID=369939736211879
whatsapp=EEAAOtbZBSUK74BO93qYEsBTetiT5qGCHCdxDRXEH1cGUrlbHN2ZB4bVrFCR1ZC8xEVDtHeLUH6yHej2RPMpmoLD69AlqBJJDtQ7nrsmbeIRZCBGPoKueLQUaEMWPC2R6EJdZArqdFy1rv4ZAGJZBV9ifxvwwTAlw7dbzA3WEZBvWKPXswUkPP9UWM1fWEPL86buyH0IvEd6j9grk6l7rG6CAZD whatsapp=EAAOtbZBSUK74BO6yE1QwIBsRCjPDANdum66xap0ZA7OZA8LqEu8MZAts1kwr12eRiNXtvpJ2ZAFSY5dw3KVSyrUuH8boLjynxdFI4Gh1Q7BCHx275X2uZBwKWZCSrsVN17i6mZAFNYYd25sQv0ZBomeTk02ZCIJot4UqWxK9ZBvxsq1k2yS7lD2NsjZB5EHbpaYGLzxFJ2FCCSX6iHyKXab6ckfK7m19wo77in7Dl3YZD
cohere=Aulwd8y5SPWos0hJhG0toUf8gOhUUrpf5Q2TPmVGXrXlBl cohere=Aulwd8y5SPWos0hJhG0toUf8gOhUUrpf5Q2TPmVGXrXlBl
claudeAiAPI=zg-qbc-qvo39-xWOxIGwWTOzCFBnIYSKKhfyz_KVAvrH-6_4ZEJL68G_QBH26oeTOMMoQug9KuOjjKSP_A4S3SUDlbxR9duVzoQ-MkX_UQQQXrXlBl claudeAiAPI=zg-qbc-qvo39-xWOxIGwWTOzCFBnIYSKKhfyz_KVAvrH-6_4ZEJL68G_QBH26oeTOMMoQug9KuOjjKSP_A4S3SUDlbxR9duVzoQ-MkX_UQQQXrXlBl
payPalClientId=QALymfNI5Tzt4s-ysoz6vD4_nqX0SUtkC_qYV-Ugk5gaM_8Z-kg4L53k8Uux_4jEWXDkNpXGSWPpIzDFXrXlBl payPalClientId=QALymfNI5Tzt4s-ysoz6vD4_nqX0SUtkC_qYV-Ugk5gaM_8Z-kg4L53k8Uux_4jEWXDkNpXGSWPpIzDFXrXlBl

BIN
Archive.zip Normal file

Binary file not shown.

View File

@@ -26,18 +26,24 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
// apply plugin: 'com.google.gms.google-services' // apply plugin: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties') def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.withReader('UTF-8') { reader ->
keystoreProperties.load(reader)
}
}
android { android {
namespace "com.mobileapp.store.ride" namespace "com.mobileapp.store.ride"
compileSdkVersion 34 compileSdk 35
ndkVersion flutter.ndkVersion ndkVersion "26.1.10909125"
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
} }
kotlinOptions { kotlinOptions {
@@ -49,33 +55,45 @@ android {
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // Specify your unique Application ID
applicationId "com.mobileapp.store.ride" applicationId = "com.mobileapp.store.ride"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
<<<<<<< HEAD
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 34 targetSdkVersion 34
versionCode 65 versionCode 65
versionName '1.5.65' versionName '1.5.65'
multiDexEnabled =true multiDexEnabled =true
// manifestPlaceholders = [mapsApiKey: 'android/app/src/main/AndroidManifest.xml'] // manifestPlaceholders = [mapsApiKey: 'android/app/src/main/AndroidManifest.xml']
=======
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = 108
versionName = '1.6.108'
multiDexEnabled =true
// manifestPlaceholders can be specified here if needed
>>>>>>> 8813b4d
} }
signingConfigs { signingConfigs {
release { release {
keyAlias keystoreProperties['keyAlias'] keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword'] keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword'] storePassword keystoreProperties['storePassword']
} }
} }
buildTypes {
release {
signingConfig signingConfigs.release
}
}
buildTypes {
release {
signingConfig signingConfigs.release
// minifyEnabled true
// shrinkResources true
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
} }
flutter { flutter {
@@ -84,6 +102,14 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
// Optional dependencies like Firebase can be uncommented if needed
// implementation platform('com.google.firebase:firebase-bom:32.1.1') // implementation platform('com.google.firebase:firebase-bom:32.1.1')
implementation "com.stripe:stripe-android:20.47.0"
implementation 'com.stripe:paymentsheet:20.47.0'
// If push provisioning is needed, make sure you have the correct version:
// implementation "com.stripe:stripe-android-pushprovisioning:VERSION" // Replace VERSION with the correct one.
} }

30
android/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,30 @@
# Suppress warnings for specific Google ML Kit and Stripe classes
-dontwarn com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions$Builder
-dontwarn com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
-dontwarn com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions$Builder
-dontwarn com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
-dontwarn com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions$Builder
-dontwarn com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
-dontwarn com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions$Builder
-dontwarn com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivity$g
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider
# Keep rules for Google ML Kit
-keep class com.google.mlkit.vision.** { *; }
-keep class com.google.mlkit.vision.text.** { *; }
-keep class com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions { *; }
-keep class com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions$Builder { *; }
-keep class com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions { *; }
-keep class com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions$Builder { *; }
-keep class com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions { *; }
-keep class com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions$Builder { *; }
-keep class com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions { *; }
-keep class com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions$Builder { *; }
# Keep rules for Stripe
-keep class com.stripe.android.** { *; }
-keep class com.stripe.android.pushProvisioning.** { *; }

View File

@@ -10,6 +10,12 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUICKBOOT_POWERON" />
<uses-permission android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
@@ -25,6 +31,7 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:enableOnBackInvokedCallback="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
@@ -58,6 +65,17 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<activity <activity
android:name="com.yalantis.ucrop.UCropActivity" android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -1,6 +1,7 @@
<resources> <resources>
<string name="app_name">My App</string> <string name="app_name">My App</string>
<!-- <string name="default_notification_channel_id">ride_channel</string> --> <!-- <string name="default_notification_channel_id">ride_channel</string> -->
<string name="default_notification_channel_id">default_channel</string> <!-- <string name="default_notification_channel_id">default_channel</string> -->
<string name="default_notification_channel_id">high_importance_channel</string>
<string name="api_key">AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0</string> <string name="api_key">AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0</string>
</resources> </resources>

View File

@@ -12,7 +12,7 @@ buildscript {
// classpath 'com.android.tools.build:gradle:7.3.1' // classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.gms:google-services:4.3.15'
// END: FlutterFire Configuration // END: FlutterFire Configuration
classpath 'com.android.tools.build:gradle:7.3.0' classpath 'com.android.tools.build:gradle:8.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }

View File

@@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx4096M org.gradle.jvmargs=-Xmx4096M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

@@ -4,4 +4,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip

BIN
assets/images/car3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 KiB

After

Width:  |  Height:  |  Size: 82 KiB

BIN
assets/images/pinkBike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -9,54 +9,61 @@ PODS:
- Flutter - Flutter
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
- Flutter - Flutter
- Firebase/Auth (10.28.0): - Firebase/Auth (11.0.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseAuth (~> 10.28.0) - FirebaseAuth (~> 11.0.0)
- Firebase/CoreOnly (10.28.0): - Firebase/CoreOnly (11.0.0):
- FirebaseCore (= 10.28.0) - FirebaseCore (= 11.0.0)
- Firebase/Messaging (10.28.0): - Firebase/Messaging (11.0.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 10.28.0) - FirebaseMessaging (~> 11.0.0)
- firebase_auth (5.1.2): - firebase_auth (5.2.0):
- Firebase/Auth (= 10.28.0) - Firebase/Auth (= 11.0.0)
- firebase_core - firebase_core
- Flutter - Flutter
- firebase_core (3.2.0): - firebase_core (3.4.0):
- Firebase/CoreOnly (= 10.28.0) - Firebase/CoreOnly (= 11.0.0)
- Flutter - Flutter
- firebase_messaging (15.0.3): - firebase_messaging (15.1.0):
- Firebase/Messaging (= 10.28.0) - Firebase/Messaging (= 11.0.0)
- firebase_core - firebase_core
- Flutter - Flutter
- FirebaseAppCheckInterop (10.29.0) - FirebaseAppCheckInterop (11.5.0)
- FirebaseAuth (10.28.0): - FirebaseAuth (11.0.0):
- FirebaseAppCheckInterop (~> 10.17) - FirebaseAppCheckInterop (~> 11.0)
- FirebaseCore (~> 10.0) - FirebaseAuthInterop (~> 11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8) - FirebaseCore (~> 11.0)
- GoogleUtilities/Environment (~> 7.8) - FirebaseCoreExtension (~> 11.0)
- GTMSessionFetcher/Core (< 4.0, >= 2.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.0)
- GoogleUtilities/Environment (~> 8.0)
- GTMSessionFetcher/Core (~> 3.4)
- RecaptchaInterop (~> 100.0) - RecaptchaInterop (~> 100.0)
- FirebaseCore (10.28.0): - FirebaseAuthInterop (11.5.0)
- FirebaseCoreInternal (~> 10.0) - FirebaseCore (11.0.0):
- GoogleUtilities/Environment (~> 7.12) - FirebaseCoreInternal (~> 11.0)
- GoogleUtilities/Logger (~> 7.12) - GoogleUtilities/Environment (~> 8.0)
- FirebaseCoreInternal (10.29.0): - GoogleUtilities/Logger (~> 8.0)
- "GoogleUtilities/NSData+zlib (~> 7.8)" - FirebaseCoreExtension (11.4.1):
- FirebaseInstallations (10.29.0): - FirebaseCore (~> 11.0)
- FirebaseCore (~> 10.0) - FirebaseCoreInternal (11.5.0):
- GoogleUtilities/Environment (~> 7.8) - "GoogleUtilities/NSData+zlib (~> 8.0)"
- GoogleUtilities/UserDefaults (~> 7.8) - FirebaseInstallations (11.4.0):
- PromisesObjC (~> 2.1) - FirebaseCore (~> 11.0)
- FirebaseMessaging (10.28.0): - GoogleUtilities/Environment (~> 8.0)
- FirebaseCore (~> 10.0) - GoogleUtilities/UserDefaults (~> 8.0)
- FirebaseInstallations (~> 10.0) - PromisesObjC (~> 2.4)
- GoogleDataTransport (~> 9.3) - FirebaseMessaging (11.0.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.8) - FirebaseCore (~> 11.0)
- GoogleUtilities/Environment (~> 7.8) - FirebaseInstallations (~> 11.0)
- GoogleUtilities/Reachability (~> 7.8) - GoogleDataTransport (~> 10.0)
- GoogleUtilities/UserDefaults (~> 7.8) - GoogleUtilities/AppDelegateSwizzler (~> 8.0)
- nanopb (< 2.30911.0, >= 2.30908.0) - GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Reachability (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- nanopb (~> 3.30910.0)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_contacts (0.0.1):
- Flutter
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- Flutter - Flutter
- flutter_secure_storage (6.0.0): - flutter_secure_storage (6.0.0):
@@ -65,8 +72,11 @@ PODS:
- Flutter - Flutter
- geolocator_apple (1.2.0): - geolocator_apple (1.2.0):
- Flutter - Flutter
- Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1): - google_maps_flutter_ios (0.0.1):
- Flutter - Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 10.0, >= 8.4) - GoogleMaps (< 10.0, >= 8.4)
- google_sign_in_ios (0.0.1): - google_sign_in_ios (0.0.1):
- AppAuth (>= 1.7.4) - AppAuth (>= 1.7.4)
@@ -74,10 +84,9 @@ PODS:
- FlutterMacOS - FlutterMacOS
- GoogleSignIn (~> 7.1) - GoogleSignIn (~> 7.1)
- GTMSessionFetcher (>= 3.4.0) - GTMSessionFetcher (>= 3.4.0)
- GoogleDataTransport (9.4.1): - GoogleDataTransport (10.1.0):
- GoogleUtilities/Environment (~> 7.7) - nanopb (~> 3.30910.0)
- nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (~> 2.4)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleMaps (8.4.0): - GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0) - GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0) - GoogleMaps/Base (8.4.0)
@@ -87,29 +96,28 @@ PODS:
- AppAuth (< 2.0, >= 1.7.3) - AppAuth (< 2.0, >= 1.7.3)
- GTMAppAuth (< 5.0, >= 4.1.1) - GTMAppAuth (< 5.0, >= 4.1.1)
- GTMSessionFetcher/Core (~> 3.3) - GTMSessionFetcher/Core (~> 3.3)
- GoogleUtilities/AppDelegateSwizzler (7.13.3): - GoogleUtilities/AppDelegateSwizzler (8.0.2):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network - GoogleUtilities/Network
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Environment (7.13.3): - GoogleUtilities/Environment (8.0.2):
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- PromisesObjC (< 3.0, >= 1.2) - GoogleUtilities/Logger (8.0.2):
- GoogleUtilities/Logger (7.13.3):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Network (7.13.3): - GoogleUtilities/Network (8.0.2):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Reachability - GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.13.3)": - "GoogleUtilities/NSData+zlib (8.0.2)":
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Privacy (7.13.3) - GoogleUtilities/Privacy (8.0.2)
- GoogleUtilities/Reachability (7.13.3): - GoogleUtilities/Reachability (8.0.2):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/UserDefaults (8.0.2):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GTMAppAuth (4.1.1): - GTMAppAuth (4.1.1):
@@ -129,13 +137,14 @@ PODS:
- Flutter - Flutter
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS
- location (0.0.1): - location (0.0.1):
- Flutter - Flutter
- nanopb (2.30910.0): - nanopb (3.30910.0):
- nanopb/decode (= 2.30910.0) - nanopb/decode (= 3.30910.0)
- nanopb/encode (= 2.30910.0) - nanopb/encode (= 3.30910.0)
- nanopb/decode (2.30910.0) - nanopb/decode (3.30910.0)
- nanopb/encode (2.30910.0) - nanopb/encode (3.30910.0)
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
@@ -155,42 +164,42 @@ PODS:
- sqflite (0.0.3): - sqflite (0.0.3):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- Stripe (23.27.6): - Stripe (23.28.3):
- StripeApplePay (= 23.27.6) - StripeApplePay (= 23.28.3)
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripePayments (= 23.27.6) - StripePayments (= 23.28.3)
- StripePaymentsUI (= 23.27.6) - StripePaymentsUI (= 23.28.3)
- StripeUICore (= 23.27.6) - StripeUICore (= 23.28.3)
- stripe_ios (0.0.1): - stripe_ios (0.0.1):
- Flutter - Flutter
- Stripe (~> 23.27.0) - Stripe (~> 23.28.0)
- StripeApplePay (~> 23.27.0) - StripeApplePay (~> 23.28.0)
- StripeFinancialConnections (~> 23.27.0) - StripeFinancialConnections (~> 23.28.0)
- StripePayments (~> 23.27.0) - StripePayments (~> 23.28.0)
- StripePaymentSheet (~> 23.27.0) - StripePaymentSheet (~> 23.28.0)
- StripePaymentsUI (~> 23.27.0) - StripePaymentsUI (~> 23.28.0)
- StripeApplePay (23.27.6): - StripeApplePay (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripeCore (23.27.6) - StripeCore (23.28.3)
- StripeFinancialConnections (23.27.6): - StripeFinancialConnections (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripeUICore (= 23.27.6) - StripeUICore (= 23.28.3)
- StripePayments (23.27.6): - StripePayments (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripePayments/Stripe3DS2 (= 23.27.6) - StripePayments/Stripe3DS2 (= 23.28.3)
- StripePayments/Stripe3DS2 (23.27.6): - StripePayments/Stripe3DS2 (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripePaymentSheet (23.27.6): - StripePaymentSheet (23.28.3):
- StripeApplePay (= 23.27.6) - StripeApplePay (= 23.28.3)
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripePayments (= 23.27.6) - StripePayments (= 23.28.3)
- StripePaymentsUI (= 23.27.6) - StripePaymentsUI (= 23.28.3)
- StripePaymentsUI (23.27.6): - StripePaymentsUI (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- StripePayments (= 23.27.6) - StripePayments (= 23.28.3)
- StripeUICore (= 23.27.6) - StripeUICore (= 23.28.3)
- StripeUICore (23.27.6): - StripeUICore (23.28.3):
- StripeCore (= 23.27.6) - StripeCore (= 23.28.3)
- TOCropViewController (2.7.4) - TOCropViewController (2.7.4)
- uni_links (0.0.1): - uni_links (0.0.1):
- Flutter - Flutter
@@ -205,6 +214,7 @@ PODS:
- Flutter - Flutter
- webview_flutter_wkwebview (0.0.1): - webview_flutter_wkwebview (0.0.1):
- Flutter - Flutter
- FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`)
@@ -213,6 +223,7 @@ DEPENDENCIES:
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
@@ -237,7 +248,7 @@ DEPENDENCIES:
- vibration (from `.symlinks/plugins/vibration/ios`) - vibration (from `.symlinks/plugins/vibration/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
@@ -245,10 +256,13 @@ SPEC REPOS:
- Firebase - Firebase
- FirebaseAppCheckInterop - FirebaseAppCheckInterop
- FirebaseAuth - FirebaseAuth
- FirebaseAuthInterop
- FirebaseCore - FirebaseCore
- FirebaseCoreExtension
- FirebaseCoreInternal - FirebaseCoreInternal
- FirebaseInstallations - FirebaseInstallations
- FirebaseMessaging - FirebaseMessaging
- Google-Maps-iOS-Utils
- GoogleDataTransport - GoogleDataTransport
- GoogleMaps - GoogleMaps
- GoogleSignIn - GoogleSignIn
@@ -281,6 +295,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios" :path: ".symlinks/plugins/firebase_messaging/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_contacts:
:path: ".symlinks/plugins/flutter_contacts/ios"
flutter_local_notifications: flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios" :path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage: flutter_secure_storage:
@@ -330,41 +346,45 @@ EXTERNAL SOURCES:
wakelock_plus: wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios" :path: ".symlinks/plugins/wakelock_plus/ios"
webview_flutter_wkwebview: webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios" :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS: SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
Firebase: 5121c624121af81cbc81df3bda414b3c28c4f3c3 Firebase: 9f574c08c2396885b5e7e100ed4293d956218af9
firebase_auth: e778ee89483b86fe4200d1f8e9a1c52aa5fb64a8 firebase_auth: 16ac5db3d064db837ecd845080d7e18e4be7c66d
firebase_core: a9d0180d5285527884d07a41eb4a9ec9ed12cdb6 firebase_core: ceec591a66629daaee82d3321551692c4a871493
firebase_messaging: ccc82a143a74de75f440a4e413dbbb37ec3fddbc firebase_messaging: 15d8b557010f3bb7b98d0302e1c7c8fbcd244425
FirebaseAppCheckInterop: 6a1757cfd4067d8e00fccd14fcc1b8fd78cfac07 FirebaseAppCheckInterop: d265d9f4484e7ec1c591086408840fdd383d1213
FirebaseAuth: 3d872fbbfc4223edeb72769e488f325fa8b0a4a9 FirebaseAuth: d5cf28be74d7e82257f6a3f717509eff70d3cf4a
FirebaseCore: 857dc1c6dd1255675047404d8466f7dfaac5d779 FirebaseAuthInterop: 1219bee9b23e6ebe84c256a0d95adab53d11c331
FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934 FirebaseCore: 3cf438f431f18c12cdf2aaf64434648b63f7e383
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e
FirebaseMessaging: 087a7c7cadef7b9239f005bc4db823894844f323 FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
FirebaseMessaging: d2d1d9c62c46dd2db49a952f7deb5b16ad2c9742
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d
geolocator_apple: 6cbaf322953988e009e5ecb481f07efece75c450 geolocator_apple: 6cbaf322953988e009e5ecb481f07efece75c450
google_maps_flutter_ios: 5bc2be60ad012e79b182ce0fb0ef5030a50fb03e Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: e31555a04d1986ab130f2b9f24b6cdc861acc6d3
google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
location: d5cf8598915965547c3f36761ae9cc4f4e87d22e location: d5cf8598915965547c3f36761ae9cc4f4e87d22e
nanopb: 438bc412db1928dac798aa6fd75726007be04262 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
@@ -374,22 +394,22 @@ SPEC CHECKSUMS:
share: 0b2c3e82132f5888bccca3351c504d0003b3b410 share: 0b2c3e82132f5888bccca3351c504d0003b3b410
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
Stripe: 9fec845645e39f371e6898926d096fd9c2feb5a5 Stripe: cdf416cf2efe286f532a6306de0fcaa0ecc8c71a
stripe_ios: 03c617acee72e48a2d055d096a4b0ed2afebb256 stripe_ios: 91946e5c07e0a0dc0e1484ee6659e1f90a302cf3
StripeApplePay: 5f017e8dfe259fafbab70137776189deef754bb2 StripeApplePay: efb62ffc08e6cd4f161d77ddb45de2451075c54e
StripeCore: 01ec57f0dddfe742054dc6a322f811426c25313d StripeCore: 9731f05e327c3dcaf7d7abd116840ceaa9482bbe
StripeFinancialConnections: 56698cb6274bf89fb8c76b934f6156f368e97765 StripeFinancialConnections: 46c0049aaab3a179193502bce4a8096eb7b73f55
StripePayments: 6adf11faf1b7038e77aa97019410305c6adca79d StripePayments: dd1867a620b0b8b5e294e9ff2f1f7b7770765f47
StripePaymentSheet: 3eaf870c4388e44b0cc37e4c69d00b6957fd8bd7 StripePaymentSheet: d155dfde74e90784d054deffb4f561a1f6dd638f
StripePaymentsUI: 59ccddeacad592b09fa67e8d641340820ddb4751 StripePaymentsUI: c24f990b03a68a7f6fe704b15dd487e7bb6b603e
StripeUICore: 879bbf5889265db13f52fac8aad7a176ba62481f StripeUICore: f2d514e900c37436dc5427fdf2c29d68ab1c2935
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
vibration: 7d883d141656a1c1a6d8d238616b2042a51a1241 vibration: 7d883d141656a1c1a6d8d238616b2042a51a1241
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1 webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
PODFILE CHECKSUM: d9271c147dd54ffd9ca5d77bf00ca21a1c9a5961 PODFILE CHECKSUM: d9271c147dd54ffd9ca5d77bf00ca21a1c9a5961

View File

@@ -467,6 +467,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@@ -594,6 +595,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@@ -651,6 +653,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";

View File

@@ -4,7 +4,7 @@ import FirebaseCore
import GoogleMaps import GoogleMaps
// import Constants // import Constants
@UIApplicationMain @main
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
@@ -37,11 +41,21 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<<<<<<< HEAD
<string>51</string> <string>51</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>4.3.51</string> <string>4.3.51</string>
=======
<string>78</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>4.3.78</string>
<key>NSHumanReadableCopyright</key>
<string></string>
>>>>>>> 8813b4d
<key>FirebaseAppDelegateProxyEnabled</key> <key>FirebaseAppDelegateProxyEnabled</key>
<string>NO</string> <string>NO</string>
<key>GMSApiKey</key> <key>GMSApiKey</key>

BIN
lib.zip Normal file

Binary file not shown.

View File

@@ -51,6 +51,7 @@ class AK {
X.r(X.r(X.r(Env.payMobApikey, cn), cC), cs); X.r(X.r(X.r(Env.payMobApikey, cn), cC), cs);
static final String integrationIdPayMobWallet = static final String integrationIdPayMobWallet =
X.r(X.r(X.r(Env.integrationIdPayMobWallet, cn), cC), cs); X.r(X.r(X.r(Env.integrationIdPayMobWallet, cn), cC), cs);
static final String apiKeyHere = Env.apiKeyHere;
static final String smsPasswordEgypt = static final String smsPasswordEgypt =
X.r(X.r(X.r(Env.smsPasswordEgypt, cn), cC), cs); X.r(X.r(X.r(Env.smsPasswordEgypt, cn), cC), cs);
static final String ocpApimSubscriptionKey = Env.ocpApimSubscriptionKey; static final String ocpApimSubscriptionKey = Env.ocpApimSubscriptionKey;

View File

@@ -10,14 +10,21 @@ class BoxName {
static const String carType = "carType"; static const String carType = "carType";
static const String carPlate = "carPlate"; static const String carPlate = "carPlate";
static const String packagInfo = "packagInfo"; static const String packagInfo = "packagInfo";
static const String isVerified = '0'; static const String isVerified = 'isVerified';
static const String isFirstTime = 'isFirstTime';
static const String isSavedPhones = 'isSavedPhones';
static const String statusDriverLocation = "statusDriverLocation"; static const String statusDriverLocation = "statusDriverLocation";
static const String isTest = "isTest";
static const String password = "password"; static const String password = "password";
static const String validity = "validity";
static const String promo = "promo";
static const String discount = "discount";
static const String arrivalTime = "arrivalTime"; static const String arrivalTime = "arrivalTime";
static const String passwordDriver = "passwordDriver"; static const String passwordDriver = "passwordDriver";
static const String agreeTerms = "agreeTerms"; static const String agreeTerms = "agreeTerms";
static const String addWork = 'addWork'; static const String addWork = 'addWork';
static const String addHome = 'addHome'; static const String addHome = 'addHome';
static const String placesDestination = 'placesDestination';
static const String tipPercentage = 'tipPercentage'; static const String tipPercentage = 'tipPercentage';
static const String accountIdStripeConnect = "accountIdStripeConnect"; static const String accountIdStripeConnect = "accountIdStripeConnect";
static const String faceDetectTimes = "faceDetectTimes"; static const String faceDetectTimes = "faceDetectTimes";
@@ -25,6 +32,11 @@ class BoxName {
static const String sosPhoneDriver = "sosPhoneDriver"; static const String sosPhoneDriver = "sosPhoneDriver";
static const String passengerID = "pasengerID"; static const String passengerID = "pasengerID";
static const String phone = "phone"; static const String phone = "phone";
static const String package = "package";
static const String isInstall = "isInstall";
static const String isGiftToken = "isGiftToken";
static const String inviteCode = "inviteCode";
static const String phoneWallet = "phoneWallet";
static const String phoneDriver = "phoneDriver"; static const String phoneDriver = "phoneDriver";
static const String dobDriver = "dobDriver"; static const String dobDriver = "dobDriver";
static const String sexDriver = "sexDriver"; static const String sexDriver = "sexDriver";
@@ -68,7 +80,6 @@ class BoxName {
static const String vin = "vin"; static const String vin = "vin";
static const String isvibrate = "isvibrate"; static const String isvibrate = "isvibrate";
static const String make = "make"; static const String make = "make";
static const String hourWait = "hourWait";
static const String model = "model"; static const String model = "model";
static const String year = "year"; static const String year = "year";
static const String expirationDate = "expirationDate"; static const String expirationDate = "expirationDate";

View File

@@ -14,6 +14,8 @@ class AppLink {
// static final String server = Env.serverPHP; // static final String server = Env.serverPHP;
static String googleMapsLink = 'https://maps.googleapis.com/maps/api/'; static String googleMapsLink = 'https://maps.googleapis.com/maps/api/';
static String searcMaps =
'https://autosuggest.search.hereapi.com/v1/autosuggest';
static String llama = 'https://api.llama-api.com/chat/completions'; static String llama = 'https://api.llama-api.com/chat/completions';
static String gemini = static String gemini =
'https://generativelanguage.googleapis.com/v1beta3/models/text-bison-001:generateText'; 'https://generativelanguage.googleapis.com/v1beta3/models/text-bison-001:generateText';
@@ -51,16 +53,20 @@ class AppLink {
//=======================promo===================ride.mobile-app.store/ride/promo/get.php //=======================promo===================ride.mobile-app.store/ride/promo/get.php
static String promo = '$server/ride/promo'; static String promo = '$server/ride/promo';
static String getPassengersPromo = "$promo/get.php"; static String getPassengersPromo = "$promo/get.php";
static String getPromoFirst = "$promo/getPromoFirst.php";
static String getPromoBytody = "$promo/getPromoBytody.php"; static String getPromoBytody = "$promo/getPromoBytody.php";
static String addPassengersPromo = "$promo/add.php"; static String addPassengersPromo = "$promo/add.php";
static String deletePassengersPromo = "$promo/delete.php"; static String deletePassengersPromo = "$promo/delete.php";
static String updatePassengersPromo = "$promo/update.php"; static String updatePassengersPromo = "$promo/update.php";
//===============contact==========================
static String savePhones = "$server/ride/egyptPhones/add.php";
static String getPhones = "$server/ride/egyptPhones/get.php";
////=======================cancelRide=================== ////=======================cancelRide===================
static String ride = '$server/ride'; static String ride = '$server/ride';
static String addCancelRideFromPassenger = static String addCancelRideFromPassenger = "$server/ride/cancelRide/add.php";
"$endPoint/ride/cancelRide/add.php"; static String cancelRide = "$server/ride/cancelRide/get.php";
static String cancelRide = "$endPoint/ride/cancelRide/get.php";
//-----------------ridessss------------------ //-----------------ridessss------------------
static String addRides = "$ride/rides/add.php"; static String addRides = "$ride/rides/add.php";
static String getRides = "$endPoint/ride/rides/get.php"; static String getRides = "$endPoint/ride/rides/get.php";
@@ -70,10 +76,10 @@ class AppLink {
"$endPoint/ride/rides/getRideStatusBegin.php"; "$endPoint/ride/rides/getRideStatusBegin.php";
static String getRideStatusFromStartApp = static String getRideStatusFromStartApp =
"$ride/rides/getRideStatusFromStartApp.php"; "$ride/rides/getRideStatusFromStartApp.php";
static String updateRides = "$endPoint/ride/rides/update.php"; static String updateRides = "$server/ride/rides/update.php";
static String updateStausFromSpeed = static String updateStausFromSpeed =
"$endPoint/ride/rides/updateStausFromSpeed.php"; "$server/ride/rides/updateStausFromSpeed.php";
static String deleteRides = "$endPoint/ride/rides/delete.php"; static String deleteRides = "$server/ride/rides/delete.php";
//-----------------DriverPayment------------------ //-----------------DriverPayment------------------
static String adddriverScam = "$ride/driver_scam/add.php"; static String adddriverScam = "$ride/driver_scam/add.php";
@@ -105,13 +111,28 @@ class AppLink {
"$ride/notificationPassenger/update.php"; "$ride/notificationPassenger/update.php";
//-----------------Driver NotificationCaptain------------------ //-----------------Driver NotificationCaptain------------------
static String addNotificationCaptain = "$ride/notificationCaptain/add.php"; static String addNotificationCaptain = "$ride/notificationCaptain/add.php";
static String addWaitingRide = "$ride/notificationCaptain/addWaitingRide.php"; static String addWaitingRide =
static String getRideWaiting = "$ride/notificationCaptain/getRideWaiting.php"; "$server/ride/notificationCaptain/addWaitingRide.php";
static String updateWaitingTrip =
"$server/ride/notificationCaptain/updateWaitingTrip.php";
static String getRideWaiting =
"$endPoint/ride/notificationCaptain/getRideWaiting.php";
static String getNotificationCaptain = "$ride/notificationCaptain/get.php"; static String getNotificationCaptain = "$ride/notificationCaptain/get.php";
static String updateNotificationCaptain = static String updateNotificationCaptain =
"$ride/notificationCaptain/update.php"; "$ride/notificationCaptain/update.php";
static String deleteNotificationCaptain = static String deleteNotificationCaptain =
"$ride/notificationCaptain/delete.php"; "$ride/notificationCaptain/delete.php";
//-----------------invitor------------------
static String addInviteDriver = "$server/ride/invitor/add.php";
static String addInvitationPassenger =
"$server/ride/invitor/addInvitationPassenger.php";
static String getInviteDriver = "$server/ride/invitor/get.php";
static String getDriverInvitationToPassengers =
"$server/ride/invitor/getDriverInvitationToPassengers.php";
static String updateInviteDriver = "$server/ride/invitor/update.php";
static String updatePassengerGift =
"$server/ride/invitor/updatePassengerGift.php";
//-----------------Api Key------------------ //-----------------Api Key------------------
static String addApiKey = "$ride/apiKey/add.php"; static String addApiKey = "$ride/apiKey/add.php";
static String getApiKey = "$ride/apiKey/get.php"; static String getApiKey = "$ride/apiKey/get.php";
@@ -121,6 +142,7 @@ class AppLink {
//-----------------Feed Back------------------ //-----------------Feed Back------------------
static String addFeedBack = "$ride/feedBack/add.php"; static String addFeedBack = "$ride/feedBack/add.php";
static String uploadAudio = "$ride/feedBack/upload_audio.php";
static String getFeedBack = "$ride/feedBack/get.php"; static String getFeedBack = "$ride/feedBack/get.php";
static String updateFeedBack = "$ride/feedBack/updateFeedBack.php"; static String updateFeedBack = "$ride/feedBack/updateFeedBack.php";
@@ -142,7 +164,8 @@ class AppLink {
static String updateLicense = "$ride/license/updateFeedBack.php"; static String updateLicense = "$ride/license/updateFeedBack.php";
//-----------------RegisrationCar------------------ //-----------------RegisrationCar------------------
static String addRegisrationCar = "$ride/RegisrationCar/add.php"; static String addRegisrationCar = "$ride/RegisrationCar/add.php";
static String getRegisrationCar = "$endPoint/ride/RegisrationCar/get.php"; static String getRegisrationCar =
"${box.read(BoxName.serverChosen)}/ride/RegisrationCar/get.php";
static String selectDriverAndCarForMishwariTrip = static String selectDriverAndCarForMishwariTrip =
"$ride/RegisrationCar/selectDriverAndCarForMishwariTrip.php"; "$ride/RegisrationCar/selectDriverAndCarForMishwariTrip.php";
static String updateRegisrationCar = "$ride/RegisrationCar/update.php"; static String updateRegisrationCar = "$ride/RegisrationCar/update.php";
@@ -150,6 +173,7 @@ class AppLink {
//-----------------mishwari------------------ //-----------------mishwari------------------
static String addMishwari = "$ride/mishwari/add.php"; static String addMishwari = "$ride/mishwari/add.php";
static String cancelMishwari = "$ride/mishwari/cancel.php";
static String getMishwari = "$ride/mishwari/get.php"; static String getMishwari = "$ride/mishwari/get.php";
//-----------------DriverOrder------------------ //-----------------DriverOrder------------------
@@ -163,6 +187,8 @@ class AppLink {
// ===================================== // =====================================
static String addRateToPassenger = "$ride/rate/add.php"; static String addRateToPassenger = "$ride/rate/add.php";
static String savePlacesServer = "$ride/places/add.php";
static String getapiKey = "$ride/apiKey/get.php";
static String addRateToDriver = "$ride/rate/addRateToDriver.php"; static String addRateToDriver = "$ride/rate/addRateToDriver.php";
static String getDriverRate = "$ride/rate/getDriverRate.php"; static String getDriverRate = "$ride/rate/getDriverRate.php";
static String getPassengerRate = "$ride/rate/getPassengerRate.php"; static String getPassengerRate = "$ride/rate/getPassengerRate.php";
@@ -183,12 +209,14 @@ class AppLink {
static String uploadEgypt = "$server/uploadEgypt.php"; static String uploadEgypt = "$server/uploadEgypt.php";
//==================certifcate========== //==================certifcate==========
static String location = '$endPoint/ride/location'; static String location = '${box.read(BoxName.serverChosen)}/ride/location';
static String getCarsLocationByPassenger = "$location/get.php"; static String getCarsLocationByPassenger = "$location/get.php";
static String addpassengerLocation = "$location/addpassengerLocation.php"; static String addpassengerLocation = "$location/addpassengerLocation.php";
static String getCarsLocationByPassengerSpeed = "$location/getSpeed.php"; static String getCarsLocationByPassengerSpeed = "$location/getSpeed.php";
static String getCarsLocationByPassengerComfort = "$location/getComfort.php"; static String getCarsLocationByPassengerComfort = "$location/getComfort.php";
static String getCarsLocationByPassengerBalash = "$location/getBalash.php"; static String getCarsLocationByPassengerBalash = "$location/getBalash.php";
static String getCarsLocationByPassengerPinkBike =
"$location/getPinkBike.php";
static String getCarsLocationByPassengerDelivery = static String getCarsLocationByPassengerDelivery =
"$location/getDelivery.php"; "$location/getDelivery.php";
static String getLocationParents = "$location/getLocationParents.php"; static String getLocationParents = "$location/getLocationParents.php";
@@ -239,7 +267,10 @@ class AppLink {
static String deletecaptainAccounr = "$authCaptin/deletecaptainAccounr.php"; static String deletecaptainAccounr = "$authCaptin/deletecaptainAccounr.php";
static String updateAccountBank = "$authCaptin/updateAccountBank.php"; static String updateAccountBank = "$authCaptin/updateAccountBank.php";
static String getAccount = "$authCaptin/getAccount.php"; static String getAccount = "$authCaptin/getAccount.php";
static String updatePassengersInvitation =
"$server/ride/invitor/updatePassengersInvitation.php";
static String updateDriverInvitationDirectly =
"$server/ride/invitor/updateDriverInvitationDirectly.php";
//===================Admin Captin============ //===================Admin Captin============
static String getPassengerDetailsByPassengerID = static String getPassengerDetailsByPassengerID =
@@ -248,6 +279,7 @@ class AppLink {
static String getPassengerbyEmail = "$server/Admin/getPassengerbyEmail.php"; static String getPassengerbyEmail = "$server/Admin/getPassengerbyEmail.php";
static String addAdminUser = "$server/Admin/adminUser/add.php"; static String addAdminUser = "$server/Admin/adminUser/add.php";
static String getAdminUser = "$server/Admin/adminUser/get.php"; static String getAdminUser = "$server/Admin/adminUser/get.php";
static String addError = "$server/Admin/errorApp.php";
static String getCaptainDetailsByEmailOrIDOrPhone = static String getCaptainDetailsByEmailOrIDOrPhone =
"$server/Admin/AdminCaptain/getCaptainDetailsByEmailOrIDOrPhone.php"; "$server/Admin/AdminCaptain/getCaptainDetailsByEmailOrIDOrPhone.php";
static String getCaptainDetails = "$server/Admin/AdminCaptain/get.php"; static String getCaptainDetails = "$server/Admin/AdminCaptain/get.php";

View File

@@ -0,0 +1,22 @@
List<String> messages = [
"🚗 عروض مميزة: استمتع بأقل الأسعار وأفضل العروض! افتح تطبيق سفر الآن لتحصل على المزيد من الخيارات. 🌟",
"💸 وفر الآن: وفر مع تطبيق سفر! عروض مستمرة وخيارات متعددة تناسب احتياجاتك. 🔥",
"🔒 أمان وراحة: مع تطبيق سفر، احصل على أمان وراحة بأفضل الأسعار! 🚕",
"💼 خيارات متنوعة: استفد من خيارات متنوعة وأسعار تنافسية على تطبيق سفر، الأفضل دائماً. 🌐",
"💵 توفير مضمون: حافظ على ميزانيتك وسافر بأمان مع تطبيق سفر العروض لا تتوقف! 🎉",
"🌍 وجهات مميزة: أفضل وجهات السفر، بأقل الأسعار مع تطبيق سفر تابعنا الآن! 🛤️",
"🛣️ سهولة وراحة: رحلاتك أصبحت أسهل وأرخص سافر معنا وتمتع بأفضل التجارب. 🎊",
"📲 حجز سهل: احجز رحلتك بسهولة وأمان مع سفر المزيد من الخصومات في انتظارك! 🎁",
"👑 فئة مميزة: خليك من الفئة المميزة واستفد بأفضل الأسعار مع تطبيق سفر. 💯",
"💡 خيارات متعددة: نوفر لك خيارات متعددة وسعر مناسب جرب تطبيق سفر الآن! 🚖",
"✨ عروض متجددة: العروض لا تتوقف على تطبيق سفر احجز رحلتك الآن وتمتع بالمزيد! 📅",
"🚀 سهولة الوصول: السفر أصبح أسهل وأسرع مع تطبيق سفر كن مستعدًا لأفضل التجارب! 🌠",
"🧳 راحة وأمان: تطبيق سفر يقدم لك أمان وراحة بأقل الأسعار! 📉",
"🔥 عروض فورية: احجز الآن واستمتع بعروض لا تُفوّت على تطبيق سفر! 🚘",
"🚖 أسعار تنافسية: اختر رحلتك الآن بأسعار تنافسية وتمتع بالراحة والأمان مع تطبيق سفر. ✅",
"💥 أسعار خاصة: أسعار خاصة بانتظارك على تطبيق سفر! افتح التطبيق الآن واحجز رحلتك. 🌐",
"🌟 راحة البال: انطلق بأمان وراحة مع تطبيق سفر استمتع بأفضل الأسعار. 💸",
"📍 خصومات حصرية: استفد من الخصومات الحصرية والعروض المستمرة على تطبيق سفر! 🛤️",
"🛫 تجربة سهلة: رحلاتك أصبحت أفضل وأسهل مع تطبيق سفر افتح التطبيق واستمتع بالتجربة. ✨",
"🔔 عروض لا مثيل لها: كن جاهزًا لعروض لا مثيل لها! تطبيق سفر يقدم لك أفضل الخيارات بأقل الأسعار. 🎉",
];

View File

@@ -5,35 +5,39 @@ import 'package:google_fonts/google_fonts.dart';
import 'colors.dart'; import 'colors.dart';
class AppStyle { class AppStyle {
static TextStyle headTitle = TextStyle( static TextStyle headTitle = const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 40, fontSize: 40,
color: AppColor.accentColor, color: AppColor.accentColor,
fontFamily: box.read(BoxName.lang) == 'ar' // fontFamily: box.read(BoxName.lang) == 'ar'
// ?GoogleFonts.notoNaskhArabic().fontFamily // // ?GoogleFonts.notoNaskhArabic().fontFamily
? GoogleFonts.notoNaskhArabic().fontFamily // ? GoogleFonts.notoNaskhArabic().fontFamily
: GoogleFonts.roboto().fontFamily); // : GoogleFonts.roboto().fontFamily,
static TextStyle headTitle2 = TextStyle( );
fontWeight: FontWeight.bold, static TextStyle headTitle2 = const TextStyle(
fontSize: 26, fontWeight: FontWeight.bold,
color: AppColor.writeColor, fontSize: 22,
fontFamily: box.read(BoxName.lang) == 'ar' color: AppColor.writeColor,
? GoogleFonts.notoNaskhArabic().fontFamily // fontFamily: box.read(BoxName.lang) == 'ar'
: GoogleFonts.roboto().fontFamily); // ? GoogleFonts.notoNaskhArabic().fontFamily
// : GoogleFonts.roboto().fontFamily
);
static TextStyle title = TextStyle( static TextStyle title = TextStyle(
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
fontSize: box.read(BoxName.lang) == 'ar' ? 14 : 16, fontSize: box.read(BoxName.lang) == 'ar' ? 14 : 16,
color: AppColor.writeColor, color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar' // fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.notoNaskhArabic().fontFamily // ? GoogleFonts.notoNaskhArabic().fontFamily
: GoogleFonts.roboto().fontFamily); // : GoogleFonts.roboto().fontFamily
static TextStyle subtitle = TextStyle( );
fontWeight: FontWeight.bold, static TextStyle subtitle = const TextStyle(
fontSize: 13, fontWeight: FontWeight.bold,
color: AppColor.writeColor, fontSize: 13,
fontFamily: box.read(BoxName.lang) == 'ar' color: AppColor.writeColor,
? GoogleFonts.notoNaskhArabic().fontFamily // fontFamily: box.read(BoxName.lang) == 'ar'
: GoogleFonts.roboto().fontFamily); // ? GoogleFonts.notoNaskhArabic().fontFamily
// : GoogleFonts.roboto().fontFamily
);
static TextStyle number = const TextStyle( static TextStyle number = const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
@@ -43,7 +47,9 @@ class AppStyle {
static BoxDecoration boxDecoration = const BoxDecoration( static BoxDecoration boxDecoration = const BoxDecoration(
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(2, 4)), color: Color.fromARGB(255, 218, 218, 255),
blurRadius: 5,
offset: Offset(2, 4)),
BoxShadow( BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2)) color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2))
], ],

View File

@@ -0,0 +1,83 @@
import 'package:google_maps_flutter/google_maps_flutter.dart';
class UniversitiesPolygons {
// AUC polygon points
static const List<List<LatLng>> universityPolygons = [
// AUC Polygon
[
LatLng(30.013431, 31.502572),
LatLng(30.018469, 31.497478),
LatLng(30.023158, 31.495870),
LatLng(30.025084, 31.496781),
LatLng(30.018701, 31.511393),
LatLng(30.015312, 31.508310),
],
// Example polygon for University 'German University in Cairo (GUC)'
[
LatLng(29.984554, 31.437829),
LatLng(29.990363, 31.438390),
LatLng(29.990560, 31.445643),
LatLng(29.984436, 31.445825),
],
//Future University in Egypt (FUE)
[
LatLng(30.025794, 31.490946),
LatLng(30.028341, 31.491014),
LatLng(30.028341, 31.492586),
LatLng(30.025844, 31.492491),
],
//'British University in Egypt (BUE)'
[
LatLng(30.117423, 31.605834),
LatLng(30.118224, 31.605543),
LatLng(30.118649, 31.607361),
LatLng(30.118932, 31.608033),
LatLng(30.119592, 31.612159),
LatLng(30.119372, 31.612958),
LatLng(30.120017, 31.617102),
LatLng(30.119435, 31.617193),
],
//Misr International University (MIU)
[
LatLng(30.166498, 31.491663),
LatLng(30.171956, 31.491060),
LatLng(30.172212, 31.495754),
LatLng(30.167112, 31.496108),
],
// Canadian International College (CIC)
[
LatLng(30.034312, 31.428963),
LatLng(30.035661, 31.429037),
LatLng(30.036074, 31.430522),
LatLng(30.036017, 31.431146),
LatLng(30.034580, 31.431117),
],
// October 6 University (O6U)
[
LatLng(29.974102, 30.946934),
LatLng(29.976620, 30.944925),
LatLng(29.979848, 30.949832),
LatLng(29.977372, 30.951950),
],
[
LatLng(30.029312, 31.210046),
LatLng(30.027124, 31.201393),
LatLng(30.014523, 31.205087),
LatLng(30.015416, 31.212218),
LatLng(30.027325, 31.210661),
],
// Add polygons for 8 more universities...
];
static const List<String> universityNames = [
"American University in Cairo (AUC)",
'German University in Cairo (GUC)',
'Future University in Egypt (FUE)',
'British University in Egypt (BUE)',
'Misr International University (MIU)',
'Canadian International College (CIC)',
'October 6 University (O6U)',
"Cairo University",
// Add names for 8 more universities...
];
}

View File

@@ -1,10 +1,17 @@
import 'dart:io';
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/controller/auth/login_controller.dart'; import 'package:SEFER/controller/auth/login_controller.dart';
import 'package:SEFER/main.dart'; import 'package:SEFER/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in/google_sign_in.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../constant/links.dart';
import '../../onbording_page.dart'; import '../../onbording_page.dart';
import '../functions/crud.dart';
class GoogleSignInHelper { class GoogleSignInHelper {
static final GoogleSignIn _googleSignIn = GoogleSignIn( static final GoogleSignIn _googleSignIn = GoogleSignIn(
@@ -32,26 +39,114 @@ class GoogleSignInHelper {
} }
} }
static Future<GoogleSignInAccount?> signInFromLogin() async { Future<GoogleSignInAccount?> signInFromLogin() async {
try { try {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
if (googleUser != null) { if (googleUser != null) {
await _handleSignUp(googleUser); await _handleSignUp(googleUser);
// if (box.read(BoxName.countryCode) == 'Egypt') {
await Get.put(LoginController()).loginUsingCredentials( await Get.put(LoginController()).loginUsingCredentials(
box.read(BoxName.passengerID).toString(), box.read(BoxName.passengerID).toString(),
box.read(BoxName.email).toString(), box.read(BoxName.email).toString(),
); );
// } else if (box.read(BoxName.countryCode) == 'Jordan') {
// // Get.to(() => AiPage());
// }
} }
return googleUser; return googleUser;
} catch (error) { } catch (error) {
// if (error is GoogleSignInAuthenticationException) {
// // Handle authentication errors from Google Sign-In
// addError("Google sign-in authentication error: ${error.message}",
// '<GoogleSignInAccount?> signInFromLogin()');
// } else if (error is GoogleSignInAccountNotFoundException) {
// // Handle the case where the user is not found (if applicable)
// addError("Google sign-in account not found error: ${error.message}",
// '<GoogleSignInAccount?> signInFromLogin()');
// }
// else
if (error is SocketException) {
// Handle network issues, like SSL certificate issues
addError("Network error (SSL certificate issue): ${error.message}",
'<GoogleSignInAccount?> signInFromLogin()');
} else if (error is PlatformException) {
// Handle platform-specific errors, like Google Play Services issues
if (error.code == 'sign_in_required') {
// Google Play Services are required but not installed or outdated
showGooglePlayServicesError();
} else {
addError("Platform error: ${error.message}",
'<GoogleSignInAccount?> signInFromLogin()');
}
} else {
// Catch all other unknown errors
addError("Unknown error: ${error.toString()}",
'<GoogleSignInAccount?> signInFromLogin()');
}
return null; return null;
} }
} }
void showGooglePlayServicesError() async {
const playStoreUrl =
'https://play.google.com/store/apps/details?id=com.google.android.gms&hl=en_US';
if (await canLaunchUrl(Uri.parse(playStoreUrl))) {
await launchUrl(Uri.parse(playStoreUrl));
} else {
// Fallback if the URL can't be opened
showDialog(
context: Get.context!,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Error'.tr),
content: Text(
'Could not open the Google Play Store. Please update Google Play Services manually.'
.tr),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Close'.tr),
),
],
);
},
);
}
}
// Future<GoogleSignInAccount?> signInFromLogin() async {
// try {
// final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
// if (googleUser != null) {
// await _handleSignUp(googleUser);
// // if (box.read(BoxName.countryCode) == 'Egypt') {
// await Get.put(LoginController()).loginUsingCredentials(
// box.read(BoxName.passengerID).toString(),
// box.read(BoxName.email).toString(),
// );
// // } else if (box.read(BoxName.countryCode) == 'Jordan') {
// // // Get.to(() => AiPage());
// // }
// }
// return googleUser;
// } catch (error) {
// addError(error.toString(), '<GoogleSignInAccount?> signInFromLogin()');
// return null;
// }
// }
addError(String error, where) async {
CRUD().post(link: AppLink.addError, payload: {
'error': error.toString(), // Example error description
'userId': box.read(BoxName.driverID) ??
box.read(BoxName.passengerID), // Example user ID
'userType': box.read(BoxName.driverID) != null
? 'Driver'
: 'passenger', // Example user type
'phone': box.read(BoxName.phone) ??
box.read(BoxName.phoneDriver), // Example phone number
'device': where
});
}
// Method to handle Google Sign-Out // Method to handle Google Sign-Out
static Future<void> signOut() async { static Future<void> signOut() async {
try { try {

View File

@@ -1,9 +1,12 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:SEFER/constant/info.dart'; import 'package:SEFER/constant/info.dart';
import 'package:SEFER/controller/firebase/firbase_messge.dart'; import 'package:SEFER/controller/firebase/firbase_messge.dart';
import 'package:SEFER/controller/functions/add_error.dart';
import 'package:SEFER/views/auth/login_page.dart'; import 'package:SEFER/views/auth/login_page.dart';
import 'package:SEFER/views/auth/sms_verfy_page.dart'; import 'package:SEFER/views/auth/sms_verfy_page.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
@@ -13,6 +16,9 @@ import 'package:SEFER/main.dart';
import 'package:SEFER/views/home/map_page_passenger.dart'; import 'package:SEFER/views/home/map_page_passenger.dart';
import 'package:location/location.dart'; import 'package:location/location.dart';
import '../../print.dart';
import '../functions/package_info.dart';
class LoginController extends GetxController { class LoginController extends GetxController {
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final formKeyAdmin = GlobalKey<FormState>(); final formKeyAdmin = GlobalKey<FormState>();
@@ -29,9 +35,20 @@ class LoginController extends GetxController {
update(); update();
} }
getAppTester(String appPlatform) async { @override
var res = await CRUD() void onInit() async {
.get(link: AppLink.getTesterApp, payload: {'appPlatform': appPlatform}); box.read(BoxName.isTest) == null ||
box.read(BoxName.isTest).toString() == '0'
? await getAppTester()
: null;
super.onInit();
}
getAppTester() async {
var res = await CRUD().get(
link: AppLink.getTesterApp,
payload: {'appPlatform': AppInformation.appName});
if (res != 'failure') { if (res != 'failure') {
var d = jsonDecode(res); var d = jsonDecode(res);
@@ -64,6 +81,8 @@ class LoginController extends GetxController {
await CRUD().get(link: AppLink.loginFromGooglePassenger, payload: { await CRUD().get(link: AppLink.loginFromGooglePassenger, payload: {
'email': email, 'email': email,
'id': passengerID, 'id': passengerID,
"platform": Platform.isAndroid ? 'android' : 'ios',
"appName": AppInformation.appName,
}); });
if (res == 'Failure') { if (res == 'Failure') {
Get.offAll(SmsSignupEgypt()); Get.offAll(SmsSignupEgypt());
@@ -73,12 +92,22 @@ class LoginController extends GetxController {
} else { } else {
var jsonDecoeded = jsonDecode(res); var jsonDecoeded = jsonDecode(res);
if (jsonDecoeded.isNotEmpty) { if (jsonDecoeded.isNotEmpty) {
var d = jsonDecoeded['data'][0];
if (jsonDecoeded['status'] == 'success' && if (jsonDecoeded['status'] == 'success' &&
jsonDecoeded['data'][0]['verified'].toString() == '1') { d['verified'].toString() == '1') {
// //
box.write(BoxName.isVerified, '1'); box.write(BoxName.isVerified, '1');
box.write(BoxName.email, jsonDecoeded['data'][0]['email']); box.write(BoxName.email, d['email']);
box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']); box.write(BoxName.phone, d['phone']);
box.write(BoxName.isTest, '1');
box.write(BoxName.package, d['package']);
box.write(BoxName.promo, d['promo']);
box.write(BoxName.discount, d['discount']);
box.write(BoxName.validity, d['validity']);
box.write(BoxName.isInstall, d['isInstall'] ?? 'none');
box.write(BoxName.isGiftToken, d['isGiftToken'] ?? 'none');
box.write(BoxName.inviteCode, d['inviteCode'] ?? 'none');
var token = await CRUD().get(link: AppLink.getTokens, payload: { var token = await CRUD().get(link: AppLink.getTokens, payload: {
'passengerID': box.read(BoxName.passengerID).toString() 'passengerID': box.read(BoxName.passengerID).toString()
@@ -86,34 +115,85 @@ class LoginController extends GetxController {
if (token != 'failure') { if (token != 'failure') {
if (jsonDecode(token)['data'][0]['token'] != if (jsonDecode(token)['data'][0]['token'] !=
box.read(BoxName.tokenFCM)) { box.read(BoxName.tokenFCM)) {
Get.put(FirebaseMessagesController()) Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP(
.sendNotificationToAnyWithoutData(
'token change'.tr, 'token change'.tr,
'change device'.tr, 'change device'.tr,
jsonDecode(token)['data'][0]['token'].toString(), jsonDecode(token)['data'][0]['token'].toString(),
[],
'cancel.wav', 'cancel.wav',
); );
Future.delayed(const Duration(seconds: 1)); Future.delayed(const Duration(seconds: 1));
await CRUD().post(link: AppLink.addTokens, payload: { await CRUD().post(
'token': box.read(BoxName.tokenFCM), link: "${AppLink.server}/ride/firebase/add.php",
'passengerID': box.read(BoxName.passengerID).toString() payload: {
}); 'token': box.read(BoxName.tokenFCM),
// Get.defaultDialog( 'passengerID': box.read(BoxName.passengerID).toString()
// title: 'Device Change Detected'.tr, });
// middleText: CRUD().post(
// 'You can only use one device at a time. This device will now be set as your active device.' link:
// .tr, "${AppLink.seferAlexandriaServer}/ride/firebase/add.php",
// textConfirm: 'OK'.tr, payload: {
// confirmTextColor: Colors.white, 'token': box.read(BoxName.tokenFCM),
// onConfirm: () { 'passengerID': box.read(BoxName.passengerID).toString()
// Get.back(); });
// Get.offAll(() => const MapPagePassenger()); CRUD().post(
// }, link: "${AppLink.seferGizaServer}/ride/firebase/add.php",
// ); payload: {
// Get.snackbar('title', 'message'); 'token': box.read(BoxName.tokenFCM),
'passengerID': box.read(BoxName.passengerID).toString()
});
Get.defaultDialog(
title: 'Device Change Detected'.tr,
middleText:
'You can only use one device at a time. This device will now be set as your active device.'
.tr,
textConfirm: 'OK'.tr,
confirmTextColor: Colors.white,
onConfirm: () {
Get.back();
Get.offAll(() => const MapPagePassenger());
},
);
} else {
print('same');
} }
} // Logging to check if inviteCode is written correctly
if (box.read(BoxName.inviteCode).toString() != 'none' &&
box.read(BoxName.isInstall).toString() != '1') {
await CRUD()
.post(link: AppLink.updatePassengersInvitation, payload: {
"inviteCode": box.read(BoxName.inviteCode).toString(),
"passengerID": box.read(BoxName.passengerID).toString(),
});
Get.defaultDialog(
title: 'Invitation Used'
.tr, // Automatically translates based on the current locale
middleText: "Your invite code was successfully applied!"
.tr, // Automatically translates based on the current locale
onConfirm: () {
try {
CRUD().post(link: AppLink.addPassengersPromo, payload: {
"promoCode":
'S-${box.read(BoxName.name).toString().split(' ')[0]}',
"amount": '25',
"passengerID": box.read(BoxName.passengerID).toString(),
"description": 'promo first'
});
} catch (e) {
addError(e.toString(),
'passenger Invitation Used dialogu as promo line 185 login_controller');
} finally {
// Continue with the rest of your flow, regardless of errors
// For example, navigate to the next page
Get.offAll(() => const MapPagePassenger());
}
},
textConfirm: "OK".tr, // Confirm button text
);
} else {
Get.offAll(() => const MapPagePassenger());
} }
Get.offAll(() => const MapPagePassenger());
} else { } else {
Get.offAll(() => SmsSignupEgypt()); Get.offAll(() => SmsSignupEgypt());
// Get.snackbar(jsonDecoeded['status'], jsonDecoeded['data'], // Get.snackbar(jsonDecoeded['status'], jsonDecoeded['data'],
@@ -128,34 +208,6 @@ class LoginController extends GetxController {
} }
} }
// void adminDashboardOpen() async {
// if (formKeyAdmin.currentState!.validate()) {
// await DeviceInfoPlus.getDeviceInfo();
// if (Platform.isAndroid) {
// // var res = await CRUD().get(link: AppLink.getAdminUser, payload: {
// // // 'device_number': DeviceInfoPlus.deviceData['serialNumber'].toString(),
// // });
// // var d = jsonDecode(res);
// // // if (DeviceInfoPlus.deviceData['serialNumber'] ==
// // d['message']['device_number']) {
// Get.back();
// Get.to(() => const AdminHomePage());
// // }
// }
// if (Platform.isIOS) {
// // var res = await CRUD().get(link: AppLink.getAdminUser, payload: {
// // 'device_number': DeviceInfoPlus.deviceData['identifierForVendor'].toString(),
// // });
// // var d = jsonDecode(res);
// // if (DeviceInfoPlus.deviceData['serialNumber'] ==
// // d['message']['device_number']) {
// Get.back();
// Get.to(() => const AdminHomePage());
// // }
// }
// }
// }
void login() async { void login() async {
isloading = true; isloading = true;
update(); update();
@@ -204,6 +256,12 @@ class LoginController extends GetxController {
} }
} }
goToMapPage() {
if (box.read(BoxName.email) != null) {
Get.offAll(() => const MapPagePassenger());
}
}
final location = Location(); final location = Location();
// late PermissionStatus permissionGranted = PermissionStatus.denied; // late PermissionStatus permissionGranted = PermissionStatus.denied;
@@ -236,15 +294,4 @@ class LoginController extends GetxController {
} }
update(); update();
} }
@override
void onInit() async {
// permissionLocation = await Permission.locationWhenInUse.isGranted;
await getAppTester(AppInformation.appName);
// if (isTest == 0 && box.read(BoxName.passengerID) != null) {
// // await loginUsingCredentials(
// // box.read(BoxName.passengerID), box.read(BoxName.email));
// }
super.onInit();
}
} }

View File

@@ -22,7 +22,7 @@ class OnBoardingControllerImp extends OnBoardingController {
if (currentPage > onBoardingList.length - 1) { if (currentPage > onBoardingList.length - 1) {
box.write(BoxName.onBoarding, 'yes'); box.write(BoxName.onBoarding, 'yes');
Get.offAll(LoginPage()); Get.offAll(() => LoginPage());
} else { } else {
pageController.animateToPage(currentPage, pageController.animateToPage(currentPage,
duration: const Duration(milliseconds: 900), curve: Curves.easeInOut); duration: const Duration(milliseconds: 900), curve: Curves.easeInOut);

View File

@@ -3,7 +3,11 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/controller/auth/login_controller.dart';
import 'package:SEFER/controller/functions/add_error.dart';
import 'package:SEFER/controller/local/phone_intel/phone_number.dart';
import 'package:SEFER/views/home/map_page_passenger.dart'; import 'package:SEFER/views/home/map_page_passenger.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/links.dart'; import 'package:SEFER/constant/links.dart';
@@ -14,14 +18,14 @@ import 'package:SEFER/views/widgets/elevated_btn.dart';
import '../../constant/box_name.dart'; import '../../constant/box_name.dart';
import '../../main.dart'; import '../../main.dart';
import '../../print.dart';
import '../../views/auth/verify_email_page.dart'; import '../../views/auth/verify_email_page.dart';
import '../functions/sms_controller.dart'; import '../functions/sms_controller.dart';
class RegisterController extends GetxController { class RegisterController extends GetxController {
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final formKey3 = GlobalKey<FormState>(); final formKey3 = GlobalKey<FormState>();
List<String> countryCodes = ['+1', '+91', '+44'];
String selectedCountryCode = '+1';
TextEditingController firstNameController = TextEditingController(); TextEditingController firstNameController = TextEditingController();
TextEditingController lastNameController = TextEditingController(); TextEditingController lastNameController = TextEditingController();
TextEditingController emailController = TextEditingController(); TextEditingController emailController = TextEditingController();
@@ -41,11 +45,6 @@ class RegisterController extends GetxController {
super.onInit(); super.onInit();
} }
void updateCountryCode(String newCode) {
selectedCountryCode = newCode;
update();
}
void startTimer() { void startTimer() {
_timer?.cancel(); // Cancel any existing timer _timer?.cancel(); // Cancel any existing timer
_timer = Timer.periodic(const Duration(seconds: 1), (timer) { _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
@@ -119,166 +118,207 @@ class RegisterController extends GetxController {
} }
sendOtpMessage() async { sendOtpMessage() async {
SmsEgyptController smsEgyptController = Get.put(SmsEgyptController()); SmsEgyptController smsEgyptController;
int randomNumber = Random().nextInt(100000) + 1;
isLoading = true; isLoading = true;
update(); update();
if (formKey3.currentState!.validate()) { try {
if (box.read(BoxName.countryCode) == 'Egypt') { // Initialize SmsEgyptController
if (isValidEgyptianPhoneNumber(phoneController.text) == true) { smsEgyptController = Get.put(SmsEgyptController());
var responseCheker = await CRUD()
.post(link: AppLink.checkPhoneNumberISVerfiedPassenger, payload: { // Generate a random OTP
'phone_number': '+2${phoneController.text}', int randomNumber = Random().nextInt(100000) + 1;
'email': box.read(BoxName.email),
}); isLoading = true;
if (responseCheker != 'failure') { update();
var d = jsonDecode(responseCheker);
if (d['message'][0]['verified'].toString() == '1') { // Get phone number from controller
String phoneNumber = phoneController.text;
// Check if the phone number is from Egypt (Assuming Egyptian numbers start with +20)
if (phoneController.text.isNotEmpty) {
bool isEgyptianNumber = phoneNumber.startsWith('+20');
if (isEgyptianNumber && phoneNumber.length == 13) {
// Check if the phone number is already verified
var responseChecker = await CRUD().post(
link: AppLink.checkPhoneNumberISVerfiedPassenger,
payload: {
'phone_number': phoneNumber,
'email': box.read(BoxName.email),
},
);
if (responseChecker != 'failure') {
var data = jsonDecode(responseChecker);
// If the phone number is already verified
if (data['message'][0]['verified'].toString() == '1') {
Get.snackbar('Phone number is verified before'.tr, '', Get.snackbar('Phone number is verified before'.tr, '',
backgroundColor: AppColor.greenColor); backgroundColor: AppColor.greenColor);
box.write(BoxName.isVerified, '1'); box.write(BoxName.isVerified, '1');
box.write(BoxName.phone, '+2${phoneController.text}'); box.write(BoxName.phone, phoneNumber);
Get.offAll(const MapPagePassenger()); Get.offAll(const MapPagePassenger());
} else { } else {
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: { await sendOtp(phoneNumber, randomNumber, isEgyptianNumber,
'phone_number': '+2${phoneController.text}', smsEgyptController);
'token': randomNumber.toString(),
});
await smsEgyptController.sendSmsEgypt(
phoneController.text.toString(), randomNumber.toString());
isSent = true;
remainingTime = 300; // Reset to 5 minutes
startTimer();
isLoading = false;
update();
} }
} else { } else {
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: { await sendOtp(phoneNumber, randomNumber, isEgyptianNumber,
'phone_number': '+2${phoneController.text}', smsEgyptController);
'token': randomNumber.toString(),
});
await smsEgyptController.sendSmsEgypt(
phoneController.text.toString(), randomNumber.toString());
isSent = true;
remainingTime = 300; // Reset to 5 minutes
startTimer();
isLoading = false;
update();
// Get.snackbar(responseCheker, 'message');
} }
} else if (isValidPhoneNumber(phoneController.text)) { } else if (phoneNumber.length > 9) {
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: { sendOtp(
'phone_number': '+${phoneController.text}', phoneNumber, randomNumber, isEgyptianNumber, smsEgyptController);
'token': randomNumber.toString(),
});
await smsEgyptController.sendWhatsAppAuth(
phoneController.text, randomNumber.toString());
// await smsEgyptController.sendSmsEgypt(
// phoneController.text.toString(), randomNumber.toString());
isSent = true;
remainingTime = 300; // Reset to 5 minutes
startTimer();
isLoading = false;
update();
} else {
Get.snackbar('Phone Number wrong'.tr, '',
backgroundColor: AppColor.redColor,
duration: const Duration(seconds: 5));
} }
} else {
MyDialog().getDialog(
'Error'.tr, 'Phone number must be exactly 11 digits long'.tr, () {
Get.back();
});
// sendOtp(
// phoneNumber, randomNumber, isEgyptianNumber, smsEgyptController);
} }
} catch (e) {
// Handle error
} finally {
isLoading = false;
update();
} }
} }
verifySMSCode() async { // Helper function to send OTP or WhatsApp message based on phone number location
// if (formKey3.currentState!.validate()) { Future<void> sendOtp(String phoneNumber, int otp, bool isEgyptian,
if (isValidEgyptianPhoneNumber(phoneController.text)) { SmsEgyptController controller) async {
var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: { // Trim any leading or trailing whitespace from the phone number
'phone_number': '+2${phoneController.text}', phoneNumber = phoneNumber.trim();
'token': verifyCode.text.toString(), var dd = await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
'phone_number': phoneNumber,
'token': otp.toString(),
});
Log.print('dd: ${dd}');
if (isEgyptian) {
await CRUD().post(link: AppLink.updatePhoneInvalidSMSPassenger, payload: {
"phone_number": Get.find<RegisterController>().phoneController.text
}); });
if (res != 'failure') { box.write(BoxName.phoneDriver, phoneController.text);
// var dec = jsonDecode(res); var nameParts = box.read(BoxName.name).toString().split(' ');
box.write(BoxName.phoneDriver, '+2${phoneController.text}'); var firstName = nameParts.isNotEmpty ? nameParts[0] : 'unknown';
var payload = { var lastName = nameParts.length > 1 ? nameParts[1] : 'unknown';
'id': box.read(BoxName.passengerID),
'phone': '+2${phoneController.text}',
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': 'unknown',
'first_name': box.read(BoxName.name).toString().split(' ')[0],
'last_name': box.read(BoxName.name).toString().split(' ')[1],
};
var res1 = await CRUD().post( var payload = {
link: AppLink.signUp, 'id': box.read(BoxName.passengerID),
'phone': phoneController.text,
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown',
'first_name': firstName,
'last_name': lastName,
};
var res1 = await CRUD().post(
link: AppLink.signUp,
payload: payload,
);
if (res1 != 'failure') {
await CRUD().post(
link: '${AppLink.seferAlexandriaServer}/auth/signup.php',
payload: payload, payload: payload,
); );
if (res1 != 'failure') { await CRUD().post(
CRUD().post( link: '${AppLink.seferGizaServer}/auth/signup.php',
link: '${AppLink.seferAlexandriaServer}/auth/signup.php', payload: payload,
payload: payload, );
);
CRUD().post( box.write(BoxName.isVerified, '1');
link: '${AppLink.seferGizaServer}/auth/signup.php', box.write(BoxName.isFirstTime, '0');
payload: payload, box.write(BoxName.phone, phoneController.text);
);
box.write(BoxName.isVerified, '1'); Get.put(LoginController()).loginUsingCredentials(
box.write(BoxName.phone, '+2${phoneController.text}'); box.read(BoxName.passengerID).toString(),
Get.offAll(const MapPagePassenger()); box.read(BoxName.email).toString(),
} );
} else {
Get.snackbar(
'Error'.tr, "The email or phone number is already registered.".tr,
backgroundColor: Colors.redAccent);
} }
// await controller.sendSmsEgypt(phoneNumber, otp.toString());
} else { } else {
var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: { await CRUD().sendWhatsAppAuth(phoneNumber, otp.toString());
'phone_number': '+${phoneController.text}', }
'token': verifyCode.text.toString(), isLoading = false;
});
if (res != 'failure') {
// var dec = jsonDecode(res);
box.write(BoxName.phoneDriver, '+${phoneController.text}');
var payload = {
'id': box.read(BoxName.passengerID),
'phone': '+${phoneController.text}',
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': 'unknown',
'first_name': box.read(BoxName.name).toString().split(' ')[0],
'last_name': box.read(BoxName.name).toString().split(' ')[1],
};
var res1 = await CRUD().post( isSent = true;
link: AppLink.signUp, remainingTime = 300;
payload: payload, update(); // Reset to 5 minutes
); startTimer();
if (res1 != 'failure') { }
CRUD().post(
link: '${AppLink.seferAlexandriaServer}/auth/signup.php', verifySMSCode() async {
try {
if (formKey3.currentState!.validate()) {
var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: {
'phone_number': phoneController.text,
'token': verifyCode.text.toString(),
});
if (res != 'failure') {
box.write(BoxName.phoneDriver, phoneController.text);
var nameParts = box.read(BoxName.name).toString().split(' ');
var firstName = nameParts.isNotEmpty ? nameParts[0] : 'unknown';
var lastName = nameParts.length > 1 ? nameParts[1] : 'unknown';
var payload = {
'id': box.read(BoxName.passengerID),
'phone': phoneController.text,
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown',
'first_name': firstName,
'last_name': lastName,
};
var res1 = await CRUD().post(
link: AppLink.signUp,
payload: payload, payload: payload,
); );
CRUD().post(
link: '${AppLink.seferGizaServer}/auth/signup.php', if (res1 != 'failure') {
payload: payload, await CRUD().post(
); link: '${AppLink.seferAlexandriaServer}/auth/signup.php',
box.write(BoxName.isVerified, '1'); payload: payload,
box.write(BoxName.phone, '+${phoneController.text}'); );
Get.offAll(const MapPagePassenger()); await CRUD().post(
link: '${AppLink.seferGizaServer}/auth/signup.php',
payload: payload,
);
box.write(BoxName.isVerified, '1');
box.write(BoxName.isFirstTime, '0');
box.write(BoxName.phone, phoneController.text);
Get.put(LoginController()).loginUsingCredentials(
box.read(BoxName.passengerID).toString(),
box.read(BoxName.email).toString(),
);
} else {
Get.snackbar('Error'.tr,
"The email or phone number is already registered.".tr,
backgroundColor: Colors.redAccent);
}
} else {
Get.snackbar('Error'.tr, "phone not verified".tr,
backgroundColor: Colors.redAccent);
} }
} else { } else {
Get.snackbar( Get.snackbar('Error'.tr, "you must insert token code".tr,
'Error'.tr, "The email or phone number is already registered.".tr, backgroundColor: AppColor.redColor);
backgroundColor: Colors.redAccent);
} }
} catch (e) {
addError(e.toString(), 'passenger sign up ');
Get.snackbar('Error'.tr, "Something went wrong. Please try again.".tr,
backgroundColor: Colors.redAccent);
} }
} }

View File

@@ -11,10 +11,9 @@ class AccessTokenManager {
factory AccessTokenManager(String jsonKey) { factory AccessTokenManager(String jsonKey) {
if (_instance._isServiceAccountKeyInitialized()) { if (_instance._isServiceAccountKeyInitialized()) {
print('Service account key already initialized.'); // Prevent re-initialization
return _instance; return _instance;
} }
print('Initializing service account key.');
_instance.serviceAccountJsonKey = jsonKey; _instance.serviceAccountJsonKey = jsonKey;
return _instance; return _instance;
} }
@@ -29,45 +28,23 @@ class AccessTokenManager {
} }
Future<String> getAccessToken() async { Future<String> getAccessToken() async {
print('Attempting to get a new access token...'); if (_accessToken != null && DateTime.now().isBefore(_expiryDate!)) {
return _accessToken!.data;
}
try { try {
// Parse service account credentials from JSON
print('Parsing service account credentials...');
final serviceAccountCredentials = ServiceAccountCredentials.fromJson( final serviceAccountCredentials = ServiceAccountCredentials.fromJson(
json.decode(serviceAccountJsonKey)); json.decode(serviceAccountJsonKey));
// Log service account email (or other non-sensitive information)
print('Service account email: ${serviceAccountCredentials.email}');
// Create an authenticated client via the service account
print('Creating authenticated client via service account...');
final client = await clientViaServiceAccount( final client = await clientViaServiceAccount(
serviceAccountCredentials, serviceAccountCredentials,
['https://www.googleapis.com/auth/firebase.messaging'], ['https://www.googleapis.com/auth/firebase.messaging'],
); );
// Log successful client creation
print('Authenticated client created successfully.');
// Update the access token and expiry date
_accessToken = client.credentials.accessToken; _accessToken = client.credentials.accessToken;
_expiryDate = client.credentials.accessToken.expiry; _expiryDate = client.credentials.accessToken.expiry;
// Log the obtained token and expiry time
print('Access token obtained: ${_accessToken!.data}');
print('Token expiry date: $_expiryDate');
// Close the client to prevent resource leaks
print('Closing authenticated client...');
client.close(); client.close();
// Return the newly fetched access token
return _accessToken!.data; return _accessToken!.data;
} catch (e) { } catch (e) {
// Log error if token fetch fails throw Exception('Failed to obtain access token');
print('Failed to obtain a new access token: $e');
throw Exception('Failed to obtain a new access token: $e');
} }
} }
} }

View File

@@ -1,12 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:SEFER/env/env.dart';
import 'package:SEFER/views/widgets/my_dialog.dart'; import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:googleapis_auth/auth_io.dart';
import 'package:googleapis_auth/googleapis_auth.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:SEFER/controller/functions/toast.dart'; import 'package:SEFER/controller/functions/toast.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
@@ -69,6 +66,9 @@ class FirebaseMessagesController extends GetxController {
} }
} }
NotificationController notificationController =
Get.put(NotificationController());
Future getTokens() async { Future getTokens() async {
String? basicAuthCredentials = String? basicAuthCredentials =
await storage.read(key: BoxName.basicAuthCredentials); await storage.read(key: BoxName.basicAuthCredentials);
@@ -122,7 +122,15 @@ class FirebaseMessagesController extends GetxController {
void fireBaseTitles(RemoteMessage message) { void fireBaseTitles(RemoteMessage message) {
if (message.notification!.title! == 'Order'.tr) { if (message.notification!.title! == 'Order'.tr) {
} else if (message.notification!.title! == 'Apply Ride'.tr) { if (Platform.isAndroid) {
notificationController.showNotification(
'Order', message.notification!.body!, 'Order');
}
} else if (message.notification!.title! == 'Accepted Ride') {
if (Platform.isAndroid) {
notificationController.showNotification(
'Apply Order'.tr, 'Driver Applied the Ride for You'.tr, 'ding');
}
var passengerList = message.data['passengerList']; var passengerList = message.data['passengerList'];
var myList = jsonDecode(passengerList) as List<dynamic>; var myList = jsonDecode(passengerList) as List<dynamic>;
@@ -131,16 +139,20 @@ class FirebaseMessagesController extends GetxController {
Get.find<MapPassengerController>().statusRide == 'Apply'; Get.find<MapPassengerController>().statusRide == 'Apply';
Get.find<MapPassengerController>().isSearchingWindow == false; Get.find<MapPassengerController>().isSearchingWindow == false;
Get.find<MapPassengerController>().update(); Get.find<MapPassengerController>().update();
NotificationController().showNotification( Get.find<MapPassengerController>().rideAppliedFromDriver(true);
'Apply Order'.tr, 'Driver Applied the Ride for You'.tr, 'order1');
// driverAppliedTripSnakBar(); // driverAppliedTripSnakBar();
} else if (message.notification!.title! == 'Promo'.tr) { } else if (message.notification!.title! == 'Promo'.tr) {
NotificationController() if (Platform.isAndroid) {
.showNotification('Promo', 'Show latest promo'.tr, 'promo'); notificationController.showNotification(
'Promo', 'Show latest promo'.tr, 'promo');
}
Get.to(const PromosPassengerPage()); Get.to(const PromosPassengerPage());
} else if (message.notification!.title! == 'Trip Monitoring'.tr) { } else if (message.notification!.title! == 'Trip Monitoring'.tr) {
NotificationController() if (Platform.isAndroid) {
.showNotification('Trip Monitoring'.tr, '', 'iphone_ringtone'); notificationController.showNotification(
'Trip Monitoring'.tr, '', 'iphone_ringtone');
}
var myListString = message.data['passengerList']; var myListString = message.data['passengerList'];
var myList = jsonDecode(myListString) as List<dynamic>; var myList = jsonDecode(myListString) as List<dynamic>;
Get.toNamed('/tripmonitor', arguments: { Get.toNamed('/tripmonitor', arguments: {
@@ -148,53 +160,72 @@ class FirebaseMessagesController extends GetxController {
'driverId': myList[1].toString(), 'driverId': myList[1].toString(),
}); });
} else if (message.notification!.title! == 'token change'.tr) { } else if (message.notification!.title! == 'token change'.tr) {
NotificationController() if (Platform.isAndroid) {
.showNotification('token change'.tr, 'token change'.tr, 'cancel'); notificationController.showNotification(
'token change'.tr, 'token change'.tr, 'cancel');
}
GoogleSignInHelper.signOut(); GoogleSignInHelper.signOut();
} else if (message.notification!.title! == 'DriverIsGoingToPassenger'.tr) { } else if (message.notification!.title! == 'DriverIsGoingToPassenger'.tr) {
Get.find<MapPassengerController>().isDriverInPassengerWay = true; Get.find<MapPassengerController>().isDriverInPassengerWay = true;
Get.find<MapPassengerController>().update(); Get.find<MapPassengerController>().update();
NotificationController().showNotification('Driver is Going To You'.tr, if (Platform.isAndroid) {
'Please stay on the picked point.'.tr, 'tone1'); notificationController.showNotification('Driver is Going To You'.tr,
'Please stay on the picked point.'.tr, 'tone1');
}
// Get.snackbar('Driver is Going To Passenger', '', // Get.snackbar('Driver is Going To Passenger', '',
// backgroundColor: AppColor.greenColor); // backgroundColor: AppColor.greenColor);
} else if (message.notification!.title! == 'message From passenger') { } else if (message.notification!.title! == 'message From passenger') {
NotificationController() if (Platform.isAndroid) {
.showNotification('message From passenger'.tr, ''.tr, 'tone2'); notificationController.showNotification(
'message From passenger'.tr, ''.tr, 'ding');
}
passengerDialog(message.notification!.body!); passengerDialog(message.notification!.body!);
update(); update();
} else if (message.notification!.title! == 'message From Driver') { } else if (message.notification!.title! == 'message From Driver') {
NotificationController() if (Platform.isAndroid) {
.showNotification('message From passenger'.tr, ''.tr, 'tone2'); notificationController.showNotification(
'message From Driver'.tr, ''.tr, 'ding');
}
passengerDialog(message.notification!.body!); passengerDialog(message.notification!.body!);
update(); update();
} else if (message.notification!.title! == 'RideIsBegin'.tr) { } else if (message.notification!.title! == 'RideIsBegin'.tr) {
if (Platform.isAndroid) {
notificationController.showNotification(
'Trip is Begin'.tr, ''.tr, 'start');
}
Get.find<MapPassengerController>().getBeginRideFromDriver(); Get.find<MapPassengerController>().getBeginRideFromDriver();
// Get.snackbar('RideIsBegin', '', backgroundColor: AppColor.greenColor); // Get.snackbar('RideIsBegin', '', backgroundColor: AppColor.greenColor);
box.write(BoxName.passengerWalletTotal, '0'); box.write(BoxName.passengerWalletTotal, '0');
NotificationController()
.showNotification('Trip is Begin'.tr, ''.tr, 'start');
update(); update();
} else if (message.notification!.title! == 'Hi ,I will go now'.tr) { } else if (message.notification!.title! == 'Hi ,I will go now'.tr) {
// Get.snackbar('Hi ,I will go now', '', // Get.snackbar('Hi ,I will go now', '',
// backgroundColor: AppColor.greenColor); // backgroundColor: AppColor.greenColor);
NotificationController().showNotification( if (Platform.isAndroid) {
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2'); notificationController.showNotification(
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'ding');
}
update(); update();
} else if (message.notification!.title! == 'Hi ,I Arrive your site'.tr) { } else if (message.notification!.title! == 'Hi ,I Arrive your site') {
NotificationController() if (Platform.isAndroid) {
.showNotification('Hi ,I Arrive your site'.tr, ''.tr, 'tone2'); notificationController.showNotification(
'Hi ,I Arrive your site'.tr, ''.tr, 'ding');
}
driverArrivePassengerDialoge(); driverArrivePassengerDialoge();
update(); update();
} else if (message.notification!.title! == "Cancel Trip from driver".tr) { } else if (message.notification!.title! == "Cancel Trip from driver") {
Get.back(); Get.back();
if (Platform.isAndroid) {
notificationController.showNotification("Cancel Trip from driver".tr,
"We will look for a new driver.\nPlease wait.".tr, 'cancel');
}
Get.defaultDialog( Get.defaultDialog(
title: "The driver canceled your ride.".tr, title: "The driver canceled your ride.".tr,
middleText: "We will look for a new driver.\\nPlease wait.".tr, middleText: "We will look for a new driver.\nPlease wait.".tr,
confirm: MyElevatedButton( confirm: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Ok'.tr, title: 'Ok'.tr,
onPressed: () async { onPressed: () async {
Get.back(); Get.back();
@@ -204,32 +235,38 @@ class FirebaseMessagesController extends GetxController {
), ),
cancel: MyElevatedButton( cancel: MyElevatedButton(
title: 'Cancel'.tr, title: 'Cancel'.tr,
kolor: AppColor.redColor,
onPressed: () { onPressed: () {
Get.offAll(const MapPagePassenger()); Get.offAll(() => const MapPagePassenger());
}, },
) )
// Get.find<MapPassengerController>() // Get.find<MapPassengerController>()
// .searchNewDriverAfterRejectingFromDriver(); // .searchNewDriverAfterRejectingFromDriver();
); );
} else if (message.notification!.title! == 'Driver Finish Trip'.tr) { } else if (message.notification!.title! == 'Driver Finish Trip'.tr) {
var myListString = message.data['passengerList']; var myListString = message.data['DriverList'];
var driverList = jsonDecode(myListString) as List<dynamic>; var driverList = jsonDecode(myListString) as List<dynamic>;
NotificationController().showNotification( if (Platform.isAndroid) {
'Driver Finish Trip'.tr, notificationController.showNotification(
'you will pay to Driver'.tr + ' ${driverList[3].toString()} \$'.tr, 'Driver Finish Trip'.tr,
'tone1'); 'you will pay to Driver'.tr + ' ${driverList[3].toString()} \$'.tr,
'tone1');
}
Get.find<AudioRecorderController>().stopRecording(); Get.find<AudioRecorderController>().stopRecording();
if (double.parse(box.read(BoxName.passengerWalletTotal)) < 0) { if (double.parse(box.read(BoxName.passengerWalletTotal)) < 0) {
box.write(BoxName.passengerWalletTotal, 0); box.write(BoxName.passengerWalletTotal, 0);
} }
Get.find<MapPassengerController>().tripFinishedFromDriver(); Get.find<MapPassengerController>().tripFinishedFromDriver();
notificationController.showNotification(
'Dont forget your personal belongings.'.tr,
'Please make sure you have all your personal belongings and that any remaining fare, if applicable, has been added to your wallet before leaving. Thank you for choosing the Sefer app'
.tr,
'ding');
Get.to(() => RateDriverFromPassenger(), arguments: { Get.to(() => RateDriverFromPassenger(), arguments: {
'driverId': driverList[0].toString(), 'driverId': driverList[0].toString(),
'rideId': driverList[1].toString(), 'rideId': driverList[1].toString(),
'price': driverList[3].toString() 'price': driverList[3].toString()
}); });
// }
} else if (message.notification!.title! == "Finish Monitor".tr) { } else if (message.notification!.title! == "Finish Monitor".tr) {
Get.defaultDialog( Get.defaultDialog(
titleStyle: AppStyle.title, titleStyle: AppStyle.title,
@@ -249,11 +286,13 @@ class FirebaseMessagesController extends GetxController {
var myListString = message.data['passengerList']; var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>; var driverList = jsonDecode(myListString) as List<dynamic>;
// if (Platform.isAndroid) { // if (Platform.isAndroid) {
NotificationController().showNotification( if (Platform.isAndroid) {
'Call Income'.tr, notificationController.showNotification(
message.notification!.body!, 'Call Income'.tr,
'iphone_ringtone', message.notification!.body!,
); 'iphone_ringtone',
);
}
// } // }
// Assuming GetMaterialApp is initialized and context is valid for navigation // Assuming GetMaterialApp is initialized and context is valid for navigation
// Get.to(() => PassengerCallPage( // Get.to(() => PassengerCallPage(
@@ -267,12 +306,13 @@ class FirebaseMessagesController extends GetxController {
var myListString = message.data['passengerList']; var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>; var driverList = jsonDecode(myListString) as List<dynamic>;
// if (Platform.isAndroid) { // if (Platform.isAndroid) {
NotificationController().showNotification( if (Platform.isAndroid) {
'Call Income'.tr, notificationController.showNotification(
message.notification!.body!, 'Call Income'.tr,
'iphone_ringtone', message.notification!.body!,
); 'iphone_ringtone',
// } );
}
// Assuming GetMaterialApp is initialized and context is valid for navigation // Assuming GetMaterialApp is initialized and context is valid for navigation
// Get.to(() => PassengerCallPage( // Get.to(() => PassengerCallPage(
// channelName: driverList[1].toString(), // channelName: driverList[1].toString(),
@@ -285,24 +325,24 @@ class FirebaseMessagesController extends GetxController {
var myListString = message.data['passengerList']; var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>; var driverList = jsonDecode(myListString) as List<dynamic>;
if (Platform.isAndroid) { if (Platform.isAndroid) {
NotificationController().showNotification( notificationController.showNotification(
'Call End'.tr, 'Call End'.tr,
message.notification!.body!, message.notification!.body!,
'tone2', 'ding',
); );
} }
// Assuming GetMaterialApp is initialized and context is valid for navigation // Assuming GetMaterialApp is initialized and context is valid for navigation
// Get.off(const CallPage()); // Get.off(const CallPage());
} catch (e) {} } catch (e) {}
} else if (message.notification!.title! == 'Driver Cancel Your Trip'.tr) { } else if (message.notification!.title! == 'Driver Cancelled Your Trip') {
// Get.snackbar( // Get.snackbar(
// 'You will be pay the cost to driver or we will get it from you on next trip' // 'You will be pay the cost to driver or we will get it from you on next trip'
// .tr, // .tr,
// 'message', // 'message',
// backgroundColor: AppColor.redColor); // backgroundColor: AppColor.redColor);
if (Platform.isAndroid) { if (Platform.isAndroid) {
NotificationController().showNotification( notificationController.showNotification(
'Driver Cancel Your Trip'.tr, 'Driver Cancelled Your Trip'.tr,
'you will pay to Driver you will be pay the cost of driver time look to your SEFER Wallet' 'you will pay to Driver you will be pay the cost of driver time look to your SEFER Wallet'
.tr, .tr,
'cancel'); 'cancel');
@@ -311,7 +351,7 @@ class FirebaseMessagesController extends GetxController {
box.remove(BoxName.tokenParent); box.remove(BoxName.tokenParent);
Get.find<MapPassengerController>().restCounter(); Get.find<MapPassengerController>().restCounter();
Get.offAll(const MapPagePassenger()); Get.offAll(() => const MapPagePassenger());
} }
// else if (message.notification!.title! == 'Order Applied') { // else if (message.notification!.title! == 'Order Applied') {
// Get.snackbar( // Get.snackbar(
@@ -325,38 +365,13 @@ class FirebaseMessagesController extends GetxController {
// } // }
else if (message.notification!.title! == 'Order Applied'.tr) { else if (message.notification!.title! == 'Order Applied'.tr) {
NotificationController().showNotification( if (Platform.isAndroid) {
'The order Accepted by another Driver'.tr, notificationController.showNotification(
'We regret to inform you that another driver has accepted this order.' 'The order Accepted by another Driver'.tr,
.tr, 'We regret to inform you that another driver has accepted this order.'
'order'); .tr,
} else if (message.notification!.title! == 'VIP Order Accepted'.tr) { 'order');
var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>;
// Assuming driverList[1] contains a valid date string
DateTime scheduledTime;
try {
scheduledTime = DateTime.parse(driverList[1]);
} catch (e) {
// Handle the error if the date format is incorrect
Log.print('Error parsing date: $e');
scheduledTime = DateTime.now()
.add(const Duration(hours: 1)); // Fallback to 1 hour from now
} }
NotificationController()
.showNotification('The driver accepted your trip'.tr, '', 'order');
MyDialog().getDialog(
'VIP Order Accepted'.tr,
'The driver accepted your trip'.tr,
() {
// Schedule a notification for the parsed date or fallback date
NotificationController().scheduleNotification('VIP Order'.tr,
'This is a scheduled notification.'.tr, scheduledTime);
},
);
} }
} }
@@ -393,7 +408,7 @@ class FirebaseMessagesController extends GetxController {
title: 'Ok I will go now.'.tr, title: 'Ok I will go now.'.tr,
onPressed: () { onPressed: () {
FirebaseMessagesController().sendNotificationToPassengerToken( FirebaseMessagesController().sendNotificationToPassengerToken(
'Hi ,I will go now'.tr, 'Hi ,I will go now',
'I will go now'.tr, 'I will go now'.tr,
Get.find<MapPassengerController>().driverToken, Get.find<MapPassengerController>().driverToken,
[], [],
@@ -410,7 +425,7 @@ class FirebaseMessagesController extends GetxController {
Future<dynamic> passengerDialog(String message) { Future<dynamic> passengerDialog(String message) {
return Get.defaultDialog( return Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
title: 'message From passenger'.tr, title: 'message From Driver'.tr,
titleStyle: AppStyle.title, titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title, middleTextStyle: AppStyle.title,
middleText: message.tr, middleText: message.tr,
@@ -456,7 +471,7 @@ class FirebaseMessagesController extends GetxController {
)); ));
} }
void sendNotificationAll(String title, body) async { void sendNotificationAll(String title, body, tone) async {
// Get the token you want to subtract. // Get the token you want to subtract.
String token = box.read(BoxName.tokenFCM); String token = box.read(BoxName.tokenFCM);
tokens = box.read(BoxName.tokens); tokens = box.read(BoxName.tokens);
@@ -468,25 +483,41 @@ class FirebaseMessagesController extends GetxController {
tokens = box.read(BoxName.tokens); tokens = box.read(BoxName.tokens);
for (var i = 0; i < tokens.length; i++) { for (var i = 0; i < tokens.length; i++) {
http http
.post(Uri.parse('https://fcm.googleapis.com/fcm/send'), .post(
headers: <String, String>{ Uri.parse('https://fcm.googleapis.com/fcm/send'),
'Content-Type': 'application/json', headers: <String, String>{
'Authorization': 'key=${AK.serverAPI}' 'Content-Type': 'application/json',
}, 'Authorization': 'key=${AK.serverAPI}'
body: jsonEncode({ },
'notification': <String, dynamic>{ body: jsonEncode({
'message': {
'token': token,
'notification': {
'title': title, 'title': title,
'body': body, 'body': body,
'sound': 'ding.wav'
}, },
'priority': 'high', // 'data': {
'data': <String, dynamic>{ // 'DriverList': jsonEncode(data),
'click_action': 'FLUTTER_NOTIFICATION_CLICK', // },
'id': '1', 'android': {
'status': 'done' 'priority': 'high', // Set priority to high
'notification': {
'sound': tone,
},
}, },
'to': tokens[i], 'apns': {
})) 'headers': {
'apns-priority': '10', // Set APNs priority to 10
},
'payload': {
'aps': {
'sound': tone,
},
},
},
},
}),
)
.whenComplete(() {}) .whenComplete(() {})
.catchError((e) {}); .catchError((e) {});
} }
@@ -521,40 +552,6 @@ class FirebaseMessagesController extends GetxController {
void sendNotificationToPassengerToken( void sendNotificationToPassengerToken(
String title, body, token, List<String> map, String tone) async { String title, body, token, List<String> map, String tone) async {
try {
final response = await http.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=${AK.serverAPI}'
},
body: jsonEncode({
'notification': <String, dynamic>{
'title': title,
'body': body,
'sound': tone
},
'data': {
'passengerList': map,
},
'priority': 'high',
'to': token,
}),
);
if (response.statusCode == 200) {
// Notification sent successfully
} else {
// Handle error response
'Failed to send notification. Status code: ${response.statusCode}';
}
} catch (e) {
// Handle other exceptions
}
}
void sendNotificationToAnyWithoutData(
String title, String body, String token, String tone) async {
try { try {
String serviceAccountKeyJson = '''{ String serviceAccountKeyJson = '''{
"type": "service_account", "type": "service_account",
@@ -594,11 +591,15 @@ class FirebaseMessagesController extends GetxController {
'body': body, 'body': body,
}, },
'android': { 'android': {
'priority': 'high', // Set priority to high
'notification': { 'notification': {
'sound': tone, 'sound': tone,
}, },
}, },
'apns': { 'apns': {
'headers': {
'apns-priority': '10', // Set APNs priority to 10
},
'payload': { 'payload': {
'aps': { 'aps': {
'sound': tone, 'sound': tone,
@@ -610,75 +611,22 @@ class FirebaseMessagesController extends GetxController {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
print('Notification sent successfully.'); print(
'Notification sent successfully. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); print('Response body: ${response.body}');
} else { } else {
print( print(
'Failed to send notification. Status code: ${response.statusCode}'); 'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); print('Response body: ${response.body}');
// Parse the response body to handle specific errors like 'UNREGISTERED'
final responseBody = jsonDecode(response.body);
if (responseBody['error'] != null &&
responseBody['error']['status'] == 'NOT_FOUND' &&
responseBody['error']['details'] != null) {
for (var detail in responseBody['error']['details']) {
if (detail['errorCode'] == 'UNREGISTERED') {
print(
'FCM token is unregistered or invalid. Consider removing this token.');
// Remove the unregistered token from your database here.
// Example: removeTokenFromDatabase(token);
}
}
}
} }
} catch (e) { } catch (e) {
print('Error sending notification: $e'); print('Error sending notification: $e');
} }
} }
// void sendNotificationToDriverMAP(String title, String body, String token, Future<void> sendNotificationToDriverMAP(
// List<String> data, String tone) async { String title, String body, String token, List<String> data, String tone,
// try { {int retryCount = 2}) async {
// final response = await http.post(
// // Uri.parse(
// // 'https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send'),
// Uri.parse('https://fcm.googleapis.com/fcm/send'),
// headers: <String, String>{
// 'Content-Type': 'application/json',
// // 'Authorization': 'Bearer 104815009508844392546'
// 'Authorization': 'key=${AK.serverAPI}'
// },
// body: jsonEncode({
// 'notification': <String, dynamic>{
// 'title': title,
// 'body': body,
// 'sound': tone
// },
// 'data': {
// 'DriverList': data,
// },
// 'priority': 'high',
// 'to': token,
// }),
// );
// if (response.statusCode == 200) {
// Log.print(
// 'Notification sent successfully. Status code: ${response.statusCode}');
// Log.print('Response body: ${response.body}');
// } else {
// Log.print(
// 'Failed to send notification. Status code: ${response.statusCode}');
// Log.print('Response body: ${response.body}');
// }
// } catch (e) {
// Log.print('Error sending notification: $e');
// }
// }
Future<void> sendNotificationToDriverMAP(String title, String body,
String token, List<String> data, String tone) async {
try { try {
String serviceAccountKeyJson = '''{ String serviceAccountKeyJson = '''{
"type": "service_account", "type": "service_account",
@@ -700,7 +648,7 @@ class FirebaseMessagesController extends GetxController {
// Obtain an OAuth 2.0 access token // Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken(); final accessToken = await accessTokenManager.getAccessToken();
Log.print('accessToken: ${accessToken}'); // Log.print('accessToken: ${accessToken}');
// Send the notification // Send the notification
final response = await http.post( final response = await http.post(
@@ -721,11 +669,15 @@ class FirebaseMessagesController extends GetxController {
'DriverList': jsonEncode(data), 'DriverList': jsonEncode(data),
}, },
'android': { 'android': {
'priority': 'high', // Set priority to high
'notification': { 'notification': {
'sound': tone, 'sound': tone,
}, },
}, },
'apns': { 'apns': {
'headers': {
'apns-priority': '10', // Set APNs priority to 10
},
'payload': { 'payload': {
'aps': { 'aps': {
'sound': tone, 'sound': tone,
@@ -735,105 +687,32 @@ class FirebaseMessagesController extends GetxController {
}, },
}), }),
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
print('Notification sent successfully.'); print(
print('Response body: ${response.body}'); 'Notification sent successfully. Status code: ${response.statusCode}');
print('Response token: ${token}');
} else { } else {
print( print(
'Failed to send notification. Status code: ${response.statusCode}'); 'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}'); print('Response body: ${response.body}');
if (retryCount > 0) {
// Parse the response body to handle specific errors like 'UNREGISTERED' print('Retrying... Attempts remaining: $retryCount');
final responseBody = jsonDecode(response.body); await Future.delayed(
if (responseBody['error'] != null && Duration(seconds: 2)); // Optional delay before retrying
responseBody['error']['status'] == 'NOT_FOUND' && return sendNotificationToDriverMAP(title, body, token, data, tone,
responseBody['error']['details'] != null) { retryCount: retryCount - 1);
for (var detail in responseBody['error']['details']) {
if (detail['errorCode'] == 'UNREGISTERED') {
print(
'FCM token is unregistered or invalid. Consider removing this token.');
// Remove the unregistered token from your database if needed
}
}
} }
} }
} catch (e) { } catch (e) {
print('Error sending notification: $e'); print('Error sending notification: $e');
} if (retryCount > 0) {
} print('Retrying... Attempts remaining: $retryCount');
await Future.delayed(
void sendNotificationToDriverMapPolyline(String title, String body, Duration(seconds: 2)); // Optional delay before retrying
String token, List<String> data, String polylineJson) async { return sendNotificationToDriverMAP(title, body, token, data, tone,
try { retryCount: retryCount - 1);
String serviceAccountKeyJson = '''{
"type": "service_account",
"project_id": "ride-b1bd8",
"private_key_id": "75e817c0b902db2ef35edf2c2bd159dec1f13249",
"private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD0zH9TQGDQHUv3\\na3/JAD1UKPwAp3wNKT0a6fxiIzjI3JxQWI30QvZCcfl6CdMhIcydX1ncSaYTcEeC\\n/AdPVCPkqyJx1YIGGg6P/mRzCWeaN8fsp6z250m5vcObDCZc3dbJEkepbep+6FPY\\n21m3KO+AHh1glgsTGZOTm5xiU8NGXpdk2QEh8wpiIIlR/HuKwVw9g8urNe3Sno+U\\nDm3z37iFqvZdmpqO8aWTJu6beb3hsREK9XK2I9JqC2JUwiGQRo3idOvPP6hkqrWx\\nKSX96vglQFYfakvJdDp2ZATOlpBYPMtS/IWhJ985u58TSS+Kl8qpnpaZBSxgJirf\\nhWzhnKLfAgMBAAECggEAJP785SePGhS7ZN6ltspm+l+hSjYFrPWFCxq+rlQ1YkHZ\\nC9l+RqKSFhOkiPmQI2s4wbXl3kFxLHHlFNoi/q2wKQBmGb8TQfnRJpjjNHGA61Ev\\n0Ue7/6qPvVb9B2MsLw/FxKiTFPuMG3bgKR9pbSFuJLYoaW7zqITOhVnYphGTqwAY\\nBVVcvISSLvELDmH9VZcv/9DVqVlqbbESHWh1Z4W6XGPoEqeDH/upNTyQQ/46Msgm\\nTGE6VqLHpWuSf6SqHp+r0Y0lI3vIPM1vz5FAJDJbOE/enHa0fSup0OHSMxl0HVMn\\nnO1yrGF3vsIPOej5HKr5d71bEIckzk73/yjNC1/mDQKBgQD7RtUvc9omsSsFMJ6e\\nBASAn6Dktx/QY/XNJjFzHQj69cywLDe5t5AL2gUi3phQ2oqB5XJdwnd5bTIEPEPZ\\nDOuOai2802p6FJk6kjmZAMVGx5JtXBH+vs6jrmQQSMiKbjwN1TT6xIWakvLOonUi\\nX6ZvjYYjU/E0YJU3jSiXWEr76wKBgQD5Zn4SouJ6BCDZMbausJVMBkk3qxsYooip\\np89WakC6e7AZinpkRcqjGGV9GOvc8crJs6fyXAA9ORepGP47Mc0ZrDssOkstznsM\\npr8R0S6MKwEZaT9ixOHdOcLZ47ps+JzA2Wr4KN2OvFHksUkB/46ATD1j9WZVgB8M\\namsYp/Y73QKBgHOo+PvsoZ9psVmkNX6abtAdqdtdB0HOoRea2uwXk0ig12TIFaZg\\nfedWpUKVnxqoXVTJHklV99RmlL0qWDiSH+LfsMnXro0e6iDxqZ1po2Se/CFmXcoa\\nXdctsFVmixhdATuExewfhTfPKABA+xWlXWC/jdy5CK+JPWXijaqMM4edAoGAE5Bj\\nsWiPpYyvWvpYX0nA3G7dzX0hqgQN/mkIjbnWDArp3IcNZNJIvBSM2Yxb7EAXbU0n\\njo6DAkp5Pa2VO+WDNlFZbvW/sf8xjeOCt44WPa6d7nVgIIpbQXRngZoopKW3/jTP\\n/FmQT8McFXmGxZ5belsAsdetSGW9icbLUerTGQ0CgYEAmf/G8Ag3XxmqTXvvHuv2\\n14OP7WnrVqkEMnydrftEwn4peXd/Lz+/GYX5Zc4ZoNgbN8IvZ5z0+OmRsallsbiW\\nBw0/tc68CjzxXOvReWxDluUopqWVGj5tlGqE5xUDku9SWJSxbkiQ3rqutzBdPXpr\\noqHwPyDrmK/Zgqn+uiIm4Ck=\\n-----END PRIVATE KEY-----\\n",
"client_email": "firebase-adminsdk-o2wqi@ride-b1bd8.iam.gserviceaccount.com",
"client_id": "111210077025005706623",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o2wqi%40ride-b1bd8.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
'''; // As defined above
// Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken();
// Send the notification
final response = await http.post(
Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'notification': <String, dynamic>{
'title': title,
'body': body,
// 'sound': 'tone2.wav',
'sound': 'order.wav'
},
'data': {
'DriverList': data,
'PolylineJson': polylineJson,
},
'priority': 'high',
'to': token,
}),
);
if (response.statusCode == 200) {
print('Notification sent successfully.');
print('Response body: ${response.body}');
} else {
print(
'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}');
// Parse the response body to handle specific errors like 'UNREGISTERED'
final responseBody = jsonDecode(response.body);
if (responseBody['error'] != null &&
responseBody['error']['status'] == 'NOT_FOUND' &&
responseBody['error']['details'] != null) {
for (var detail in responseBody['error']['details']) {
if (detail['errorCode'] == 'UNREGISTERED') {
print(
'FCM token is unregistered or invalid. Consider removing this token.');
// Remove the unregistered token from your database if needed
}
}
}
} }
} catch (e) {
// Handle other exceptions
} }
} }
} }

View File

@@ -1,8 +1,14 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz; import 'package:timezone/timezone.dart' as tz;
import '../../main.dart';
class NotificationController extends GetxController { class NotificationController extends GetxController {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlugin();
@@ -11,53 +17,315 @@ class NotificationController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
initNotifications(); initNotifications();
tz.initializeTimeZones();
} }
// Initializes the local notifications plugin // Initializes the local notifications plugin
Future<void> initNotifications() async { Future<void> initNotifications() async {
const AndroidInitializationSettings android = const AndroidInitializationSettings android =
AndroidInitializationSettings('@mipmap/launcher_icon'); AndroidInitializationSettings('@mipmap/launcher_icon');
const InitializationSettings initializationSettings = DarwinInitializationSettings ios = DarwinInitializationSettings(
InitializationSettings(android: android); requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
onDidReceiveLocalNotification:
(int id, String? title, String? body, String? payload) async {},
);
InitializationSettings initializationSettings =
InitializationSettings(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings); await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
tz.initializeTimeZones();
print('Notifications initialized');
} }
// Displays a notification with the given title and message // Displays a notification with the given title and message
void showNotification(String title, String message, String tone) async { void showNotification(String title, String message, String tone) async {
AndroidNotificationDetails android = AndroidNotificationDetails( final AndroidNotificationDetails android = AndroidNotificationDetails(
'your channel id', 'your channel name', 'high_importance_channel',
importance: Importance.max, 'High Importance Notifications',
priority: Priority.high, importance: Importance.max,
showWhen: false, priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone)); showWhen: false,
sound: RawResourceAndroidNotificationSound(tone),
);
NotificationDetails details = NotificationDetails(android: android); const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.show(0, title, message, details); await _flutterLocalNotificationsPlugin.show(0, title, message, details);
print('Notification shown: $title - $message');
}
// /Users/hamzaaleghwairyeen/development/App/ride 2/lib/controller/firebase/local_notification.dart
// Assume _flutterLocalNotificationsPlugin is initialized somewhere in your code
// void scheduleNotificationsForSevenDays(
// String title, String message, String tone) async {
// final AndroidNotificationDetails android = AndroidNotificationDetails(
// 'high_importance_channel',
// 'High Importance Notifications',
// importance: Importance.max,
// priority: Priority.high,
// sound: RawResourceAndroidNotificationSound(tone),
// );
// const DarwinNotificationDetails ios = DarwinNotificationDetails(
// sound: 'default',
// presentAlert: true,
// presentBadge: true,
// presentSound: true,
// );
// final NotificationDetails details =
// NotificationDetails(android: android, iOS: ios);
// // Check for the exact alarm permission on Android 12 and above
// if (Platform.isAndroid) {
// if (await Permission.scheduleExactAlarm.isDenied) {
// if (await Permission.scheduleExactAlarm.request().isGranted) {
// print('SCHEDULE_EXACT_ALARM permission granted');
// } else {
// print('SCHEDULE_EXACT_ALARM permission denied');
// return;
// }
// }
// }
// // Schedule notifications for the next 7 days
// for (int day = 0; day < 7; day++) {
// // Schedule for 8:00 AM
// await _scheduleNotificationForTime(
// day, 8, 0, title, message, details, day * 1000 + 1);
// // Schedule for 3:00 PM
// await _scheduleNotificationForTime(
// day, 15, 0, title, message, details, day * 1000 + 2); // Unique ID
// // Schedule for 8:00 PM
// await _scheduleNotificationForTime(
// day, 20, 0, title, message, details, day * 1000 + 3); // Unique ID
// }
// print('Notifications scheduled successfully for the next 7 days');
// }
void scheduleNotificationsForSevenDays(
String title, String message, String tone) async {
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
// Check for the exact alarm permission on Android 12 and above
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
}
// Schedule notifications for the next 7 days
for (int day = 0; day < 7; day++) {
// List of notification times
final notificationTimes = [
{'hour': 8, 'minute': 0, 'id': day * 1000 + 1}, // 8:00 AM
{'hour': 15, 'minute': 0, 'id': day * 1000 + 2}, // 3:00 PM
{'hour': 20, 'minute': 0, 'id': day * 1000 + 3}, // 8:00 PM
];
for (var time in notificationTimes) {
final notificationId = time['id'] as int;
// Check if this notification ID is already stored
bool isScheduled = box.read('notification_$notificationId') ?? false;
if (!isScheduled) {
// Schedule the notification if not already scheduled
await _scheduleNotificationForTime(
day,
time['hour'] as int,
time['minute'] as int,
title,
message,
details,
notificationId,
);
// Mark this notification ID as scheduled in GetStorage
box.write('notification_$notificationId', true);
} else {
print('Notification with ID $notificationId is already scheduled.');
}
}
}
print('Notifications scheduled successfully for the next 7 days');
} }
// Schedules a notification for a specific time void scheduleNotificationsForTimeSelected(
Future<void> scheduleNotification( String title, String message, String tone, DateTime timeSelected) async {
String title, String body, DateTime scheduledTime) async { final AndroidNotificationDetails android = AndroidNotificationDetails(
await _flutterLocalNotificationsPlugin.zonedSchedule( 'high_importance_channel',
0, 'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
// Check for the exact alarm permission on Android 12 and above
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
}
// Schedule notifications for 10 and 30 minutes before the timeSelected
await _scheduleNotificationForTimeVIP(
timeSelected.subtract(const Duration(minutes: 10)), // 10 minutes before
title, title,
body, message,
tz.TZDateTime.from(scheduledTime, tz.local), details,
const NotificationDetails( 1, // Unique ID for 10-minute before notification
android: AndroidNotificationDetails( );
'your_channel_id',
'your_channel_name', await _scheduleNotificationForTimeVIP(
channelDescription: 'your_channel_description', timeSelected.subtract(const Duration(minutes: 30)), // 30 minutes before
importance: Importance.max, title,
priority: Priority.high, message,
showWhen: false, details,
), 2, // Unique ID for 30-minute before notification
), );
androidAllowWhileIdle: true,
print('Notifications scheduled successfully for the time selected');
}
Future<void> _scheduleNotificationForTimeVIP(
DateTime scheduledDate,
String title,
String message,
NotificationDetails details,
int notificationId,
) async {
// Initialize and set Cairo timezone
tz.initializeTimeZones();
var cairoLocation = tz.getLocation('Africa/Cairo');
final now = tz.TZDateTime.now(cairoLocation);
// Convert to Cairo time
tz.TZDateTime scheduledTZDateTime =
tz.TZDateTime.from(scheduledDate, cairoLocation);
// Check if 10 minutes before the scheduled time is in the past
if (scheduledTZDateTime
.subtract(const Duration(minutes: 10))
.isBefore(now)) {
// If the 10 minutes before the scheduled time is in the past, don't schedule
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');
await _flutterLocalNotificationsPlugin.zonedSchedule(
notificationId, // Unique ID for each notification
title,
message,
scheduledTZDateTime,
details,
androidScheduleMode: AndroidScheduleMode.exact,
uiLocalNotificationDateInterpretation: uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime, UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time, matchDateTimeComponents:
null, // Don't repeat automatically; we handle manually
); );
print('Notification scheduled successfully for: $scheduledTZDateTime');
}
Future<void> _scheduleNotificationForTime(
int dayOffset,
int hour,
int minute,
String title,
String message,
NotificationDetails details,
int notificationId,
) async {
// Initialize and set Cairo timezone
tz.initializeTimeZones();
var cairoLocation = tz.getLocation('Africa/Cairo');
final now = tz.TZDateTime.now(cairoLocation);
tz.TZDateTime scheduledDate = tz.TZDateTime(
cairoLocation,
now.year,
now.month,
now.day + dayOffset, // Add offset to schedule for the next days
hour,
minute,
);
// If the scheduled time is in the past, move it to the next day
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledDate');
await _flutterLocalNotificationsPlugin.zonedSchedule(
notificationId, // Unique ID for each notification
title,
message,
scheduledDate,
details,
androidScheduleMode: AndroidScheduleMode.exact,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents:
null, // Don't repeat automatically; we handle 7 days manually
);
print('Notification scheduled successfully for: $scheduledDate');
} }
} }

View File

@@ -0,0 +1,19 @@
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import 'crud.dart';
addError(String error, where) async {
CRUD().post(link: AppLink.addError, payload: {
'error': error.toString(), // Example error description
'userId': box.read(BoxName.driverID) ??
box.read(BoxName.passengerID), // Example user ID
'userType': box.read(BoxName.driverID) != null
? 'Driver'
: 'passenger', // Example user type
'phone': box.read(BoxName.phone) ??
box.read(BoxName.phoneDriver), // Example phone number
'device': where
});
}

View File

@@ -9,6 +9,8 @@ import 'package:SEFER/env/env.dart';
import '../../constant/api_key.dart'; import '../../constant/api_key.dart';
import '../../print.dart'; import '../../print.dart';
import '../../views/widgets/elevated_btn.dart';
import 'add_error.dart';
import 'upload_image.dart'; import 'upload_image.dart';
class CRUD { class CRUD {
@@ -28,8 +30,9 @@ class CRUD {
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}', 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
}, },
); );
// Log.print('payload: ${payload}');
Log.print('response.request: ${response.request}'); Log.print('response.request: ${response.request}');
Log.print('payload: ${payload}');
// Log.print('response.reasonPhrase: ${response.reasonPhrase}'); // Log.print('response.reasonPhrase: ${response.reasonPhrase}');
Log.print('response.body: ${response.body}'); Log.print('response.body: ${response.body}');
@@ -43,7 +46,6 @@ class CRUD {
return jsonData['status']; return jsonData['status'];
} }
// }
Future<dynamic> getTokenParent({ Future<dynamic> getTokenParent({
required String link, required String link,
Map<String, dynamic>? payload, Map<String, dynamic>? payload,
@@ -71,6 +73,73 @@ class CRUD {
} }
} }
Future sendWhatsAppAuth(String to, String token) async {
var res = await CRUD()
.get(link: AppLink.getApiKey, payload: {'keyName': 'whatsapp_key'});
var accesstoken = jsonDecode(res)['message']['whatsapp_key'];
var headers = {
'Authorization': 'Bearer $accesstoken',
'Content-Type': 'application/json'
};
var url = 'https://graph.facebook.com/v20.0/${Env.whatappID}/messages';
var request = http.Request('POST', Uri.parse(url));
var body = json.encode({
"messaging_product": "whatsapp",
"to": to,
"type": "template",
"template": {
"name": "sefer1",
"language": {"code": "en"},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": token,
}
]
}
]
}
});
request.body = body;
request.headers.addAll(headers);
try {
print('Sending request to $url');
print('Request headers: $headers');
print('Request body: $body');
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String responseBody = await response.stream.bytesToString();
print('Response: $responseBody');
Get.defaultDialog(
title: 'You will receive a code in WhatsApp Messenger'.tr,
middleText: 'wait 1 minute to recive message'.tr,
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
},
),
);
} else {
String errorBody = await response.stream.bytesToString();
print('Error ${response.statusCode}: ${response.reasonPhrase}');
print('Error body: $errorBody');
}
} catch (e) {
print('Exception occurred: $e');
}
}
Future<dynamic> getAgoraToken({ Future<dynamic> getAgoraToken({
required String channelName, required String channelName,
required String uid, required String uid,
@@ -215,36 +284,83 @@ class CRUD {
} else {} } else {}
} }
// Future<dynamic> post({
// required String link,
// Map<String, dynamic>? payload,
// }) async {
// // String? basicAuthCredentials =
// // await storage.read(key: BoxName.basicAuthCredentials);
// var url = Uri.parse(
// link,
// );
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
// },
// );
// Log.print('payload: ${payload}');
// Log.print('response.request: ${response.request}');
// Log.print('response.body: ${response.body}');
// var jsonData = jsonDecode(response.body);
// if (response.statusCode == 200) {
// if (jsonData['status'] == 'success') {
// return response.body;
// } else {
// return (jsonData['status']);
// }
// } else {
// return response.statusCode;
// }
// }
Future<dynamic> post({ Future<dynamic> post({
required String link, required String link,
Map<String, dynamic>? payload, Map<String, dynamic>? payload,
}) async { }) async {
// String? basicAuthCredentials = var url = Uri.parse(link);
// await storage.read(key: BoxName.basicAuthCredentials); try {
var url = Uri.parse( var response = await http.post(
link, url,
); body: payload,
var response = await http.post( headers: {
url, "Content-Type": "application/x-www-form-urlencoded",
body: payload, 'Authorization':
headers: { 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
"Content-Type": "application/x-www-form-urlencoded", },
'Authorization': );
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
}, Log.print('Response.request: ${response.request}');
); Log.print('Payload: $payload');
// Log.print('payload: ${payload}'); // Log.print('Response.statusCode: ${response.statusCode}');
// Log.print('response.request: ${response.request}'); Log.print('Response.body: ${response.body}');
Log.print('response.body: ${response.body}');
var jsonData = jsonDecode(response.body); if (response.statusCode == 200) {
if (response.statusCode == 200) { try {
if (jsonData['status'] == 'success') { var jsonData = jsonDecode(response.body);
return response.body;
if (jsonData['status'] == 'success') {
return jsonData;
} else {
return jsonData['status'];
}
} catch (e) {
Log.print('JSON parsing error: $e');
addError(e.toString(), 'crud().post');
return 'failure'; // Return a recognizable failure string for JSON errors
}
} else { } else {
return (jsonData['status']); Log.print('Non-200 response code: ${response.statusCode}');
addError(
'Non-200 response code: ${response.statusCode}', 'crud().post');
return 'failure'; // Handle unexpected status codes as failures
} }
} else { } catch (e) {
return response.statusCode; Log.print('HTTP request error: $e');
addError('HTTP request error: $e', 'crud().post');
return 'failure'; // Handle HTTP request errors as failures
} }
} }
@@ -393,6 +509,26 @@ class CRUD {
return (jsonData['status']); return (jsonData['status']);
} }
Future<dynamic> getHereMap({
required String link,
}) async {
var url = Uri.parse(link);
try {
var response = await http.get(url);
if (response.statusCode == 200) {
// Ensure the response body is decoded as UTF-8
var decodedBody = utf8.decode(response.bodyBytes);
var data = jsonDecode(decodedBody);
return data;
} else {
return null;
}
} catch (e) {
return null;
}
}
Future<dynamic> update({ Future<dynamic> update({
required String endpoint, required String endpoint,
required Map<String, dynamic> data, required Map<String, dynamic> data,

View File

@@ -3,7 +3,6 @@ import 'dart:io';
import 'package:SEFER/constant/links.dart'; import 'package:SEFER/constant/links.dart';
import 'package:SEFER/controller/functions/crud.dart'; import 'package:SEFER/controller/functions/crud.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -17,7 +16,7 @@ Future<void> checkForUpdate(BuildContext context) async {
final version = packageInfo.version; final version = packageInfo.version;
print('currentVersion is : $currentVersion'); print('currentVersion is : $currentVersion');
// Fetch the latest version from your server // Fetch the latest version from your server
String latestVersion = await getPackageInfo(); String latestVersion = box.read(BoxName.package);
box.write(BoxName.packagInfo, version); box.write(BoxName.packagInfo, version);
if (latestVersion.isNotEmpty && latestVersion != currentVersion) { if (latestVersion.isNotEmpty && latestVersion != currentVersion) {
@@ -25,18 +24,22 @@ Future<void> checkForUpdate(BuildContext context) async {
} }
} }
Future<String> getPackageInfo() async { checkForBounusInvitation() {
final response = await CRUD().get(link: AppLink.packageInfo, payload: { if (box.read(BoxName.inviteCode) != null) {}
"platform": Platform.isAndroid ? 'android' : 'ios',
"appName": AppInformation.appName,
});
if (response != 'failure') {
return jsonDecode(response)['message'][0]['version'];
}
return '';
} }
// Future<String> getPackageInfo() async {
// final response = await CRUD().get(link: AppLink.packageInfo, payload: {
// "platform": Platform.isAndroid ? 'android' : 'ios',
// "appName": AppInformation.appName,
// });
// if (response != 'failure') {
// return jsonDecode(response)['message'][0]['version'];
// }
// return '';
// }
void showUpdateDialog(BuildContext context) { void showUpdateDialog(BuildContext context) {
final String storeUrl = Platform.isAndroid final String storeUrl = Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=com.mobileapp.store.ride' ? 'https://play.google.com/store/apps/details?id=com.mobileapp.store.ride'
@@ -65,10 +68,11 @@ void showUpdateDialog(BuildContext context) {
}, },
), ),
CupertinoDialogAction( CupertinoDialogAction(
child: Text('Cancel'.tr), child: Text('Cancel'.tr),
onPressed: () { onPressed: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
}) },
),
], ],
); );
}, },

View File

@@ -11,6 +11,7 @@ import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../../print.dart';
import '../auth/register_controller.dart'; import '../auth/register_controller.dart';
import 'crud.dart'; import 'crud.dart';
@@ -29,12 +30,12 @@ class SmsEgyptController extends GetxController {
Future<dynamic> sendSmsEgypt(String phone, otp) async { Future<dynamic> sendSmsEgypt(String phone, otp) async {
String sender = await getSender(); String sender = await getSender();
var body = jsonEncode({ var body = jsonEncode({
"username": AppInformation.appName, "username": 'Sefer',
"password": AK.smsPasswordEgypt, "password": AK.smsPasswordEgypt,
"message": "${AppInformation.appName} app code is $otp\ncopy it to app", "message": "${AppInformation.appName} app code is $otp\ncopy it to app",
"language": box.read(BoxName.lang) == 'en' ? "e" : 'r', "language": box.read(BoxName.lang) == 'en' ? "e" : 'r',
"sender": sender, //"Sefer Egy", "sender": sender, //"Sefer Egy",
"receiver": "2$phone" "receiver": phone
}); });
var res = await http.post( var res = await http.post(

View File

@@ -1,49 +1,50 @@
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart'; import 'package:flutter_tts/flutter_tts.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../main.dart';
class TextToSpeechController extends GetxController { class TextToSpeechController extends GetxController {
final flutterTts = FlutterTts(); final flutterTts = FlutterTts();
// Initialize TTS in initState
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
initTts(); initTts();
} }
// Dispose of TTS when controller is closed
@override @override
void onClose() { void onClose() {
flutterTts.stop(); // Stop any ongoing TTS
super.onClose(); super.onClose();
flutterTts.completionHandler;
} }
// Function to initialize TTS engine // Initialize TTS engine with language check
Future<void> initTts() async { Future<void> initTts() async {
String? lang = try {
WidgetsBinding.instance.platformDispatcher.locale.countryCode; String langCode = box.read(BoxName.lang) ?? 'en-US';
await flutterTts bool isAvailable = await flutterTts.isLanguageAvailable(langCode);
.setLanguage(box.read(BoxName.lang).toString()); //'en-US' Set language
// await flutterTts.setLanguage('ar-SA'); //'en-US' Set language // If language is unavailable, default to 'en-US'
// await flutterTts.setLanguage(lang!); //'en-US' Set language if (!isAvailable) {
await flutterTts.setSpeechRate(0.5); // Adjust speech rate langCode = 'en-US';
await flutterTts.setVolume(1.0); // Set volume }
await flutterTts.setLanguage(langCode);
await flutterTts.setSpeechRate(0.5); // Adjust speech rate
await flutterTts.setVolume(1.0); // Set volume
} catch (error) {
Get.snackbar('Error', 'Failed to initialize TTS: $error');
}
} }
// Function to speak the given text // Function to speak the given text
Future<void> speakText(String text) async { Future<void> speakText(String text) async {
try { try {
await flutterTts.awaitSpeakCompletion(true); await flutterTts.awaitSpeakCompletion(true);
var result = await flutterTts.speak(text); await flutterTts.speak(text);
if (result == 1) {
// TTS operation has started
// You can perform additional operations here, if needed
}
} catch (error) { } catch (error) {
// Handle error gracefully, e.g., show a message
Get.snackbar('Error', 'Failed to speak text: $error'); Get.snackbar('Error', 'Failed to speak text: $error');
} }
} }

View File

@@ -0,0 +1,91 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/elevated_btn.dart';
import '../functions/crud.dart';
class BlinkingController extends GetxController {
final promoFormKey = GlobalKey<FormState>();
final promo = TextEditingController();
bool promoTaken = false;
void applyPromoCodeToPassenger() async {
//TAWJIHI
if (promoFormKey.currentState!.validate()) {
CRUD().get(link: AppLink.getPassengersPromo, payload: {
'promo_code': promo.text,
}).then((value) {
if (value == 'failure') {
Get.defaultDialog(
title: 'Promo End !'.tr,
confirm: MyElevatedButton(
title: 'Back',
onPressed: () {
Get.back();
Get.back();
},
));
}
var decode = jsonDecode(value);
// if (decode["status"] == "success") {
// var firstElement = decode["message"][0];
// if (double.parse(box.read(BoxName.passengerWalletTotal)) < 0) {
// totalPassenger = totalCostPassenger -
// (totalCostPassenger * int.parse(firstElement['amount']) / 100);
// update();
// } else {
// totalPassenger = totalCostPassenger -
// (totalCostPassenger * int.parse(firstElement['amount']) / 100);
// update();
// }
// totalDriver = totalDriver -
// (totalDriver * int.parse(firstElement['amount']) / 100);
// promoTaken = true;
// update();
// Get.back();
// }
});
}
}
// Reactive variable for blinking (on/off)
var isLightOn = false.obs;
// To animate the border color
var borderColor = Colors.black.obs;
Timer? _blinkingTimer;
// Method to start blinking for 5 seconds
void startBlinking() {
int count = 0;
_blinkingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
// Toggle light on/off
isLightOn.value = !isLightOn.value;
borderColor.value = isLightOn.value
? Colors.yellow
: Colors.black; // Animate border color
count++;
// Stop blinking after 5 seconds
if (count >= 35) {
timer.cancel();
isLightOn.value = false; // Ensure light turns off
borderColor.value = Colors.black; // Reset the border color
}
});
}
@override
void onClose() {
_blinkingTimer?.cancel();
super.onClose();
}
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../functions/launch.dart';
class ContactUsController extends GetxController {
final String phone1 = '+201018805430';
final String phone2 = '+201080182934';
final TimeOfDay workStartTime = const TimeOfDay(hour: 12, minute: 0);
final TimeOfDay workEndTime = const TimeOfDay(hour: 19, minute: 0);
bool _isWithinWorkTime(TimeOfDay now) {
return (now.hour > workStartTime.hour ||
(now.hour == workStartTime.hour &&
now.minute >= workStartTime.minute)) &&
(now.hour < workEndTime.hour ||
(now.hour == workEndTime.hour && now.minute <= workEndTime.minute));
}
void showContactDialog(BuildContext context) {
TimeOfDay now = TimeOfDay.now();
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: Text('Contact Us'.tr),
message: Text('Choose a contact option'.tr),
actions: <Widget>[
if (_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(phone1),
onPressed: () => makePhoneCall(
phone1,
),
),
if (_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(phone2),
onPressed: () => makePhoneCall(phone2),
),
if (!_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(
'Work time is from 12:00 - 19:00.\nYou can send a WhatsApp message or email.'
.tr),
onPressed: () => Navigator.pop(context),
),
CupertinoActionSheetAction(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
Text('Send WhatsApp Message'.tr),
],
),
onPressed: () =>
launchCommunication('whatsapp', phone1, 'Hello'.tr),
),
CupertinoActionSheetAction(
child: Text('Send Email'.tr),
onPressed: () =>
launchCommunication('email', 'support@sefer.live', 'Hello'.tr),
),
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () => Navigator.pop(context),
),
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,10 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/links.dart'; import 'package:SEFER/constant/links.dart';
@@ -9,11 +12,38 @@ import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart'; import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/main.dart'; import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import '../../../constant/api_key.dart';
import '../../../print.dart';
class ComplaintController extends GetxController { class ComplaintController extends GetxController {
bool isLoading = false; bool isLoading = false;
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final complaintController = TextEditingController(); final complaintController = TextEditingController();
final suggestionController = TextEditingController();
List feedBack = [];
@override
void onInit() {
super.onInit();
getLatestRidesForPassengers();
}
getLatestRidesForPassengers() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getFeedBack, payload: {
'passengerId': box.read(BoxName.passengerID).toString(),
});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
feedBack = d;
}
isLoading = false;
update();
}
void addComplaint() async { void addComplaint() async {
isLoading = true; isLoading = true;
@@ -34,11 +64,175 @@ class ComplaintController extends GetxController {
title: 'Ok'.tr, title: 'Ok'.tr,
onPressed: () { onPressed: () {
Get.back(); Get.back();
Get.back(); // Get.back();
})); }));
} }
isLoading = false; isLoading = false;
update(); update();
} }
var isUploading = false.obs;
var uploadSuccess = false.obs;
late String audioLink = '';
Future<void> uploadAudioFile(File audioFile) async {
try {
isUploading.value = true;
// Prepare the file upload
var uri = Uri.parse('${AppLink.seferCairoServer}/upload_audio.php');
var request = http.MultipartRequest('POST', uri);
// Add the file to the request with MIME type
var mimeType = lookupMimeType(audioFile.path);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
request.files.add(
await http.MultipartFile.fromPath(
'audio',
audioFile.path,
contentType: mimeType != null ? MediaType.parse(mimeType) : null,
),
);
// Send the request
var response = await request.send();
// Convert response to string for parsing
var responseBody = await http.Response.fromStream(response);
// After the upload request
if (response.statusCode == 200) {
var jsonResponse = jsonDecode(responseBody.body);
if (jsonResponse['status'] == 'Audio file uploaded successfully.') {
uploadSuccess.value = true;
audioLink = jsonResponse['link']; // Get the audio link
Get.back();
Get.snackbar('Success'.tr, 'Audio uploaded successfully.'.tr,
backgroundColor: const Color.fromARGB(255, 89, 185, 115));
} else {
uploadSuccess.value = false;
}
} else {
uploadSuccess.value = false;
}
} catch (e) {
uploadSuccess.value = false;
} finally {
isUploading.value = false;
}
}
var customerServiceSolutions;
var passengerReport;
var driverReport;
var isloading = false;
Future<void> geminiAudio(payload, String audioLink, String complain) async {
String prompt = '''
Analyze the following complaint between a passenger and driver in a ride-hailing service. The complaint includes an audio link for reference. Provide two possible solutions for customer service to resolve the issue, and generate a detailed report for both the passenger and the driver.
Complaint details:
- Passenger: $complain
- Driver: [Driver's complaint]
- Ride Information: {ride details such as start_location, end_location, date, price, status, and rating details}
- Audio Link: [$audioLink]
Output the result in JSON format with the following structure:
{
"customerServiceSolutions": [
"solution1",
"solution2"
],
"passengerReport": {
"solution": "Passenger's solution" if passenger right,
"complaint": "Passenger's complaint",
"rideDetails": {detailed ride info}
},
"driverReport": {
"complaint": "Driver's complaint",
"rideDetails": {detailed ride info}
}
} the response in arabic language with egypt
''';
var requestBody = jsonEncode({
"contents": [
{
"parts": [
{"text": "$payload $prompt"}
]
}
],
"generationConfig": {
"temperature": 1,
"topK": 64,
"topP": 0.95,
"maxOutputTokens": 8192,
"stopSequences": []
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
}
]
});
final response = await http.post(
Uri.parse(
'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'),
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
var result = responseData['candidates'][0]['content']['parts'][0]['text'];
Log.print('result: ${result}');
// Clean up the result by removing surrounding backticks if they exist
result = result.replaceAll(RegExp(r'^```json\s*|\s*```$'), '');
// Attempt to decode the cleaned result as JSON
try {
var jsonResult = jsonDecode(result);
// Access customer service solutions and reports for both passenger and driver
customerServiceSolutions = jsonResult['customerServiceSolutions'];
passengerReport = jsonResult['passengerReport'];
driverReport = jsonResult['driverReport'];
update();
// Use the data accordingly
// For example, log the reports or display them in a UI dialog
update();
} catch (e) {
MyDialog().getDialog(
'Error'.tr,
'Unable to parse the response as JSON. Please check the format and try again.'
.tr, () {
Get.back();
});
}
} else {
Get.snackbar(
'Error', "Request failed with status: ${response.statusCode}",
backgroundColor: AppColor.redColor);
}
}
} }

View File

@@ -0,0 +1,309 @@
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/controller/payment/payment_controller.dart';
import 'package:SEFER/views/widgets/mysnakbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_contacts/contact.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:get/get.dart';
import 'package:share/share.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../../views/widgets/my_dialog.dart';
import '../../functions/launch.dart';
import '../../notification/notification_captain_controller.dart';
class InviteController extends GetxController {
final TextEditingController invitePhoneController = TextEditingController();
List driverInvitationData = [];
List driverInvitationDataToPassengers = [];
String? couponCode;
String? driverCouponCode;
int selectedTab = 0;
PassengerStats passengerStats = PassengerStats();
void updateSelectedTab(int index) {
selectedTab = index;
update();
}
Future<void> shareCouponCode() async {
// TODO: Implement sharing functionality
// You can use share_plus package to share the coupon code
}
Future<void> shareDriverCode() async {
if (driverCouponCode != null) {
final String shareText = '''
Join SEFER as a driver using my referral code!
Use code: $driverCouponCode
Download the SEFER Driver app now and earn rewards!
''';
await Share.share(shareText);
}
}
Future<void> sharePassengerCode() async {
if (couponCode != null) {
final String shareText = '''
Get a discount on your first SEFER ride!
Use my referral code: $couponCode
Download the SEFER app now and enjoy your ride!
''';
await Share.share(shareText);
}
}
@override
void onInit() {
super.onInit();
// fetchDriverStats();
}
void fetchDriverStats() async {
try {
var response = await CRUD().get(link: AppLink.getInviteDriver, payload: {
"driverId": box.read(BoxName.driverID),
});
if (response != 'failure') {
var data = jsonDecode(response);
driverInvitationData = data['message'];
update();
}
} catch (e) {}
}
void fetchDriverStatsPassengers() async {
try {
var response = await CRUD()
.get(link: AppLink.getDriverInvitationToPassengers, payload: {
"driverId": box.read(BoxName.passengerID),
});
if (response != 'failure') {
var data = jsonDecode(response);
driverInvitationDataToPassengers = data['message'];
update();
}
} catch (e) {}
}
void selectPhone(String phone) {
if (box.read(BoxName.countryCode) == 'Egypt') {
invitePhoneController.text = phone;
update();
Get.back();
}
}
Future<void> saveContactsToServer() async {
try {
// TODO: Implement the actual server upload logic here
// Simulating a server request
await Future.delayed(Duration(seconds: 2));
Get.snackbar('Success'.tr,
'${selectedContacts.length} contacts saved to server'.tr);
} catch (e) {
Get.snackbar('Error'.tr,
'An error occurred while saving contacts to server: $e'.tr);
}
}
List<Contact> contacts = <Contact>[];
List<Contact> selectedContacts = <Contact>[];
RxList<Map<String, dynamic>> contactMaps = <Map<String, dynamic>>[].obs;
Future<void> pickContacts() async {
try {
// Request contacts permission
if (await FlutterContacts.requestPermission(readonly: true)) {
// Fetch all contacts with full properties
final List<Contact> allContacts = await FlutterContacts.getContacts(
withProperties: true,
withThumbnail: false,
withPhoto: true,
);
// Check if contacts are available
if (allContacts.isNotEmpty) {
// Store the contacts
contacts = allContacts;
Log.print('contacts: $contacts');
// Convert contacts to a list of maps
contactMaps.value = await Future.wait(contacts.map((contact) async {
Log.print('Contact name: ${contact.displayName}');
// Fetch phone numbers separately
final phones = await contact.phones;
Log.print('Contact phones: $phones');
// Fetch email addresses separately
final emails = await contact.emails;
Log.print('Contact emails: $emails');
// Handle empty or null values
return {
'name': contact.displayName ?? '',
'phones': phones
.where((phone) => phone.normalizedNumber != null)
.map((phone) => phone.normalizedNumber ?? 'No number')
.toList(),
'emails': emails
.where((email) => email.address != null)
.map((email) => email.address ?? 'No email')
.toList(),
};
}).toList());
update();
} else {
Get.snackbar('No contacts available'.tr,
'Please add contacts to your phone.'.tr);
}
} else {
Get.snackbar('Permission denied'.tr,
'Contact permission is required to pick contacts'.tr);
}
} catch (e) {
Get.snackbar(
'Error'.tr, 'An error occurred while picking contacts: $e'.tr);
}
}
void onSelectPassengerInvitation(int index) async {
MyDialog().getDialog(
driverInvitationDataToPassengers[index]['countOfInvitDriver'] < 2
? '${'When'.tr} ${driverInvitationDataToPassengers[index]['passengerName']} ${"complete, you can claim your gift".tr} '
: 'You deserve the gift'.tr,
'${driverInvitationDataToPassengers[index]['passengerName']} ${driverInvitationDataToPassengers[index]['countOfInvitDriver']} / 2 ${'Trip'.tr}',
() async {
if (driverInvitationDataToPassengers[index]['countOfInvitDriver'] < 2) {
Get.back();
} else {
// Claim the gift if 100 trips are completed
if (driverInvitationDataToPassengers[index]['isGiftToken']
.toString() ==
'0') {
Get.back();
// Add wallet to the inviter
await Get.find<PaymentController>().addPassengersWallet('20');
// add for invitor too
// await Get.find<CaptainWalletController>().addDriverWalletToInvitor(
// 'paymentMethod',
// driverInvitationData[index]['driverInviterId'],
// '50');
// Update invitation as claimed
await CRUD().post(
link: AppLink.updatePassengerGift,
payload: {'id': driverInvitationDataToPassengers[index]['id']},
);
// Notify the inviter
NotificationCaptainController().addNotificationCaptain(
driverInvitationDataToPassengers[index]['passengerInviterId']
.toString(),
"You have got a gift for invitation".tr,
'${"You have 20".tr} ${'LE'}',
false,
);
} else {
Get.back();
MyDialog().getDialog(
"You have got a gift".tr,
"Share the app with another new passenger".tr,
() {
Get.back();
},
);
}
}
},
);
}
savePhoneToServer() async {
for (var i = 0; i < contactMaps.length; i++) {
var phones = contactMaps[i]['phones'];
if (phones != null && phones.isNotEmpty && phones[0].isNotEmpty) {
var res = await CRUD().post(link: AppLink.savePhones, payload: {
"name": contactMaps[i]['name'] ?? 'none',
"phones": phones[0] ?? 'none',
"phones2": phones.join(', ') ??
'none', // Convert List<String> to a comma-separated string
});
if (res != 'failure') {}
} else {}
}
}
String formatPhoneNumber(String input) {
// Remove any non-digit characters
String digitsOnly = input.replaceAll(RegExp(r'\D'), '');
// Ensure the number starts with the country code
if (digitsOnly.startsWith('20')) {
digitsOnly = digitsOnly.substring(1);
}
return digitsOnly;
}
void sendInviteToPassenger() async {
if (invitePhoneController.text.isEmpty ||
invitePhoneController.text.length < 11) {
mySnackeBarError('Please enter a correct phone'.tr);
return;
}
// try {
String phoneNumber = formatPhoneNumber(invitePhoneController.text);
var response =
await CRUD().post(link: AppLink.addInvitationPassenger, payload: {
"driverId": box.read(BoxName.passengerID),
"inviterPassengerPhone": '+2$phoneNumber'
});
if (response != 'failure') {
var d = response;
Get.snackbar('Success', 'Invite sent successfully'.tr);
String message = '${'*SEFER APP CODE*'.tr}\n\n'
'${"Use this code in registration".tr}\n'
'${"To get a gift for both".tr}\n\n'
'${"The period of this code is 1 hour".tr}\n\n'
'${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n'
'_*${d['message']['inviteCode'].toString()}*_\n\n'
'${"Install our app:".tr}\n'
'*Android:* https://play.google.com/store/apps/details?id=com.mobileapp.store.ride\n\n\n'
'*iOS:* https://apps.apple.com/us/app/sefer/id6458734951';
launchCommunication('whatsapp', '+2$phoneNumber', message);
invitePhoneController.clear();
} else {
Get.snackbar('Error'.tr, "Invite code already used".tr,
backgroundColor: AppColor.redColor,
duration: const Duration(seconds: 4));
}
// } catch (e) {
// Get.snackbar('Error', 'An error occurred'.tr);
// }
}
}
class PassengerStats {
final int totalInvites;
final int activeUsers;
final double totalEarnings;
PassengerStats({
this.totalInvites = 0,
this.activeUsers = 0,
this.totalEarnings = 0.0,
});
}

View File

@@ -1,10 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/links.dart'; import 'package:SEFER/constant/links.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart'; import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import '../../../main.dart';
class PromosController extends GetxController { class PromosController extends GetxController {
List<dynamic> promoList = []; List<dynamic> promoList = [];
@@ -17,7 +18,9 @@ class PromosController extends GetxController {
} }
Future getPromoByToday() async { Future getPromoByToday() async {
var res = await CRUD().get(link: AppLink.getPromoBytody, payload: {}); var res = await CRUD().get(link: AppLink.getPromoBytody, payload: {
'passengerID': box.read(BoxName.passengerID).toString(),
});
if (res.toString() == 'failure') { if (res.toString() == 'failure') {
// Get.defaultDialog( // Get.defaultDialog(
// title: 'No Promo for today .'.tr, // title: 'No Promo for today .'.tr,

View File

@@ -0,0 +1,323 @@
import 'dart:async';
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/home/map_passenger_controller.dart';
import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:SEFER/views/widgets/mycircular.dart';
import 'package:SEFER/views/widgets/mysnakbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../functions/crud.dart';
class VipOrderController extends GetxController {
RxBool isLoading = false.obs;
final arguments = Get.arguments;
RxList<dynamic> tripData = <dynamic>[].obs;
RxBool isButtonVisible = false.obs;
RxInt countdown = 60.obs;
Timer? _countdownTimer;
@override
void onInit() {
super.onInit();
fetchOrder();
startCountdown();
}
@override
void onClose() {
_countdownTimer?.cancel();
super.onClose();
}
Future<void> fetchOrder() async {
try {
isLoading.value = true;
var mapPassengerController = Get.find<MapPassengerController>();
var res = await CRUD().get(
link: AppLink.getMishwari,
payload: {
// 'driverId': mapPassengerController.driverIdVip.toString(),
'driverId': box.read(BoxName.passengerID).toString(),
},
);
if (res != 'failure') {
var decodedResponse = jsonDecode(res);
if (decodedResponse['message'] is List) {
tripData.value = decodedResponse['message'];
} else {
tripData.clear(); // Ensure empty list if no data
// mySnackeBarError('No trip data found');
}
} else {
tripData.clear();
// mySnackeBarError('Failed to fetch trip data');
}
} catch (e) {
tripData.clear();
// mySnackeBarError('An error occurred: $e');
} finally {
isLoading.value = false;
}
}
void startCountdown() {
_countdownTimer?.cancel(); // Cancel any existing timer
countdown.value = 60;
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (countdown.value > 0) {
countdown.value--;
} else {
timer.cancel();
isButtonVisible.value = true;
}
});
}
void sendToDriverAgain() {
// Reset states
isButtonVisible.value = false;
fetchOrder(); // Refresh order
startCountdown(); // Restart countdown
}
}
class VipWaittingPage extends StatelessWidget {
VipWaittingPage({super.key});
final VipOrderController vipOrderController = Get.put(VipOrderController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: "Waiting VIP".tr,
body: [
Obx(() {
// Loading state
if (vipOrderController.isLoading.value) {
return const Center(child: MyCircularProgressIndicator());
}
// No data state
if (vipOrderController.tripData.isEmpty) {
return Center(
child: Text(
'No trip data available'.tr,
style: AppStyle.title,
),
);
}
// Data available
var data = vipOrderController.tripData[0];
// Function to get the localized status string
String getLocalizedStatus(String status) {
switch (status) {
case 'pending':
return 'pending'.tr;
case 'accepted':
return 'accepted'.tr;
case 'begin':
return 'begin'.tr;
case 'rejected':
return 'rejected'.tr;
case 'cancelled':
return 'cancelled'.tr;
default:
return 'unknown'.tr;
}
}
// Function to get the appropriate status color
Color getStatusColor(String status) {
switch (status) {
case 'pending':
return Colors.yellow;
case 'accepted':
return Colors.green;
case 'begin':
return Colors.green;
case 'rejected':
return Colors.red;
case 'cancelled':
return Colors.red;
default:
return Colors.grey;
}
}
return Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${'Driver Name:'.tr} ${data['name']}",
style: AppStyle.title,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${'Car Plate:'.tr} ${data['car_plate']}",
style: AppStyle.title,
),
Text(
"${'Car Make:'.tr} ${data['make']}",
style: AppStyle.title,
),
Text(
"${'Car Model:'.tr} ${data['model']}",
style: AppStyle.title,
),
Text(
"${"Car Color:".tr} ${data['color']}",
style: AppStyle.title,
),
],
),
SizedBox(
width: 100,
height: 100,
child: Icon(
Fontisto.car,
size: 80,
color: Color(
int.parse(
data['color_hex'].replaceFirst('#', '0xff'),
),
),
),
),
],
),
const SizedBox(height: 12),
const Divider(),
const SizedBox(height: 12),
Container(
color: getStatusColor(data['status']),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"${'Trip Status:'.tr} ${getLocalizedStatus(data['status'])}",
style: const TextStyle(fontSize: 16),
),
),
),
Text(
"${'Scheduled Time:'.tr} ${DateFormat('yyyy-MM-dd hh:mm a').format(DateTime.parse(data['timeSelected']))}",
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
data['status'].toString() != 'begin'
? MyElevatedButton(
title: "Cancel Trip".tr,
kolor: AppColor.redColor,
onPressed: () {
Get.find<MapPassengerController>().cancelVip(
data['token'].toString(),
data['id'].toString(),
);
},
)
: const SizedBox(),
Obx(() {
// If countdown is still running, show countdown
if (!vipOrderController.isButtonVisible.value) {
return Column(
children: [
CircularProgressIndicator(
value: 1 -
(vipOrderController.countdown.value / 60),
strokeWidth: 6.0,
color: AppColor.greenColor,
backgroundColor: AppColor.accentColor,
),
const SizedBox(height: 10),
Text(
"${vipOrderController.countdown.value}s ${'remaining'.tr}",
style: const TextStyle(fontSize: 16),
),
],
);
}
// Once countdown is complete, show "Send to Driver Again" button
return MyElevatedButton(
title: "Send to Driver Again".tr,
kolor: AppColor.greenColor,
onPressed: () {
Get.find<MapPassengerController>()
.sendToDriverAgain(data['token']);
vipOrderController.fetchOrder();
},
);
}),
],
),
const SizedBox(
height: 30,
),
data['status'].toString() == 'begin'
? MyElevatedButton(
title: "Click here to begin your trip\n\nGood luck, "
.tr +
box.read(BoxName.name).toString(),
kolor: AppColor.greenColor,
onPressed: () {
final mapPassengerController =
Get.find<MapPassengerController>();
mapPassengerController.make = data['make'];
mapPassengerController.licensePlate =
data['car_plate'];
mapPassengerController.model = data['model'];
mapPassengerController.driverId = data['driverId'];
mapPassengerController.carColor = data['color'];
mapPassengerController.driverRate = data['rating'];
mapPassengerController.colorHex = data['color_hex'];
mapPassengerController.driverPhone = data['phone'];
mapPassengerController.driverToken = data['token'];
mapPassengerController.driverName =
data['name'].toString().split(' ')[0];
Get.back();
mapPassengerController.begiVIPTripFromPassenger();
},
)
: const SizedBox()
],
),
),
);
}),
],
isleading: true,
);
}
}

View File

@@ -20,72 +20,58 @@ class LocaleController extends GetxController {
case "ar": case "ar":
locale = const Locale("ar"); locale = const Locale("ar");
appTheme = themeArabic; appTheme = themeArabic;
box.write(BoxName.lang, 'ar');
break; break;
case "en": case "en":
locale = const Locale("en"); locale = const Locale("en");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'en');
break; break;
case "tr": case "tr":
locale = const Locale("tr"); locale = const Locale("tr");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'tr');
break; break;
case "fr": case "fr":
locale = const Locale("fr"); locale = const Locale("fr");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'fr');
break; break;
case "it": case "it":
locale = const Locale("it"); locale = const Locale("it");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'it');
break; break;
case "de": case "de":
locale = const Locale("de"); locale = const Locale("de");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'de');
break; break;
case "el": case "el":
locale = const Locale("el"); locale = const Locale("el");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'el');
break; break;
case "es": case "es":
locale = const Locale("es"); locale = const Locale("es");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'es');
break; break;
case "fa": case "fa":
locale = const Locale("fa"); locale = const Locale("fa");
appTheme = themeEnglish; appTheme = themeArabic;
box.write(BoxName.lang, 'fa');
break; break;
case "zh": case "zh":
locale = const Locale("zh"); locale = const Locale("zh");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'zh');
break; break;
case "ru": case "ru":
locale = const Locale("ru"); locale = const Locale("ru");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'ru');
break; break;
case "hi": case "hi":
locale = const Locale("hi"); locale = const Locale("hi");
appTheme = themeEnglish; appTheme = themeEnglish;
box.write(BoxName.lang, 'hi');
break; break;
default: default:
locale = Locale(Get.deviceLocale!.languageCode); locale = Locale(Get.deviceLocale!.languageCode);
box.write(BoxName.lang, Get.deviceLocale!.languageCode);
appTheme = themeEnglish; appTheme = themeEnglish;
break; break;
} }
box.write(BoxName.lang, langcode); box.write(BoxName.lang, langcode);
// box.write(BoxName.lang, langcode);
Get.changeTheme(appTheme); Get.changeTheme(appTheme);
Get.updateLocale(locale); Get.updateLocale(locale);
restartApp(); restartApp();
@@ -94,62 +80,15 @@ class LocaleController extends GetxController {
@override @override
void onInit() { void onInit() {
String storedLang = box.read(BoxName.lang) ?? ""; String? storedLang = box.read(BoxName.lang);
switch (storedLang) { if (storedLang == null) {
case "ar": // Use device language if no language is stored
language = const Locale("ar"); storedLang = Get.deviceLocale!.languageCode;
appTheme = themeArabic; box.write(BoxName.lang, storedLang);
break;
case "en":
language = const Locale("en");
appTheme = themeEnglish;
break;
case "tr":
language = const Locale("tr");
appTheme = themeEnglish;
break;
case "fr":
language = const Locale("fr");
appTheme = themeEnglish;
break;
case "it":
language = const Locale("it");
appTheme = themeEnglish;
break;
case "de":
language = const Locale("de");
appTheme = themeEnglish;
break;
case "el":
language = const Locale("el");
appTheme = themeEnglish;
break;
case "es":
language = const Locale("es");
appTheme = themeEnglish;
break;
case "fa":
language = const Locale("fa");
appTheme = themeArabic;
break;
case "zh":
language = const Locale("zh");
appTheme = themeEnglish;
break;
case "ru":
language = const Locale("ru");
appTheme = themeEnglish;
break;
case "hi":
language = const Locale("hi");
appTheme = themeEnglish;
break;
default:
language = Locale(Get.deviceLocale!.languageCode);
appTheme = themeEnglish;
break;
} }
changeLang(storedLang);
super.onInit(); super.onInit();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
import 'package:SEFER/controller/local/phone_intel/helpers.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'countries.dart';
class PickerDialogStyle {
final Color? backgroundColor;
final TextStyle? countryCodeStyle;
final TextStyle? countryNameStyle;
final Widget? listTileDivider;
final EdgeInsets? listTilePadding;
final EdgeInsets? padding;
final Color? searchFieldCursorColor;
final InputDecoration? searchFieldInputDecoration;
final EdgeInsets? searchFieldPadding;
final double? width;
PickerDialogStyle({
this.backgroundColor,
this.countryCodeStyle,
this.countryNameStyle,
this.listTileDivider,
this.listTilePadding,
this.padding,
this.searchFieldCursorColor,
this.searchFieldInputDecoration,
this.searchFieldPadding,
this.width,
});
}
class CountryPickerDialog extends StatefulWidget {
final List<Country> countryList;
final Country selectedCountry;
final ValueChanged<Country> onCountryChanged;
final String searchText;
final List<Country> filteredCountries;
final PickerDialogStyle? style;
final String languageCode;
const CountryPickerDialog({
Key? key,
required this.searchText,
required this.languageCode,
required this.countryList,
required this.onCountryChanged,
required this.selectedCountry,
required this.filteredCountries,
this.style,
}) : super(key: key);
@override
State<CountryPickerDialog> createState() => _CountryPickerDialogState();
}
class _CountryPickerDialogState extends State<CountryPickerDialog> {
late List<Country> _filteredCountries;
late Country _selectedCountry;
@override
void initState() {
_selectedCountry = widget.selectedCountry;
_filteredCountries = widget.filteredCountries.toList()
..sort(
(a, b) => a
.localizedName(widget.languageCode)
.compareTo(b.localizedName(widget.languageCode)),
);
super.initState();
}
@override
Widget build(BuildContext context) {
final mediaWidth = MediaQuery.of(context).size.width;
final width = widget.style?.width ?? mediaWidth;
const defaultHorizontalPadding = 40.0;
const defaultVerticalPadding = 24.0;
return Dialog(
insetPadding: EdgeInsets.symmetric(
vertical: defaultVerticalPadding,
horizontal: mediaWidth > (width + defaultHorizontalPadding * 2)
? (mediaWidth - width) / 2
: defaultHorizontalPadding),
backgroundColor: widget.style?.backgroundColor,
child: Container(
padding: widget.style?.padding ?? const EdgeInsets.all(10),
child: Column(
children: <Widget>[
Padding(
padding:
widget.style?.searchFieldPadding ?? const EdgeInsets.all(0),
child: TextField(
cursorColor: widget.style?.searchFieldCursorColor,
decoration: widget.style?.searchFieldInputDecoration ??
InputDecoration(
suffixIcon: const Icon(Icons.search),
labelText: widget.searchText,
),
onChanged: (value) {
_filteredCountries = widget.countryList.stringSearch(value)
..sort(
(a, b) => a
.localizedName(widget.languageCode)
.compareTo(b.localizedName(widget.languageCode)),
);
if (mounted) setState(() {});
},
),
),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: _filteredCountries.length,
itemBuilder: (ctx, index) => Column(
children: <Widget>[
ListTile(
leading: kIsWeb
? Image.asset(
'assets/flags/${_filteredCountries[index].code.toLowerCase()}.png',
package: 'intl_phone_field',
width: 32,
)
: Text(
_filteredCountries[index].flag,
style: const TextStyle(fontSize: 18),
),
contentPadding: widget.style?.listTilePadding,
title: Text(
_filteredCountries[index]
.localizedName(widget.languageCode),
style: widget.style?.countryNameStyle ??
const TextStyle(fontWeight: FontWeight.w700),
),
trailing: Text(
'+${_filteredCountries[index].dialCode}',
style: widget.style?.countryCodeStyle ??
const TextStyle(fontWeight: FontWeight.w700),
),
onTap: () {
_selectedCountry = _filteredCountries[index];
widget.onCountryChanged(_selectedCountry);
Navigator.of(context).pop();
},
),
widget.style?.listTileDivider ??
const Divider(thickness: 1),
],
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'countries.dart';
bool isNumeric(String s) =>
s.isNotEmpty && int.tryParse(s.replaceAll("+", "")) != null;
String removeDiacritics(String str) {
var withDia =
'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽž';
var withoutDia =
'AAAAAAaaaaaaOOOOOOOooooooEEEEeeeeeCcDIIIIiiiiUUUUuuuuNnSsYyyZz';
for (int i = 0; i < withDia.length; i++) {
str = str.replaceAll(withDia[i], withoutDia[i]);
}
return str;
}
extension CountryExtensions on List<Country> {
List<Country> stringSearch(String search) {
search = removeDiacritics(search.toLowerCase());
return where(
(country) => isNumeric(search) || search.startsWith("+")
? country.dialCode.contains(search)
: removeDiacritics(country.name.replaceAll("+", "").toLowerCase())
.contains(search) ||
country.nameTranslations.values.any((element) =>
removeDiacritics(element.toLowerCase()).contains(search)),
).toList();
}
}

View File

@@ -0,0 +1,521 @@
library intl_phone_field;
import 'dart:async';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './countries.dart';
import './phone_number.dart';
import 'country_picker_dialog.dart';
import 'helpers.dart';
class IntlPhoneField extends StatefulWidget {
/// The TextFormField key.
final GlobalKey<FormFieldState>? formFieldKey;
/// Whether to hide the text being edited (e.g., for passwords).
final bool obscureText;
/// How the text should be aligned horizontally.
final TextAlign textAlign;
/// How the text should be aligned vertically.
final TextAlignVertical? textAlignVertical;
final VoidCallback? onTap;
/// {@macro flutter.widgets.editableText.readOnly}
final bool readOnly;
final FormFieldSetter<PhoneNumber>? onSaved;
/// {@macro flutter.widgets.editableText.onChanged}
///
/// See also:
///
/// * [inputFormatters], which are called before [onChanged]
/// runs and can validate and change ("format") the input value.
/// * [onEditingComplete], [onSubmitted], [onSelectionChanged]:
/// which are more specialized input change notifications.
final ValueChanged<PhoneNumber>? onChanged;
final ValueChanged<Country>? onCountryChanged;
/// An optional method that validates an input. Returns an error string to display if the input is invalid, or null otherwise.
///
/// A [PhoneNumber] is passed to the validator as argument.
/// The validator can handle asynchronous validation when declared as a [Future].
/// Or run synchronously when declared as a [Function].
///
/// By default, the validator checks whether the input number length is between selected country's phone numbers min and max length.
/// If `disableLengthCheck` is not set to `true`, your validator returned value will be overwritten by the default validator.
/// But, if `disableLengthCheck` is set to `true`, your validator will have to check phone number length itself.
final FutureOr<String?> Function(PhoneNumber?)? validator;
/// {@macro flutter.widgets.editableText.keyboardType}
final TextInputType keyboardType;
/// Controls the text being edited.
///
/// If null, this widget will create its own [TextEditingController].
final TextEditingController? controller;
/// Defines the keyboard focus for this widget.
///
/// The [focusNode] is a long-lived object that's typically managed by a
/// [StatefulWidget] parent. See [FocusNode] for more information.
///
/// To give the keyboard focus to this widget, provide a [focusNode] and then
/// use the current [FocusScope] to request the focus:
///
/// ```dart
/// FocusScope.of(context).requestFocus(myFocusNode);
/// ```
///
/// This happens automatically when the widget is tapped.
///
/// To be notified when the widget gains or loses the focus, add a listener
/// to the [focusNode]:
///
/// ```dart
/// focusNode.addListener(() { print(myFocusNode.hasFocus); });
/// ```
///
/// If null, this widget will create its own [FocusNode].
///
/// ## Keyboard
///
/// Requesting the focus will typically cause the keyboard to be shown
/// if it's not showing already.
///
/// On Android, the user can hide the keyboard - without changing the focus -
/// with the system back button. They can restore the keyboard's visibility
/// by tapping on a text field. The user might hide the keyboard and
/// switch to a physical keyboard, or they might just need to get it
/// out of the way for a moment, to expose something it's
/// obscuring. In this case requesting the focus again will not
/// cause the focus to change, and will not make the keyboard visible.
///
/// This widget builds an [EditableText] and will ensure that the keyboard is
/// showing when it is tapped by calling [EditableTextState.requestKeyboard()].
final FocusNode? focusNode;
/// {@macro flutter.widgets.editableText.onSubmitted}
///
/// See also:
///
/// * [EditableText.onSubmitted] for an example of how to handle moving to
/// the next/previous field when using [TextInputAction.next] and
/// [TextInputAction.previous] for [textInputAction].
final void Function(String)? onSubmitted;
/// If false the widget is "disabled": it ignores taps, the [TextFormField]'s
/// [decoration] is rendered in grey,
/// [decoration]'s [InputDecoration.counterText] is set to `""`,
/// and the drop down icon is hidden no matter [showDropdownIcon] value.
///
/// If non-null this property overrides the [decoration]'s
/// [Decoration.enabled] property.
final bool enabled;
/// The appearance of the keyboard.
///
/// This setting is only honored on iOS devices.
///
/// If unset, defaults to the brightness of [ThemeData.brightness].
final Brightness? keyboardAppearance;
/// Initial Value for the field.
/// This property can be used to pre-fill the field.
final String? initialValue;
final String languageCode;
/// 2 letter ISO Code or country dial code.
///
/// ```dart
/// initialCountryCode: 'IN', // India
/// initialCountryCode: '+225', // Côte d'Ivoire
/// ```
final String? initialCountryCode;
/// List of Country to display see countries.dart for format
final List<Country>? countries;
/// The decoration to show around the text field.
///
/// By default, draws a horizontal line under the text field but can be
/// configured to show an icon, label, hint text, and error text.
///
/// Specify null to remove the decoration entirely (including the
/// extra padding introduced by the decoration to save space for the labels).
final InputDecoration decoration;
/// The style to use for the text being edited.
///
/// This text style is also used as the base style for the [decoration].
///
/// If null, defaults to the `subtitle1` text style from the current [Theme].
final TextStyle? style;
/// Disable view Min/Max Length check
final bool disableLengthCheck;
/// Won't work if [enabled] is set to `false`.
final bool showDropdownIcon;
final BoxDecoration dropdownDecoration;
/// The style use for the country dial code.
final TextStyle? dropdownTextStyle;
/// {@macro flutter.widgets.editableText.inputFormatters}
final List<TextInputFormatter>? inputFormatters;
/// The text that describes the search input field.
///
/// When the input field is empty and unfocused, the label is displayed on top of the input field (i.e., at the same location on the screen where text may be entered in the input field).
/// When the input field receives focus (or if the field is non-empty), the label moves above (i.e., vertically adjacent to) the input field.
final String searchText;
/// Position of an icon [leading, trailing]
final IconPosition dropdownIconPosition;
/// Icon of the drop down button.
///
/// Default is [Icon(Icons.arrow_drop_down)]
final Icon dropdownIcon;
/// Whether this text field should focus itself if nothing else is already focused.
final bool autofocus;
/// Autovalidate mode for text form field.
///
/// If [AutovalidateMode.onUserInteraction], this FormField will only auto-validate after its content changes.
/// If [AutovalidateMode.always], it will auto-validate even without user interaction.
/// If [AutovalidateMode.disabled], auto-validation will be disabled.
///
/// Defaults to [AutovalidateMode.onUserInteraction].
final AutovalidateMode? autovalidateMode;
/// Whether to show or hide country flag.
///
/// Default value is `true`.
final bool showCountryFlag;
/// Message to be displayed on autoValidate error
///
/// Default value is `Invalid Mobile Number`.
final String? invalidNumberMessage;
/// The color of the cursor.
final Color? cursorColor;
/// How tall the cursor will be.
final double? cursorHeight;
/// How rounded the corners of the cursor should be.
final Radius? cursorRadius;
/// How thick the cursor will be.
final double cursorWidth;
/// Whether to show cursor.
final bool? showCursor;
/// The padding of the Flags Button.
///
/// The amount of insets that are applied to the Flags Button.
///
/// If unset, defaults to [EdgeInsets.zero].
final EdgeInsetsGeometry flagsButtonPadding;
/// The type of action button to use for the keyboard.
final TextInputAction? textInputAction;
/// Optional set of styles to allow for customizing the country search
/// & pick dialog
final PickerDialogStyle? pickerDialogStyle;
/// The margin of the country selector button.
///
/// The amount of space to surround the country selector button.
///
/// If unset, defaults to [EdgeInsets.zero].
final EdgeInsets flagsButtonMargin;
/// Enable the autofill hint for phone number.
final bool disableAutoFillHints;
/// If null, default magnification configuration will be used.
final TextMagnifierConfiguration? magnifierConfiguration;
const IntlPhoneField({
Key? key,
this.formFieldKey,
this.initialCountryCode,
this.languageCode = 'en',
this.disableAutoFillHints = false,
this.obscureText = false,
this.textAlign = TextAlign.left,
this.textAlignVertical,
this.onTap,
this.readOnly = false,
this.initialValue,
this.keyboardType = TextInputType.phone,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
this.style,
this.dropdownTextStyle,
this.onSubmitted,
this.validator,
this.onChanged,
this.countries,
this.onCountryChanged,
this.onSaved,
this.showDropdownIcon = true,
this.dropdownDecoration = const BoxDecoration(),
this.inputFormatters,
this.enabled = true,
this.keyboardAppearance,
@Deprecated('Use searchFieldInputDecoration of PickerDialogStyle instead')
this.searchText = 'Search country',
this.dropdownIconPosition = IconPosition.leading,
this.dropdownIcon = const Icon(Icons.arrow_drop_down),
this.autofocus = false,
this.textInputAction,
this.autovalidateMode = AutovalidateMode.onUserInteraction,
this.showCountryFlag = true,
this.cursorColor,
this.disableLengthCheck = false,
this.flagsButtonPadding = EdgeInsets.zero,
this.invalidNumberMessage = 'Invalid Mobile Number',
this.cursorHeight,
this.cursorRadius = Radius.zero,
this.cursorWidth = 2.0,
this.showCursor = true,
this.pickerDialogStyle,
this.flagsButtonMargin = EdgeInsets.zero,
this.magnifierConfiguration,
}) : super(key: key);
@override
State<IntlPhoneField> createState() => _IntlPhoneFieldState();
}
class _IntlPhoneFieldState extends State<IntlPhoneField> {
late List<Country> _countryList;
late Country _selectedCountry;
late List<Country> filteredCountries;
late String number;
String? validatorMessage;
@override
void initState() {
super.initState();
_countryList = widget.countries ?? countries;
filteredCountries = _countryList;
number = widget.initialValue ?? '';
if (widget.initialCountryCode == null && number.startsWith('+')) {
number = number.substring(1);
// parse initial value
_selectedCountry = countries.firstWhere(
(country) => number.startsWith(country.fullCountryCode),
orElse: () => _countryList.first);
// remove country code from the initial number value
number = number.replaceFirst(
RegExp("^${_selectedCountry.fullCountryCode}"), "");
} else {
_selectedCountry = _countryList.firstWhere(
(item) => item.code == (widget.initialCountryCode ?? 'US'),
orElse: () => _countryList.first);
// remove country code from the initial number value
if (number.startsWith('+')) {
number = number.replaceFirst(
RegExp("^\\+${_selectedCountry.fullCountryCode}"), "");
} else {
number = number.replaceFirst(
RegExp("^${_selectedCountry.fullCountryCode}"), "");
}
}
if (widget.autovalidateMode == AutovalidateMode.always) {
final initialPhoneNumber = PhoneNumber(
countryISOCode: _selectedCountry.code,
countryCode: '+${_selectedCountry.dialCode}',
number: widget.initialValue ?? '',
);
final value = widget.validator?.call(initialPhoneNumber);
if (value is String) {
validatorMessage = value;
} else {
(value as Future).then((msg) {
validatorMessage = msg;
});
}
}
}
Future<void> _changeCountry() async {
filteredCountries = _countryList;
await showDialog(
context: context,
useRootNavigator: false,
builder: (context) => StatefulBuilder(
builder: (ctx, setState) => CountryPickerDialog(
languageCode: widget.languageCode.toLowerCase(),
style: widget.pickerDialogStyle,
filteredCountries: filteredCountries,
searchText: widget.searchText,
countryList: _countryList,
selectedCountry: _selectedCountry,
onCountryChanged: (Country country) {
_selectedCountry = country;
widget.onCountryChanged?.call(country);
setState(() {});
},
),
),
);
if (mounted) setState(() {});
}
@override
Widget build(BuildContext context) {
return TextFormField(
key: widget.formFieldKey,
initialValue: (widget.controller == null) ? number : null,
autofillHints: widget.disableAutoFillHints
? null
: [AutofillHints.telephoneNumberNational],
readOnly: widget.readOnly,
obscureText: widget.obscureText,
textAlign: widget.textAlign,
textAlignVertical: widget.textAlignVertical,
cursorColor: widget.cursorColor,
onTap: widget.onTap,
controller: widget.controller,
focusNode: widget.focusNode,
cursorHeight: widget.cursorHeight,
cursorRadius: widget.cursorRadius,
cursorWidth: widget.cursorWidth,
showCursor: widget.showCursor,
onFieldSubmitted: widget.onSubmitted,
magnifierConfiguration: widget.magnifierConfiguration,
decoration: widget.decoration.copyWith(
prefixIcon: _buildFlagsButton(),
counterText: !widget.enabled ? '' : null,
),
style: widget.style,
onSaved: (value) {
widget.onSaved?.call(
PhoneNumber(
countryISOCode: _selectedCountry.code,
countryCode:
'+${_selectedCountry.dialCode}${_selectedCountry.regionCode}',
number: value!,
),
);
},
onChanged: (value) async {
final phoneNumber = PhoneNumber(
countryISOCode: _selectedCountry.code,
countryCode: '+${_selectedCountry.fullCountryCode}',
number: value,
);
if (widget.autovalidateMode != AutovalidateMode.disabled) {
validatorMessage = await widget.validator?.call(phoneNumber);
}
widget.onChanged?.call(phoneNumber);
},
validator: (value) {
if (value == null || !isNumeric(value)) return validatorMessage;
if (!widget.disableLengthCheck) {
return value.length >= _selectedCountry.minLength &&
value.length <= _selectedCountry.maxLength
? null
: widget.invalidNumberMessage;
}
return validatorMessage;
},
maxLength: widget.disableLengthCheck ? null : _selectedCountry.maxLength,
keyboardType: widget.keyboardType,
inputFormatters: widget.inputFormatters,
enabled: widget.enabled,
keyboardAppearance: widget.keyboardAppearance,
autofocus: widget.autofocus,
textInputAction: widget.textInputAction,
autovalidateMode: widget.autovalidateMode,
);
}
Container _buildFlagsButton() {
return Container(
margin: widget.flagsButtonMargin,
child: DecoratedBox(
decoration: widget.dropdownDecoration,
child: InkWell(
borderRadius: widget.dropdownDecoration.borderRadius as BorderRadius?,
onTap: widget.enabled ? _changeCountry : null,
child: Padding(
padding: widget.flagsButtonPadding,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const SizedBox(
width: 4,
),
if (widget.enabled &&
widget.showDropdownIcon &&
widget.dropdownIconPosition == IconPosition.leading) ...[
widget.dropdownIcon,
const SizedBox(width: 4),
],
if (widget.showCountryFlag) ...[
kIsWeb
? Image.asset(
'assets/flags/${_selectedCountry.code.toLowerCase()}.png',
package: 'intl_phone_field',
width: 32,
)
: Text(
_selectedCountry.flag,
style: const TextStyle(fontSize: 18),
),
const SizedBox(width: 8),
],
FittedBox(
child: Text(
'+${_selectedCountry.dialCode}',
style: widget.dropdownTextStyle,
),
),
if (widget.enabled &&
widget.showDropdownIcon &&
widget.dropdownIconPosition == IconPosition.trailing) ...[
const SizedBox(width: 4),
widget.dropdownIcon,
],
const SizedBox(width: 8),
],
),
),
),
),
);
}
}
enum IconPosition {
leading,
trailing,
}

View File

@@ -0,0 +1,79 @@
import 'countries.dart';
class NumberTooLongException implements Exception {}
class NumberTooShortException implements Exception {}
class InvalidCharactersException implements Exception {}
class PhoneNumber {
String countryISOCode;
String countryCode;
String number;
PhoneNumber({
required this.countryISOCode,
required this.countryCode,
required this.number,
});
factory PhoneNumber.fromCompleteNumber({required String completeNumber}) {
if (completeNumber == "") {
return PhoneNumber(countryISOCode: "", countryCode: "", number: "");
}
try {
Country country = getCountry(completeNumber);
String number;
if (completeNumber.startsWith('+')) {
number = completeNumber.substring(1 + country.dialCode.length + country.regionCode.length);
} else {
number = completeNumber.substring(country.dialCode.length + country.regionCode.length);
}
return PhoneNumber(
countryISOCode: country.code, countryCode: country.dialCode + country.regionCode, number: number);
} on InvalidCharactersException {
rethrow;
// ignore: unused_catch_clause
} on Exception catch (e) {
return PhoneNumber(countryISOCode: "", countryCode: "", number: "");
}
}
bool isValidNumber() {
Country country = getCountry(completeNumber);
if (number.length < country.minLength) {
throw NumberTooShortException();
}
if (number.length > country.maxLength) {
throw NumberTooLongException();
}
return true;
}
String get completeNumber {
return countryCode + number;
}
static Country getCountry(String phoneNumber) {
if (phoneNumber == "") {
throw NumberTooShortException();
}
final validPhoneNumber = RegExp(r'^[+0-9]*[0-9]*$');
if (!validPhoneNumber.hasMatch(phoneNumber)) {
throw InvalidCharactersException();
}
if (phoneNumber.startsWith('+')) {
return countries
.firstWhere((country) => phoneNumber.substring(1).startsWith(country.dialCode + country.regionCode));
}
return countries.firstWhere((country) => phoneNumber.startsWith(country.dialCode + country.regionCode));
}
@override
String toString() => 'PhoneNumber(countryISOCode: $countryISOCode, countryCode: $countryCode, number: $number)';
}

View File

@@ -4,7 +4,52 @@ class MyTranslation extends Translations {
@override @override
Map<String, Map<String, String>> get keys => { Map<String, Map<String, String>> get keys => {
"ar": { "ar": {
"Home Page": "الصفحة الرئيسية",
"To change Language the App": "لتغيير لغة التطبيق",
"Learn more about our app and mission":
"تعرف على المزيد حول تطبيقنا ورسالتنا",
"Promos For Today": "عروض اليوم",
'Bonus gift': 'بونص', "Pay": "ادفع",
"Get": "احصل على",
"Send to Driver Again": "إرسال إلى السائق مرة أخرى",
"Driver Name:": "اسم السائق:",
'No trip data available': "لا توجد بيانات رحلة متاحة",
"Car Plate:": "رقم اللوحة:", "remaining": "متبقي",
"Order Cancelled": "تم إلغاء الطلب",
'You canceled VIP trip': "ألغيت الرحلة",
"Passenger cancelled order": "الراكب قام بإلغاء الطلب",
"Your trip is scheduled": "رحلتك مجدولة",
"Don't forget your ride!": "لا تنسَ رحلتك!",
"Trip updated successfully": "تم تحديث الرحلة بنجاح",
"Car Make:": "ماركة السيارة:",
"Car Model:": "طراز السيارة:", "Car Color:": "لون السيارة:",
"Driver Phone:": "رقم هاتف السائق:",
'Pre-booking': 'احجز مسبقًا', "Waiting VIP": "انتظار VIP",
"Driver List": "قائمة السائقين", "Confirm Trip": "تأكيد الرحلة",
"Select date and time of trip": "حدد تاريخ ووقت الرحلة",
"Date and Time Picker": "اختيار التاريخ والوقت",
"Trip Status:": "حالة الرحلة:", "pending": "قيد الانتظار",
"accepted": "تم القبول",
"rejected": "تم الرفض",
"Scheduled Time:": "الوقت المحدد:",
"No drivers available": "لا يوجد سائقين متاحين",
"Please try again in a few moments":
"يرجى المحاولة مرة أخرى بعد قليل",
"Unknown Driver": "سائق غير معروف",
"rides": "الرحلات",
"The reason is": "السبب هو",
"User does not have a wallet #1652": "المستخدم ليس لديه محفظة ",
"Price of trip": "سعر الرحلة",
"For Speed and Delivery trips, the price is calculated dynamically. For Comfort trips, the price is based on time and distance":
"بالنسبة لرحلات السرعة والتوصيل، يتم حساب السعر ديناميكياً. بالنسبة لرحلات الراحة، يتم حساب السعر بناءً على الوقت والمسافة",
"Phone Wallet Saved Successfully": "تم حفظ المحفظة الهاتفية بنجاح",
"Add wallet phone you use": "أضف محفظة الهاتف التي تستخدمها",
"Update Available": "تحديث متوفر", "Update Available": "تحديث متوفر",
'Phone number must be exactly 11 digits long':
"رقم الهاتف يجب أن يكون بطول 11 رقماً",
'Insert Wallet phone number': 'أدخل رقم هاتف المحفظة',
"Phone number isn't an Egyptian phone number":
"رقم الهاتف ليس رقم هاتف مصري",
"A new version of the app is available. Please update to the latest version.": "A new version of the app is available. Please update to the latest version.":
"تتوفر نسخة جديدة من التطبيق. يرجى التحديث إلى أحدث إصدار.", "تتوفر نسخة جديدة من التطبيق. يرجى التحديث إلى أحدث إصدار.",
"We use location to get accurate and nearest passengers for you": "We use location to get accurate and nearest passengers for you":
@@ -41,14 +86,20 @@ class MyTranslation extends Translations {
"Pick from map destination": "حدد وجهتك على الخريطة", "Pick from map destination": "حدد وجهتك على الخريطة",
"Pick or Tap to confirm": "حدد أو انقر للتأكيد", "Pick or Tap to confirm": "حدد أو انقر للتأكيد",
"Select Order Type": "حدد نوع الطلب", "Select Order Type": "حدد نوع الطلب",
'Accepted your order': "تم قبول طلبك",
"Choose who this order is for": "اختر لمن هذا الطلب", "Choose who this order is for": "اختر لمن هذا الطلب",
"Order Accepted": "تم قبول الطلب", "with type": "مع النوع",
"accepted your order at price": "قبل طلبك بالسعر",
"I want to order for myself": "أريد أن أطلب لنفسي", "I want to order for myself": "أريد أن أطلب لنفسي",
"I want to order for someone else": "أريد أن أطلب لشخص آخر", "I want to order for someone else": "أريد أن أطلب لشخص آخر",
"Cancel Trip from driver": "إلغاء الرحلة من السائق", "Cancel Trip from driver": "إلغاء الرحلة من السائق",
"Order Cancelled": "تم إلغاء الطلب",
"you canceled order": "لقد قمت بإلغاء الطلب",
"If you want order to another person": "إذا كنت تريد الطلب لشخص آخر", "If you want order to another person": "إذا كنت تريد الطلب لشخص آخر",
"We will look for a new driver.\nPlease wait.": "Ok I will go now.": "حسنًا، سأذهب الآن.",
"سنبحث عن سائق جديد.\nمن فضلك انتظر.", "Hi, I will go now": "مرحبًا، سأذهب الآن.",
"upgrade price": "رفع السعر", "upgrade price": "رفع السعر",
'Please enter a correct phone': 'يرجى إدخال رقم هاتف صحيح',
'airport': 'مطار', 'airport': 'مطار',
"Best choice for a comfortable car with a flexible route and stop points. This airport offers visa entry at this price.": "Best choice for a comfortable car with a flexible route and stop points. This airport offers visa entry at this price.":
"أفضل اختيار لسيارة مريحة مع طريق ونقاط توقف مرنة. يقدم هذا المطار تأشيرة دخول بهذا السعر.", "أفضل اختيار لسيارة مريحة مع طريق ونقاط توقف مرنة. يقدم هذا المطار تأشيرة دخول بهذا السعر.",
@@ -224,15 +275,76 @@ iOS [https://getapp.cc/app/6458734951]
"Capture an Image of Your ID Document front": "Capture an Image of Your ID Document front":
"التقط صورة للواجهة الأمامية لوثيقة هويتك", "التقط صورة للواجهة الأمامية لوثيقة هويتك",
"NationalID": "الرقم القومي", "NationalID": "الرقم القومي",
'You can share the SEFER App with your friends and earn rewards for rides they take using your code':
'يمكنك مشاركة تطبيق SEFER مع أصدقائك وكسب مكافآت من الرحلات التي يقومون بها باستخدام كودك.',
"FullName": "الاسم الكامل", "FullName": "الاسم الكامل",
"No invitation found yet!": "لم يتم العثور على دعوات حتى الآن!",
"InspectionResult": "نتيجة الفحص", "InspectionResult": "نتيجة الفحص",
"Criminal Record": "السجل الجنائي", "Criminal Record": "السجل الجنائي", 'Share App': 'شارك التطبيق',
"The email or phone number is already registered.": "The email or phone number is already registered.":
"البريد الإلكتروني أو رقم الهاتف مسجل بالفعل.", "البريد الإلكتروني أو رقم الهاتف مسجل بالفعل.",
'To become a ride-sharing driver on the Sefer app, you need to upload your driver\'s license, ID document, and car registration document. Our AI system will instantly review and verify their authenticity in just 2-3 minutes. If your documents are approved, you can start working as a driver on the Sefer app. Please note, submitting fraudulent documents is a serious offense and may result in immediate termination and legal consequences.': 'To become a ride-sharing driver on the Sefer app, you need to upload your driver\'s license, ID document, and car registration document. Our AI system will instantly review and verify their authenticity in just 2-3 minutes. If your documents are approved, you can start working as a driver on the Sefer app. Please note, submitting fraudulent documents is a serious offense and may result in immediate termination and legal consequences.':
'لِتُصْبِحَ سَائِقَاً لِلرُّكوبِ المُشْتَرَكِ عَلَى تَطْبِيق سَفَر، يَجِبُ عَلَيْكَ تَحْمِيل رُخْصَةِ القِيَادَةِ، وَثِيقَةِ الهُوِيَّةِ، وَوَثِيقَةَ تَسْجِيل السَّيَّارَةِ. سَيَقُومُ نِظَامُ الذَّكَاءِ الاِصْطِنَاعِيِّ لَدَيْنَا بِمُرَاجَعَةِ وَتَحْقِيقِ صِحَّةِ الوَثَائِقِ فِي غُضُونِ ٢-٣ دَقَائِقَ فَقَطْ. إِذَا تَمَّتْ المُوَافَقَةُ عَلَى وَثَائِقِكَ، يُمْكِنُكَ البَدْءُ فِي العَمَلِ كَسَائِقٍ عَلَى تَطْبِيق سَفَر. يُرْجَى مُلَاحَظَةُ، تَقْدِيمُ وَثَائِقَ مُزَورَةٍ يُعَدُّ جَرِيمَةً خَطِيرَةً وَقَدْ يَتَرَتَّبُ عَلَيْهِ اِنهَاءُ الحِسَابِ فَوْرِيَّاً وَعَوَاقِبُ قَانُونِيَّة.', 'لِتُصْبِحَ سَائِقَاً لِلرُّكوبِ المُشْتَرَكِ عَلَى تَطْبِيق سَفَر، يَجِبُ عَلَيْكَ تَحْمِيل رُخْصَةِ القِيَادَةِ، وَثِيقَةِ الهُوِيَّةِ، وَوَثِيقَةَ تَسْجِيل السَّيَّارَةِ. سَيَقُومُ نِظَامُ الذَّكَاءِ الاِصْطِنَاعِيِّ لَدَيْنَا بِمُرَاجَعَةِ وَتَحْقِيقِ صِحَّةِ الوَثَائِقِ فِي غُضُونِ ٢-٣ دَقَائِقَ فَقَطْ. إِذَا تَمَّتْ المُوَافَقَةُ عَلَى وَثَائِقِكَ، يُمْكِنُكَ البَدْءُ فِي العَمَلِ كَسَائِقٍ عَلَى تَطْبِيق سَفَر. يُرْجَى مُلَاحَظَةُ، تَقْدِيمُ وَثَائِقَ مُزَورَةٍ يُعَدُّ جَرِيمَةً خَطِيرَةً وَقَدْ يَتَرَتَّبُ عَلَيْهِ اِنهَاءُ الحِسَابِ فَوْرِيَّاً وَعَوَاقِبُ قَانُونِيَّة.',
"Documents check": "فحص الوثائق", "Documents check": "فحص الوثائق",
"Driver's License": "رخصة القيادة", "Driver's License": "رخصة القيادة",
"for your first registration!": "للتسجيل الأول!",
"Get it Now!": "احصل عليه الآن!",
"before": "قبل",
"Code not approved": "الرمز غير موافق عليه",
"3000 LE": "3000 جنيه مصري",
"Do you have an invitation code from another driver?":
"هل لديك كود دعوة من سائق آخر؟",
"Paste the code here": "الصق الكود هنا",
"No, I don't have a code": "لا، لا أملك كودا",
"Code approved": "تمت الموافقة على الكود",
"Install our app:": "قم بتثبيت تطبيقنا:",
"Invite another driver and both get a gift after he completes 100 trips!":
"ادع صديقًا ليكون سائقًا واحصلا على هدية بعد إكماله 100 مشوار!",
"Share App": "شارك التطبيق",
"Invite": "دعوة", "Are you sure?": "هل أنت متأكد؟",
"This will delete all recorded files from your device.":
"سيؤدي هذا إلى حذف جميع الملفات المسجلة من جهازك.",
"Select a file": "اختر ملفاً",
"Select a File": "اختر ملفاً", "Delete": "حذف",
'attach audio of complain': 'إرفاق صوت للشكوى',
"Phone Number Check": "فحص رقم الهاتف",
"Drivers received orders": "السائقون استقبلوا الطلبات",
'No audio files recorded.': 'لا توجد ملفات صوتية مسجلة.',
'This is for delivery or a motorcycle.':
"هذا للتوصيل أو للدراجة النارية.",
"We will look for a new driver.\nPlease wait.":
"سوف نبحث عن سائق جديد.\nيرجى الانتظار",
"Sefer Reminder": "تطبيق سفر",
"It's time to check the Sefer app!": "حان وقت استخدام تطبيق سفر",
"The email or phone number is already registered.":
"البريد الإلكتروني أو رقم الهاتف مسجل بالفعل.",
"you must insert token code": "يجب إدخال رمز التحقق.",
"Something went wrong. Please try again.":
"حدث خطأ ما. يرجى المحاولة مرة أخرى.",
"This is for delivery or a motorcycle.":
"هذا للتوصيل أو للدراجة النارية.",
"Trip Details": "تفاصيل الرحلة",
'The context does not provide any complaint details, so I cannot provide a solution to this issue. Please provide the necessary information, and I will be happy to assist you.':
"لا تتوفر تفاصيل الشكوى في السياق، لذا لا أستطيع تقديم حل لهذه المشكلة. يرجى تقديم المعلومات اللازمة، وسأكون سعيدًا بمساعدتك",
'Submit Your Complaint': "أرسل شكواك",
"Date": "التاريخ",
"Price": "السعر",
"Status": "الحالة",
"Choose from contact": "اختر من جهات الاتصال",
'attach correct audio': "إرفاق صوت للشكوى",
'be sure': 'كن متأكدًا',
'Audio uploaded successfully.': 'تم رفع الصوت بنجاح',
"Perfect for passengers seeking the latest car models with the freedom to choose any route they desire":
"مثالي للركاب الذين يبحثون عن أحدث موديلات السيارات مع حرية اختيار أي طريق يرغبون به",
"Share this code with your friends and earn rewards when they use it!":
"شارك هذا الرمز مع أصدقائك واحصل على مكافآت عند استخدامهم له!",
"Enter phone": "أدخل رقم الهاتف",
'You deserve the gift': "أنت تستحق الهدية",
"complete, you can claim your gift": " يمكنك المطالبة بهديتك",
"When": "‏عندما يكمل",
"Enter driver's phone": "أدخل رقم هاتف السائق",
"Send Invite": "أرسل الدعوة", "Show Invitations": "عرض الدعوات",
"License Type": "نوع الرخصة", "License Type": "نوع الرخصة",
"National Number": "الرقم الوطني", "National Number": "الرقم الوطني",
"Name (Arabic)": "الاسم بالعربي", "Name (Arabic)": "الاسم بالعربي",
@@ -369,7 +481,7 @@ iOS [https://getapp.cc/app/6458734951]
"Go to this Target": "الانْتِقَال إِلَى هَذَا الهَدَف", "Go to this Target": "الانْتِقَال إِلَى هَذَا الهَدَف",
"My Profile": "مَلَفِي الشَّخْصِي", "My Profile": "مَلَفِي الشَّخْصِي",
"Sign Out": "تَسْجِيل الخُرُوج", "Sign Out": "تَسْجِيل الخُرُوج",
"Home Page": "الصَّفْحَة الرَّئِيسِيَّة",
"Are you want to go to this site": "Are you want to go to this site":
"هَل تَرْغَب فِي الانْتِقَال إِلَى هَذَا المَوْقِع", "هَل تَرْغَب فِي الانْتِقَال إِلَى هَذَا المَوْقِع",
"MyLocation": "مَوْقِعِي", "MyLocation": "مَوْقِعِي",
@@ -446,11 +558,11 @@ iOS [https://getapp.cc/app/6458734951]
"Bachelor's Degree": "بَكَالُورِيُوس", "Bachelor's Degree": "بَكَالُورِيُوس",
"Master's Degree": "مَاجِسْتِير", "Master's Degree": "مَاجِسْتِير",
"Doctoral Degree": "دُكْتُورَاه", "Doctoral Degree": "دُكْتُورَاه",
"Promos For today": "الْعُرُوض التَّرْوِيجِيَّة لِلْيَوْم",
"Copy this Promo to use it in your Ride!": "Copy this Promo to use it in your Ride!":
"انْسَخْ هَذَا الْعَرْض لِاسْتِخْدَامِهِ فِي رِحْلَتِك!", "انْسَخْ هَذَا الْعَرْض لِاسْتِخْدَامِهِ فِي رِحْلَتِك!",
"To change some Settings": "لِتَغْيِير بَعْض الإِعْدَادَات", "To change some Settings": "لِتَغْيِير بَعْض الإِعْدَادَات",
"To change Language the App": "لِتَغْيِير لُغَة التَّطْبِيق",
"Order Request Page": "صَفْحَة طَلَب الطَّلَب", "Order Request Page": "صَفْحَة طَلَب الطَّلَب",
"Rouats of Trip": "طُرُق الرِّحْلَة", "Rouats of Trip": "طُرُق الرِّحْلَة",
"Passenger Name is ": "اسْم الرَّاكِب هُوَ ", "Passenger Name is ": "اسْم الرَّاكِب هُوَ ",
@@ -503,6 +615,30 @@ iOS [https://getapp.cc/app/6458734951]
"Do you want to pay Tips for this Driver": "Do you want to pay Tips for this Driver":
"هَل تُرِيد دَفْع أُكْرَامِيَّة لِهَذَا السَّائِق؟", "هَل تُرِيد دَفْع أُكْرَامِيَّة لِهَذَا السَّائِق؟",
"Tip is ": " مَبْلَغ الأُكْرَامِيَّة هُوَ", "Tip is ": " مَبْلَغ الأُكْرَامِيَّة هُوَ",
"Are you sure to delete this location?":
"هل أنت متأكد من حذف هذا الموقع؟",
'Are you want to wait drivers to accept your order':
'هل تريد الانتظار حتى يقبل السائقون طلبك؟',
"deleted": "تم الحذف",
'Trip is begin': "الرحلة قد بدأت",
'This price is fixed even if the route changes for the driver.':
"هذا السعر مثبت حتى لو تغير المسار للسائق",
'The price may increase if the route changes.':
"احتمالية زيادة السعر عند تغيير المسار",
"The captain is responsible for the route.":
"الكابتن مسؤول عن المسار",
'Your order is being prepared': "جاري تجهيز الطلب",
'The drivers are reviewing your request': 'يدرس السائقين طلبك',
'Your order sent to drivers': 'تم إرسال طلبك إلى السائقين',
"يمكنك الاتصال أو تسجيل صوت لهذه الرحلة":
"You can call or record audio of this trip",
'The trip has started! Feel free to contact emergency numbers, share your trip, or activate voice recording for the journey':
"بدأت الرحلة! لا تتردد في الاتصال بأرقام الطوارئ، مشاركة رحلتك، أو تفعيل التسجيل الصوتي للرحلة",
'Please make sure you have all your personal belongings and that any remaining fare, if applicable, has been added to your wallet before leaving. Thank you for choosing the Sefer app':
'الرجاء التأكد من جميع أغراضك الشخصية وأنه تم إضافة باقي الأجرة إن وجد إلى محفظتك قبل النزول. شكرا لاختيارك تطبيق سفر',
'Dont forget your personal belongings.': "لا تنسى متعلقاتك الشخصية",
"Tip is": " مَبْلَغ الأُكْرَامِيَّة هُوَ", "Tip is": " مَبْلَغ الأُكْرَامِيَّة هُوَ",
"Camera Access Denied.": "تَمَّ رَفْض الْوُصُول إِلَى الْكَامِيرَا.", "Camera Access Denied.": "تَمَّ رَفْض الْوُصُول إِلَى الْكَامِيرَا.",
"Open Settings": "افْتَحْ الإِعْدَادَات", "Open Settings": "افْتَحْ الإِعْدَادَات",
@@ -596,6 +732,45 @@ iOS [https://getapp.cc/app/6458734951]
'الرَّجَاء التَّحَرُّك إِلَى السَّيَّارَة الآن', 'الرَّجَاء التَّحَرُّك إِلَى السَّيَّارَة الآن',
'You will receive a code in WhatsApp Messenger': 'You will receive a code in WhatsApp Messenger':
"سوف تتلقى رمزًا في واتساب ماسنجر", "سوف تتلقى رمزًا في واتساب ماسنجر",
'If you need assistance, contact us':
"إذا كنت بحاجة إلى المساعدة، تواصل معنا",
"Promo Ended": "انتهى العرض",
'Enter the promo code and get': 'أدخل رمز الترويج واحصل على',
'DISCOUNT': 'خصم',
'No wallet record found': 'لم يتم العثور على سجل محفظة',
'for': 'لمدة',
"SEFER is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.":
"سفر هو التطبيق الأكثر أمانًا لمشاركة الركوب الذي يقدم العديد من الميزات لكل من السائقين والركاب. نحن نقدم أقل عمولة بنسبة 8% فقط، مما يضمن حصولك على أفضل قيمة لرحلاتك. يتضمن تطبيقنا التأمين لأفضل السائقين، الصيانة المنتظمة للسيارات مع أفضل المهندسين، والخدمات على الطريق لضمان تجربة محترمة وعالية الجودة لجميع المستخدمين.",
"You can contact us during working hours from 12:00 - 19:00.":
"يمكنك الاتصال بنا خلال ساعات العمل من 12:00 - 7:00.",
"Contact Us": "اتصل بنا",
'Choose a contact option': 'اختر خيار الاتصال',
'Work time is from 12:00 - 19:00.\nYou can send a WhatsApp message or email.':
'ساعات العمل من 12:00 - 19:00.\nيمكنك إرسال رسالة عبر واتساب أو بريد إلكتروني.',
'Promo code copied to clipboard!': "'تم نسخ رمز العرض إلى الحافظة!'",
'Copy Code': 'نسخ الرمز',
"Your invite code was successfully applied!":
"تم تطبيق رمز الدعوة بنجاح!",
"Payment Options": " خيارات الدفع",
"wait 1 minute to receive message":
"انتظر دقيقة واحدة لاستلام الرسالة",
'Promo Copied!': 'تم نسخ العرض!',
'You have copied the promo code.': 'لقد قمت بنسخ رمز العرض.',
'Valid Until:': 'لمدة:',
"Select Payment Amount": " اختر مبلغ الدفع",
"The promotion period has ended.": "انتهت فترة العرض.",
"Promo Code Accepted": "تم قبول كود العرض",
'Tap on the promo code to copy it!': 'اضغط على رمز العرض لنسخه!',
"Lowest Price Achieved": "تم الوصول إلى أدنى سعر",
"Cannot apply further discounts.":
"لا يمكن تطبيق المزيد من الخصومات.",
"Promo Already Used": "تم استخدام كود العرض بالفعل",
'Invitation Used': "تم استخدام الدعوة",
"You have already used this promo code.":
"لقد استخدمت هذا الكود بالفعل.",
"Insert Your Promo Code": "أدخل كود العرض الخاص بك",
"Enter promo code here": "أدخل كود العرض هنا",
"Please enter a valid promo code": "يرجى إدخال كود عرض صالح",
'Awfar Car': 'أوفر كار', 'Awfar Car': 'أوفر كار',
"Old and affordable, perfect for budget rides.": "Old and affordable, perfect for budget rides.":
"سيارة ميسورة التكلفة، مثالية للرحلات الاقتصادية.", "سيارة ميسورة التكلفة، مثالية للرحلات الاقتصادية.",
@@ -656,7 +831,7 @@ iOS [https://getapp.cc/app/6458734951]
'Selected Date and Time': "التاريخ والوقت المحددان", 'Selected Date and Time': "التاريخ والوقت المحددان",
"Lets check Car license ": "Lets check Car license ":
"دَعْنَا نَتَحَقَّق مِن رُخْصَة السَّيَّارَة ", "دَعْنَا نَتَحَقَّق مِن رُخْصَة السَّيَّارَة ",
'Driver List': 'قائمة السائقين', // 'Driver List': 'قائمة السائقين',
'Car': 'السيارة', 'Car': 'السيارة',
'Plate': 'لوحة السيارة', 'Plate': 'لوحة السيارة',
'N/A': 'غير متوفر', 'N/A': 'غير متوفر',

View File

@@ -1,13 +1,12 @@
import 'dart:convert'; import 'dart:convert';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/controller/firebase/firbase_messge.dart'; import 'package:SEFER/controller/firebase/firbase_messge.dart';
import '../../constant/box_name.dart'; import '../../constant/box_name.dart';
import '../../constant/links.dart'; import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart'; import '../../main.dart';
import '../../views/widgets/elevated_btn.dart';
import '../functions/crud.dart'; import '../functions/crud.dart';
class PassengerNotificationController extends GetxController { class PassengerNotificationController extends GetxController {
@@ -21,22 +20,17 @@ class PassengerNotificationController extends GetxController {
link: AppLink.getNotificationPassenger, link: AppLink.getNotificationPassenger,
payload: {'passenger_id': box.read(BoxName.passengerID)}); payload: {'passenger_id': box.read(BoxName.passengerID)});
if (res == "failure") { if (res == "failure") {
Get.defaultDialog( MyDialog().getDialog('There is no notification yet'.tr, '', () {
title: 'There is no notification yet'.tr, Get.back();
titleStyle: AppStyle.title, Get.back();
middleText: '', });
confirm: MyElevatedButton( } else {
title: 'Back', notificationData = jsonDecode(res);
onPressed: () { isloading = false;
Get.back(); update();
Get.back();
}));
} }
notificationData = jsonDecode(res);
// sql.insertData(notificationData['message'], TableName.captainNotification);
isloading = false; // sql.insertData(notificationData['message'], TableName.captainNotification);
update();
} }
updateNotification(String id) async { updateNotification(String id) async {

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:SEFER/constant/style.dart'; import 'package:SEFER/constant/style.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/links.dart'; import 'package:SEFER/constant/links.dart';
@@ -23,17 +24,10 @@ class PassengerWalletHistoryController extends GetxController {
isLoading = false; isLoading = false;
update(); update();
} else { } else {
Get.defaultDialog( MyDialog().getDialog('No wallet record found'.tr, '', () {
barrierDismissible: false, Get.back();
title: 'No wallet record found'.tr, Get.back();
titleStyle: AppStyle.title, });
middleText: '',
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
Get.back();
}));
} }
} }

View File

@@ -19,6 +19,7 @@ import '../../constant/colors.dart';
import '../../constant/info.dart'; import '../../constant/info.dart';
import '../../constant/links.dart'; import '../../constant/links.dart';
import '../../main.dart'; import '../../main.dart';
import '../../print.dart';
import '../functions/crud.dart'; import '../functions/crud.dart';
import '../functions/toast.dart'; import '../functions/toast.dart';
import 'paymob/paymob_wallet.dart'; import 'paymob/paymob_wallet.dart';
@@ -31,6 +32,7 @@ class PaymentController extends GetxController {
bool isPromoSheetDialogue = false; bool isPromoSheetDialogue = false;
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final promo = TextEditingController(); final promo = TextEditingController();
final walletphoneController = TextEditingController();
double totalPassenger = Get.find<MapPassengerController>().totalPassenger; double totalPassenger = Get.find<MapPassengerController>().totalPassenger;
int? selectedAmount = 0; int? selectedAmount = 0;
List<dynamic> totalPassengerWalletDetails = []; List<dynamic> totalPassengerWalletDetails = [];
@@ -166,7 +168,7 @@ class PaymentController extends GetxController {
? '1140' ? '1140'
: '0'); : '0');
getPassengerWallet(); // getPassengerWallet();
isLoading = false; isLoading = false;
update(); update();
@@ -696,25 +698,31 @@ class PaymentController extends GetxController {
billingData: PaymobBillingDataWallet(), billingData: PaymobBillingDataWallet(),
onPayment: (PaymobResponseWallet response) {}, onPayment: (PaymobResponseWallet response) {},
); );
// Log.print('response.message!: ${response!.responseCode!}');
// if (response!.success == true && response.responseCode == '200') {
if (response!.success == true && response.responseCode == '200') { if (response!.success == true &&
Get.defaultDialog( response!.message.toString() == 'Approved') {
barrierDismissible: false, // Log.print('transactionID wewer: ${response.transactionID}');
title: 'Payment Successful'.tr, Toast.show(context, 'Payment Successful'.tr, AppColor.greenColor);
titleStyle: AppStyle.title, method();
content: Text( // Get.defaultDialog(
'The payment was approved.'.tr, // barrierDismissible: false,
style: AppStyle.title, // title: 'Payment Successful'.tr,
), // titleStyle: AppStyle.title,
confirm: MyElevatedButton( // content: Text(
title: 'OK'.tr, // 'The payment was approved.'.tr,
kolor: AppColor.greenColor, // style: AppStyle.title,
onPressed: () async { // ),
Get.back(); // confirm: MyElevatedButton(
method(); // title: 'OK'.tr,
}, // kolor: AppColor.greenColor,
), // onPressed: () async {
); // Get.back();
// method();
// },
// ),
// );
} else { } else {
Get.defaultDialog( Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
@@ -762,26 +770,29 @@ class PaymentController extends GetxController {
billingData: PaymobBillingData(), billingData: PaymobBillingData(),
onPayment: (PaymobResponse response) {}, onPayment: (PaymobResponse response) {},
); );
if (response!.responseCode == '200' && response.success == true) {
if (response!.responseCode == 'APPROVED') { // if (response!.success == true && response.responseCode == '200') {
Get.defaultDialog( // if (response!.responseCode == 'APPROVED') {
barrierDismissible: false, Toast.show(context, 'Payment Successful'.tr, AppColor.greenColor);
title: 'Payment Successful'.tr, method();
titleStyle: AppStyle.title, // Get.defaultDialog(
// backgroundColor: AppColor.greenColor, // barrierDismissible: false,
content: Text( // title: 'Payment Successful'.tr,
'The payment was approved.'.tr, // titleStyle: AppStyle.title,
style: AppStyle.title, // // backgroundColor: AppColor.greenColor,
), // content: Text(
confirm: MyElevatedButton( // 'The payment was approved.'.tr,
kolor: AppColor.greenColor, // style: AppStyle.title,
title: 'OK'.tr, // ),
onPressed: () async { // confirm: MyElevatedButton(
Get.back(); // kolor: AppColor.greenColor,
method(); // title: 'OK'.tr,
}, // onPressed: () async {
), // Get.back();
); // method();
// },
// ),
// );
} else { } else {
Get.defaultDialog( Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
@@ -801,6 +812,7 @@ class PaymentController extends GetxController {
), ),
); );
} }
// }
} }
} catch (e) { } catch (e) {
Get.defaultDialog( Get.defaultDialog(

View File

@@ -144,7 +144,7 @@ class PaymobPaymentWallet {
}) async { }) async {
final Map<String, dynamic> data = { final Map<String, dynamic> data = {
"source": { "source": {
"identifier": box.read(BoxName.phone).toString(), "identifier": box.read(BoxName.phoneWallet).toString(),
"subtype": "WALLET", "subtype": "WALLET",
}, },
"payment_token": paymentToken, "payment_token": paymentToken,
@@ -269,7 +269,7 @@ class PaymobBillingDataWallet {
"email": box.read(BoxName.email) ?? box.read(BoxName.emailDriver), "email": box.read(BoxName.email) ?? box.read(BoxName.emailDriver),
"first_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver), "first_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
"last_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver), "last_name": box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
"phone_number": box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver), "phone_number": box.read(BoxName.phoneWallet),
"apartment": apartment ?? "NA", "apartment": apartment ?? "NA",
"floor": floor ?? "NA", "floor": floor ?? "NA",
"building": building ?? "NA", "building": building ?? "NA",

View File

@@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:SEFER/constant/colors.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
@@ -48,40 +50,41 @@ class ProfileController extends GetxController {
} }
updatField(String columnName, TextInputType type) async { updatField(String columnName, TextInputType type) async {
Get.defaultDialog( Get.dialog(
title: '${'Update'.tr} $columnName', CupertinoAlertDialog(
content: Column( title: Text('${'Update'.tr} $columnName'),
children: [ content: Column(
SizedBox( children: [
width: Get.width * .7, const SizedBox(height: 16), // Add spacing between title and input
child: MyTextForm( CupertinoTextField(
controller: txtController, controller: txtController,
label: 'type here'.tr, placeholder: 'type here'.tr,
hint: 'type here', keyboardType: type,
type: type) padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
// TextField( decoration: BoxDecoration(
// controller: txtController, border: Border.all(color: CupertinoColors.lightBackgroundGray),
// decoration: const InputDecoration( borderRadius: BorderRadius.circular(8),
// border: OutlineInputBorder(), hintText: 'type here'),
// ),
), ),
MyElevatedButton( ),
title: 'Update'.tr, const SizedBox(height: 20),
onPressed: () async { CupertinoButton(
Get.back(); color: AppColor.blueColor,
await updateColumn({ onPressed: () async {
'id': box.read(BoxName.passengerID), Get.back();
columnName: txtController.text, await updateColumn({
}); 'id': box.read(BoxName.passengerID),
if (columnName == 'first_name') { columnName: txtController.text,
box.write(BoxName.name, txtController.text); });
} if (columnName == 'first_name') {
box.write(BoxName.name, txtController.text);
}
txtController.clear(); txtController.clear();
}, },
) child: Text('Update'.tr),
], ),
],
),
), ),
); );
} }

View File

@@ -70,24 +70,39 @@ class RateController extends GetxController {
'token': token1, 'token': token1,
}); });
if (res != 'failure') { if (res != 'failure') {
FirebaseMessagesController().sendNotificationToAnyWithoutData( FirebaseMessagesController().sendNotificationToDriverMAP(
'You Have Tips'.tr, 'You Have Tips'.tr,
'${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + (Get.find<MapPassengerController>().totalPassenger)}', '${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + (Get.find<MapPassengerController>().totalPassenger)}',
Get.find<MapPassengerController>().driverToken.toString(), Get.find<MapPassengerController>().driverToken.toString(),
[],
'ding.wav', 'ding.wav',
); );
} }
} }
} }
await CRUD().post(link: AppLink.addRateToDriver, payload: { await CRUD().post(
'passenger_id': box.read(BoxName.passengerID).toString(), link: "${AppLink.seferCairoServer}/ride/rate/addRateToDriver.php",
'driver_id': driverId, payload: {
'ride_id': rideId, 'passenger_id': box.read(BoxName.passengerID).toString(),
'rating': selectedRateItemId.toString(), 'driver_id': driverId.toString(),
'comment': comment.text, 'ride_id': rideId.toString(),
}).then((value) { 'rating': selectedRateItemId.toString(),
Get.find<MapPassengerController>().restCounter(); 'comment': comment.text,
Get.offAll(const MapPagePassenger()); });
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rate/addRateToDriver.php",
payload: {
'passenger_id': box.read(BoxName.passengerID).toString(),
'driver_id': driverId.toString(),
'ride_id': rideId.toString(),
'rating': selectedRateItemId.toString(),
'comment': comment.text,
});
}
Get.find<MapPassengerController>().restCounter();
Get.offAll(const MapPagePassenger());
} }
} }

View File

@@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:SEFER/controller/payment/paymob/paymob_response.dart'; import 'package:SEFER/controller/payment/paymob/paymob_response.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
@@ -12,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:wakelock_plus/wakelock_plus.dart';
import 'constant/api_key.dart'; import 'constant/api_key.dart';
import 'constant/info.dart'; import 'constant/info.dart';
import 'constant/notification.dart';
import 'controller/firebase/firbase_messge.dart'; import 'controller/firebase/firbase_messge.dart';
import 'controller/firebase/local_notification.dart'; import 'controller/firebase/local_notification.dart';
import 'controller/local/local_controller.dart'; import 'controller/local/local_controller.dart';
@@ -40,10 +42,12 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
WakelockPlus.enable(); WakelockPlus.enable();
// await LocationController().startLocationUpdates(); // await LocationController().startLocationUpdates();
if (Platform.isAndroid) { // if (Platform.isAndroid) {
await NotificationController().initNotifications(); NotificationController notificationController =
} Get.put(NotificationController());
// await NotificationController().initNotifications();
// }
await GetStorage.init(); await GetStorage.init();
// Get.put(DriverCallController()); // Get.put(DriverCallController());
// await AC().gAK(); // await AC().gAK();
@@ -77,20 +81,25 @@ void main() async {
userTokenExpiration: 200, userTokenExpiration: 200,
iFrameID: 837992, iFrameID: 837992,
); );
PaymobPaymentWallet.instance.initialize( PaymobPaymentWallet.instance.initialize(
apiKey: AK apiKey: AK.payMobApikey,
.payMobApikey, // from dashboard Select Settings -> Account Info -> API Key
integrationID: int.parse(AK.integrationIdPayMobWallet), integrationID: int.parse(AK.integrationIdPayMobWallet),
userTokenExpiration: 200, userTokenExpiration: 200,
iFrameID: 837992, iFrameID: 837992,
); );
// Get device information await notificationController.initNotifications();
// List<Map<String, dynamic>> deviceDataList =
// await DeviceInfoPlus.getDeviceInfo();
//
// // Print all device data
// DeviceInfoPlus.printDeviceInfo();
// Generate a random index to pick a message
final random = Random();
final randomMessage = messages[random.nextInt(messages.length)];
// Schedule the notification with the random message
notificationController.scheduleNotificationsForSevenDays(
randomMessage.split(':')[0],
randomMessage.split(':')[1],
"tone1",
);
runApp(const MyApp()); runApp(const MyApp());
} }
@@ -104,20 +113,20 @@ class MyApp extends StatelessWidget {
LocaleController localController = Get.put(LocaleController()); LocaleController localController = Get.put(LocaleController());
return GetMaterialApp( return GetMaterialApp(
title: AppInformation.appName, title: AppInformation.appName,
translations: MyTranslation(), translations: MyTranslation(),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
locale: localController.language, locale: localController.language,
theme: localController.appTheme, theme: localController.appTheme,
key: UniqueKey(), key: UniqueKey(),
// routes: {'/':const HomePage()}, // routes: {'/':const HomePage()},
home: SplashScreen() // home: LoginCaptin());
// initialRoute: '/', initialRoute: '/',
// getPages: [ getPages: [
// GetPage(name: '/', page: () => SplashScreen()), GetPage(name: '/', page: () => SplashScreen()),
// GetPage(name: '/tripmonitor', page: () => const TripMonitor()), GetPage(name: '/tripmonitor', page: () => const TripMonitor()),
// ], ],
// home: SplashScreen() // home: SplashScreen()
); );
} }
} }

View File

@@ -37,18 +37,21 @@ class DbSql {
latitude REAL, latitude REAL,
longitude REAL, longitude REAL,
name TEXT UNIQUE, name TEXT UNIQUE,
rate TEXT rate TEXT,
createdAt TEXT
) )
'''); ''');
await db.execute('DROP TABLE IF EXISTS ${TableName.recentLocations}');
await db.execute(''' await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.recentLocations}( CREATE TABLE ${TableName.recentLocations}(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
latitude REAL, latitude REAL,
longitude REAL, longitude REAL,
name TEXT , name TEXT,
rate TEXT rate TEXT,
) createdAt TEXT
'''); )
''');
await db.execute(''' await db.execute('''
CREATE TABLE IF NOT EXISTS ${TableName.driverOrdersRefuse}( CREATE TABLE IF NOT EXISTS ${TableName.driverOrdersRefuse}(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -97,6 +100,34 @@ class DbSql {
return await db.insert(table, map); return await db.insert(table, map);
} }
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
);
} else {
// If record doesn't exist, insert new record with the current timestamp
map['createdAt'] = DateTime.now().toIso8601String();
return await db.insert(table, map);
}
}
Future<int> updateData(Map<String, dynamic> map, String table, int id) async { Future<int> updateData(Map<String, dynamic> map, String table, int id) async {
Database db = await instance.database; Database db = await instance.database;

View File

@@ -0,0 +1,276 @@
import 'dart:math' as math;
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:SEFER/splash_screen_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class CouponPainter extends CustomPainter {
final Color primaryColor;
final Color secondaryColor;
CouponPainter({
required this.primaryColor,
required this.secondaryColor,
});
@override
void paint(Canvas canvas, Size size) {
final Paint primaryPaint = Paint()
..color = primaryColor
..style = PaintingStyle.fill; //
final Paint secondaryPaint = Paint()
..color = secondaryColor
..style = PaintingStyle.fill;
final Path path = Path();
// Draw the main ticket shape
path.moveTo(0, size.height * 0.1);
path.lineTo(size.width * 0.93, size.height * 0.1);
path.arcToPoint(
Offset(size.width, size.height * 0.2),
radius: const Radius.circular(20),
clockwise: false,
);
path.lineTo(size.width, size.height * 0.8);
path.arcToPoint(
Offset(size.width * 0.93, size.height * 0.9),
radius: const Radius.circular(20),
clockwise: false,
);
path.lineTo(0, size.height * 0.9);
path.close();
canvas.drawPath(path, primaryPaint);
// Draw decorative circles on the left side
for (int i = 0; i < 5; i++) {
canvas.drawCircle(
Offset(0, size.height * (0.2 + i * 0.15)),
10,
secondaryPaint,
);
}
// Draw a wavy pattern on the right side
final wavePaint = Paint()
..color = secondaryColor.withOpacity(0.3)
..style = PaintingStyle.stroke
..strokeWidth = 2;
for (int i = 0; i < 20; i++) {
canvas.drawLine(
Offset(size.width * 0.8, i * 10.0),
Offset(
size.width,
i * 10.0 + math.sin(i * 0.5) * 10,
),
wavePaint,
);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class PromoBanner extends StatelessWidget {
final String promoCode;
final String discountPercentage;
final String validity;
const PromoBanner({
Key? key,
required this.promoCode,
required this.discountPercentage,
required this.validity,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CouponPainter(
primaryColor: Colors.blue[800]!,
secondaryColor: Colors.white,
),
child: Container(
width: 320,
height: 240,
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const SizedBox(
height: 10,
),
Text(
'Enter the promo code and get'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
textAlign: TextAlign.center,
),
Text(
'${'DISCOUNT'.tr} $discountPercentage ${'for'.tr} $validity'
.toUpperCase(),
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Text(
promoCode,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue[800],
),
),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
// Copy promo code to clipboard
Clipboard.setData(ClipboardData(text: promoCode));
// Show a Snackbar or other feedback to the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Promo code copied to clipboard!'.tr)),
);
box.write(BoxName.isGiftToken, '1');
box.write(BoxName.isFirstTime, '1');
Get.back();
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.blue[800], // Customize the color
backgroundColor: Colors.white, // Customize the background color
),
child: Text('Copy Code'.tr),
)
],
),
),
);
}
}
// import 'package:SEFER/constant/colors.dart';
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// class CouponPainter extends CustomPainter {
// @override
// void paint(Canvas canvas, Size size) {
// final Paint paint = Paint()
// ..color = AppColor.blueColor
// ..style = PaintingStyle.fill;
// final Path path = Path();
// // Draw the ticket shape (like the image)
// path.moveTo(0, 0);
// path.lineTo(size.width * 0.7, 0); // top left to top right edge
// // Draw curve for the cut on the right side (ticket look)
// path.arcToPoint(Offset(size.width, size.height * 0.15),
// radius: const Radius.circular(15), clockwise: false);
// path.lineTo(size.width, size.height * 0.85);
// path.arcToPoint(Offset(size.width * 0.7, size.height),
// radius: const Radius.circular(15), clockwise: false);
// path.lineTo(0, size.height);
// canvas.drawPath(path, paint);
// }
// @override
// bool shouldRepaint(CustomPainter oldDelegate) {
// return false;
// }
// }
// class PromoBanner extends StatelessWidget {
// final String promoCode;
// final String discountPercentage;
// final String validity;
// const PromoBanner({
// required this.promoCode,
// required this.discountPercentage,
// required this.validity,
// });
// @override
// Widget build(BuildContext context) {
// return CustomPaint(
// painter: CouponPainter(),
// child: Container(
// width: 300, // Fixed width for the promo banner
// height: 180, // Set the desired height for your banner
// padding: const EdgeInsets.all(16),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// Text(
// 'Enter the promo code and get'.toUpperCase(),
// style: const TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: Colors.white,
// ),
// textAlign: TextAlign.center,
// ),
// Text(
// '$discountPercentage OFF for $validity'.toUpperCase(),
// style: const TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// color: Colors.white,
// ),
// textAlign: TextAlign.center,
// ),
// const SizedBox(height: 10),
// Container(
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(10),
// ),
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// child: Text(
// promoCode,
// style: TextStyle(
// fontSize: 20,
// fontWeight: FontWeight.bold,
// color: Colors.blue[800],
// ),
// ),
// ),
// ],
// ),
// ),
// );
// }
// }

View File

@@ -40,7 +40,7 @@ class RateDriverFromPassenger extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Text( Text(
'${'Total price to '.tr}${Get.find<MapPassengerController>().firstName}', '${'Total price to '.tr}${Get.find<MapPassengerController>().driverName}',
style: AppStyle.title, style: AppStyle.title,
), ),
Row( Row(

View File

@@ -12,11 +12,13 @@ import 'package:SEFER/constant/style.dart';
import 'package:SEFER/main.dart'; import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:SEFER/views/widgets/my_scafold.dart'; import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../constant/info.dart'; import '../../constant/info.dart';
import '../../controller/auth/apple_signin_controller.dart'; import '../../controller/auth/apple_signin_controller.dart';
import '../../controller/auth/google_sign.dart'; import '../../controller/auth/google_sign.dart';
import '../../controller/auth/login_controller.dart'; import '../../controller/auth/login_controller.dart';
import '../home/HomePage/contact_us.dart';
import '../home/profile/passenger_profile_page.dart'; import '../home/profile/passenger_profile_page.dart';
import '../widgets/mycircular.dart'; import '../widgets/mycircular.dart';
@@ -218,7 +220,7 @@ class LoginPage extends StatelessWidget {
// ), // ),
InkWell( InkWell(
onTap: () async { onTap: () async {
await GoogleSignInHelper.signInFromLogin(); await GoogleSignInHelper().signInFromLogin();
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@@ -303,6 +305,14 @@ class LoginPage extends StatelessWidget {
SizedBox( SizedBox(
height: Get.height * .1, height: Get.height * .1,
), ),
GestureDetector(
onTap: () => Get.to(() => ContactUsPage()),
child: Text(
'If you need assistance, contact us'
.tr, // Improved wording
style: AppStyle.subtitle,
),
),
// Text( // Text(
// 'if you dont have account'.tr, // 'if you dont have account'.tr,
// style: AppStyle.subtitle, // style: AppStyle.subtitle,
@@ -447,6 +457,16 @@ class LoginPage extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: AppStyle.title, style: AppStyle.title,
), ),
TextButton(
onPressed: () {
// Optionally, navigate to app settings for manual permission control
openAppSettings();
},
child: Text(
"Open Settings".tr,
style: const TextStyle(color: AppColor.blueColor),
),
),
MyElevatedButton( MyElevatedButton(
title: 'Next'.tr, title: 'Next'.tr,
onPressed: () async { onPressed: () async {

View File

@@ -1,11 +1,17 @@
import 'package:SEFER/constant/style.dart'; import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/auth/register_controller.dart'; import 'package:SEFER/controller/auth/register_controller.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:SEFER/views/widgets/my_scafold.dart'; import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:SEFER/views/widgets/my_textField.dart'; import 'package:SEFER/views/widgets/my_textField.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../controller/local/phone_intel/intl_phone_field.dart';
import '../../print.dart';
import '../widgets/mycircular.dart';
// import 'package:intl_phone_field/intl_phone_field.dart';
class SmsSignupEgypt extends StatelessWidget { class SmsSignupEgypt extends StatelessWidget {
SmsSignupEgypt({super.key}); SmsSignupEgypt({super.key});
@@ -13,7 +19,7 @@ class SmsSignupEgypt extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(RegisterController()); Get.put(RegisterController());
return MyScafolld( return MyScafolld(
title: 'Phone Check'.tr, title: "Phone Number Check".tr,
body: [ body: [
GetBuilder<RegisterController>(builder: (registerController) { GetBuilder<RegisterController>(builder: (registerController) {
return ListView( return ListView(
@@ -37,62 +43,43 @@ class SmsSignupEgypt extends StatelessWidget {
style: AppStyle.title, style: AppStyle.title,
), ),
), ),
// Enter phone number text
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Text(
'Enter your phone number'.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
),
// Phone number input field with country code dropdown // Phone number input field with country code dropdown
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: !registerController.isSent child: IntlPhoneField(
? Form( decoration: InputDecoration(
key: registerController.formKey3, labelText: 'Phone Number'.tr,
child: Row( border: const OutlineInputBorder(
children: [ borderSide: BorderSide(),
// Country Code Dropdown ),
DropdownButton<String>( ),
value: registerController.selectedCountryCode, initialCountryCode: 'EG',
items: registerController.countryCodes onChanged: (phone) {
.map((String code) { // Properly concatenate country code and number
return DropdownMenuItem<String>( registerController.phoneController.text =
value: code, phone.completeNumber.toString();
child: Text(code), Log.print(' phone.number: ${phone.number}');
); print(
}).toList(), "Formatted phone number: ${registerController.phoneController.text}");
onChanged: (String? newValue) { },
registerController.updateCountryCode(newValue!); validator: (phone) {
}, // Check if the phone number is not null and is valid
), if (phone == null || phone.completeNumber.isEmpty) {
// Spacer return 'Please enter your phone number';
const SizedBox(width: 10), }
// Phone Number Input Field
Expanded( // Extract the phone number (excluding the country code)
child: MyTextForm( final number = phone.completeNumber.toString();
controller:
registerController.phoneController, // Check if the number length is exactly 11 digits
label: 'Enter your phone number'.tr, if (number.length != 13) {
hint: 'Enter your phone number'.tr, return 'Phone number must be exactly 11 digits long';
type: TextInputType.phone), }
),
], // If all validations pass, return null
), return null;
) },
: Container( ),
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
registerController.phoneController.text,
style: AppStyle.title,
),
),
),
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
@@ -110,14 +97,19 @@ class SmsSignupEgypt extends StatelessWidget {
), ),
), ),
// Submit button // Submit button
MyElevatedButton( registerController.isLoading
onPressed: () async { ? const MyCircularProgressIndicator()
!registerController.isSent : Padding(
? await registerController.sendOtpMessage() padding: const EdgeInsets.all(16.0),
: await registerController.verifySMSCode(); child: MyElevatedButton(
}, onPressed: () async {
title: 'Submit'.tr, !registerController.isSent
), ? await registerController.sendOtpMessage()
: await registerController.verifySMSCode();
},
title: 'Submit'.tr,
),
),
], ],
); );
}), }),

View File

@@ -1,101 +1,153 @@
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/main.dart'; import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/cupertino.dart';
class AboutPage extends StatelessWidget { class AboutPage extends StatelessWidget {
const AboutPage({super.key}); const AboutPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MyScafolld( return CupertinoPageScaffold(
title: 'About Us'.tr, navigationBar: CupertinoNavigationBar(
body: [ middle: Text('About Us'.tr),
// Company Logo (consider adding an image asset) ),
ListView( child: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Company Logo
Center( Center(
child: Image.asset( child: Padding(
'assets/images/logo.png', // Replace with your logo image asset path padding: const EdgeInsets.all(16.0),
height: 100.0, child: Image.asset(
width: 100.0, 'assets/images/logo.png', // Replace with your logo image asset path
height: 100.0,
width: 100.0,
),
), ),
), // Company Name and Location ),
// Company Name and Location
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(
'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}', 'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}',
style: AppStyle.headTitle2, style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
// About Us Description
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text( child: Text(
'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:' 'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
.tr, .tr,
style: AppStyle.title, style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16.0,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), // Security Features List
const SizedBox(
height: 20,
), ),
const SizedBox(height: 20),
// Security Features
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column( child: Column(
children: [ children: [
Row( Row(
children: [ children: [
const Icon(Icons.lock, color: Colors.blue), const Icon(CupertinoIcons.lock_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Expanded(
'Most Secure Methods'.tr, child: Text(
style: AppStyle.title, 'Most Secure Methods'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
), ),
], ],
), ),
const SizedBox(height: 8.0), const SizedBox(height: 8.0),
Row( Row(
children: [ children: [
const Icon(Icons.phone, color: Colors.blue), const Icon(CupertinoIcons.phone_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Expanded(
'In-App VOIP Calls'.tr, child: Text(
style: AppStyle.title, 'In-App VOIP Calls'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
), ),
], ],
), ),
const SizedBox(height: 8.0), const SizedBox(height: 8.0),
Row( Row(
children: [ children: [
const Icon(Icons.videocam, color: Colors.blue), const Icon(CupertinoIcons.videocam_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Expanded(
'Recorded Trips for Safety'.tr, child: Text(
style: AppStyle.title, 'Recorded Trips for Safety'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
), ),
], ],
), ),
], ],
), ),
), // Affordability Highlight ),
// Affordability Highlight
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text( child: Text(
'\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.' '\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.'
.tr, .tr,
style: AppStyle.title, style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
const SizedBox(height: 20),
], ],
), ),
),
// About Us Text ),
], );
isleading: true);
} }
} }

View File

@@ -0,0 +1,102 @@
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart';
// ignore: unused_import
import 'package:SEFER/controller/functions/launch.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/contact_us_controller.dart';
class ContactUsPage extends StatelessWidget {
ContactUsPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(ContactUsController());
return GetBuilder<ContactUsController>(builder: (controller) {
return MyScafolld(
title: "Contact Us".tr,
body: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: AppStyle.boxDecoration1,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image.asset('assets/images/logo.gif')),
IconButton(
onPressed: () async {
Get.put(TextToSpeechController()).speakText(
'SEFER is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.'
.tr);
},
icon: const Icon(Icons.headphones),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'SEFER is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
],
),
),
const SizedBox(
height: 30,
),
Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"You can contact us during working hours from 12:00 - 19:00."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
),
InkWell(
onTap: () => controller.showContactDialog(context),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
Icons.phone,
color: AppColor.blueColor,
),
Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
Icon(
Icons.email,
color: AppColor.redColor,
),
],
),
),
const SizedBox(
height: 30,
)
],
),
)
],
isleading: true);
});
}
}

View File

@@ -0,0 +1,572 @@
import 'package:SEFER/constant/style.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/profile/invit_controller.dart';
import '../../../main.dart';
import '../../../print.dart';
class ShareAppPage extends StatelessWidget {
final InviteController controller = Get.put(InviteController());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.systemBackground,
appBar: AppBar(
backgroundColor: CupertinoColors.systemBackground,
elevation: 0,
title: Text(
'Invite'.tr,
style: const TextStyle(color: CupertinoColors.label),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColor.blueColor),
onPressed: () => Get.back(),
),
),
body: SafeArea(
child: GetBuilder<InviteController>(
builder: (controller) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: _buildPassengerTab(context),
),
),
],
);
},
),
),
);
}
Widget _buildPassengerTab(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
"Share this code with your friends and earn rewards when they use it!"
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 13,
),
),
],
),
),
const SizedBox(height: 20),
_buildPhoneInput(),
const SizedBox(height: 20),
_buildActionButtonsPassengers(),
const SizedBox(height: 20),
const SizedBox(height: 20),
_buildInvitationsListPassengers(context),
],
);
}
// Widget _buildPhoneInput() {
// return Container(
// decoration: BoxDecoration(
// color: CupertinoColors.systemGrey6,
// borderRadius: BorderRadius.circular(8),
// ),
// child: Row(
// children: [
// Expanded(
// child: CupertinoTextField.borderless(
// controller: controller.invitePhoneController,
// placeholder: 'Enter phone'.tr,
// padding: const EdgeInsets.all(12),
// keyboardType: TextInputType.phone,
// ),
// ),
// CupertinoButton(
// child: const Icon(CupertinoIcons.person_badge_plus,
// color: AppColor.blueColor),
// onPressed: () async {
// await controller.pickContacts();
// if (controller.contacts.isNotEmpty) {
// if (box.read(BoxName.isSavedPhones) == null) {
// controller.savePhoneToServer();
// box.write(BoxName.isSavedPhones, true);
// }
// _showContactsDialog(Get.context!);
// }
// },
// ),
// ],
// ),
// );
// }
Widget _buildPhoneInput() {
return Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
child: CupertinoTextField.borderless(
controller: controller.invitePhoneController,
placeholder: 'Enter phone'.tr,
padding: const EdgeInsets.all(12),
keyboardType: TextInputType.phone,
),
),
CupertinoButton(
child: const Icon(CupertinoIcons.person_badge_plus,
color: AppColor.blueColor),
onPressed: () async {
await controller.pickContacts();
Log.print('contacts: ${controller.contacts}');
if (controller.contacts.isNotEmpty) {
_showContactsDialog(Get
.context!); // Show contacts dialog after loading contacts
} else {
Get.snackbar(
'No contacts available'.tr,
'Please add contacts to your phone.'.tr,
);
}
},
),
],
),
);
}
Widget _buildActionButtonsPassengers() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInviteToPassenger,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
onPressed: () async {
controller.fetchDriverStatsPassengers();
},
),
),
),
],
),
);
}
Widget _buildInvitationsListPassengers(BuildContext context) {
return SizedBox(
height: Get.height * .4,
child: controller.driverInvitationDataToPassengers.isEmpty
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 17,
),
),
)
: ListView.builder(
itemCount: controller.driverInvitationDataToPassengers.length,
itemBuilder: (context, index) {
return _buildInvitationItemPassengers(context, index);
},
),
);
}
Widget _buildInvitationItemPassengers(BuildContext context, int index) {
// Extracting the data from the sample JSON-like structure
var invitation = controller.driverInvitationDataToPassengers[index];
int countOfInvitDriver =
int.tryParse(invitation['countOfInvitDriver']?.toString() ?? '0') ?? 0;
double progressValue = (countOfInvitDriver / 10.0).clamp(0.0, 1.0);
return GestureDetector(
onTap: () {
controller.onSelectPassengerInvitation(index);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
invitation['passengerName']
.toString(), // Handle null or missing data
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 2 ${'Trip'.tr}', // Show trips completed
style: const TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
],
),
),
);
}
Widget _buildPassengerStats(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Your Rewards".tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 16),
_buildStatItem(
context,
"Total Invites".tr,
controller.driverInvitationDataToPassengers[0]['countOfInvitDriver']
.toString(),
),
_buildStatItem(
context,
"Active Users".tr,
controller.driverInvitationDataToPassengers[0]['passengerName']
.toString(),
),
],
),
);
}
Widget _buildStatItem(BuildContext context, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 15,
),
),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: AppColor.blueColor,
fontSize: 15,
),
),
],
),
);
}
void _showContactsDialog(BuildContext context) {
Get.defaultDialog(
title: 'Choose from contact'.tr,
content: SizedBox(
height: 400,
width: 400,
child: Column(
children: [
// Header with cancel and title
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
// color: CupertinoColors.systemGrey6,
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// CupertinoButton(
// padding: EdgeInsets.zero,
// child: Text(
// 'Cancel'.tr,
// style: const TextStyle(color: CupertinoColors.systemBlue),
// ),
// onPressed: () => Navigator.pop(context),
// ),
// Container(
// child: Text('Choose from contact'.tr,
// style: AppStyle.title)),
// const SizedBox(width: 60), // Balance for Cancel button
// ],
// ),
// ),
// Contact list
Expanded(
child: ListView.builder(
itemCount: controller.contactMaps.length,
itemBuilder: (context, index) {
final contact = controller.contactMaps[index];
return CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () {
controller.selectPhone(contact['phones'].toString());
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5),
),
),
),
child: Row(
children: [
// Display contact name and phone number
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact['name'],
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
Text(
controller.formatPhoneNumber(
contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 15,
),
),
],
),
),
// Chevron icon for selection
const Icon(
CupertinoIcons.chevron_forward,
color: CupertinoColors.systemGrey2,
),
],
),
),
);
},
),
),
],
),
),
);
// showCupertinoModalPopup(
// context: context,
// builder: (BuildContext context) => Container(
// height: 400,
// decoration: BoxDecoration(
// color: CupertinoColors.systemBackground,
// borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
// boxShadow: [
// BoxShadow(
// color: CupertinoColors.black.withOpacity(0.2),
// offset: const Offset(0, -4),
// blurRadius: 10,
// ),
// ],
// ),
// child: Column(
// children: [
// // Header with cancel and title
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
// color: CupertinoColors.systemGrey6,
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// CupertinoButton(
// padding: EdgeInsets.zero,
// child: Text(
// 'Cancel'.tr,
// style: const TextStyle(color: CupertinoColors.systemBlue),
// ),
// onPressed: () => Navigator.pop(context),
// ),
// Container(
// child: Text('Choose from contact'.tr,
// style: AppStyle.title)),
// const SizedBox(width: 60), // Balance for Cancel button
// ],
// ),
// ),
// // Contact list
// Expanded(
// child: ListView.builder(
// itemCount: controller.contactMaps.length,
// itemBuilder: (context, index) {
// final contact = controller.contactMaps[index];
// return CupertinoButton(
// padding: EdgeInsets.zero,
// onPressed: () {
// controller.selectPhone(contact['phones'].toString());
// },
// child: Container(
// padding: const EdgeInsets.symmetric(
// horizontal: 16, vertical: 12),
// decoration: BoxDecoration(
// color: CupertinoColors.systemBackground,
// border: Border(
// bottom: BorderSide(
// color: CupertinoColors.separator.withOpacity(0.5),
// ),
// ),
// ),
// child: Row(
// children: [
// // Display contact name and phone number
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// contact['name'],
// style: const TextStyle(
// color: CupertinoColors.label,
// fontSize: 17,
// fontWeight: FontWeight.w500,
// ),
// ),
// Text(
// controller.formatPhoneNumber(
// contact['phones'][0].toString()),
// style: const TextStyle(
// color: CupertinoColors.secondaryLabel,
// fontSize: 15,
// ),
// ),
// ],
// ),
// ),
// // Chevron icon for selection
// const Icon(
// CupertinoIcons.chevron_forward,
// color: CupertinoColors.systemGrey2,
// ),
// ],
// ),
// ),
// );
// },
// ),
// ),
// ],
// ),
// ),
// );
}
}

View File

@@ -1,14 +1,11 @@
import 'package:SEFER/views/widgets/my_scafold.dart'; import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:share/share.dart'; import 'package:share/share.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart'; import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/tts.dart';
import '../../widgets/elevated_btn.dart';
class TripsRecordedPage extends StatelessWidget { class TripsRecordedPage extends StatelessWidget {
const TripsRecordedPage({ const TripsRecordedPage({
@@ -21,132 +18,193 @@ class TripsRecordedPage extends StatelessWidget {
title: 'Trips recorded'.tr, title: 'Trips recorded'.tr,
body: [ body: [
GetBuilder<AudioRecorderController>(builder: (audio) { GetBuilder<AudioRecorderController>(builder: (audio) {
return Column( return SingleChildScrollView(
children: [ child: Column(
FutureBuilder<List<String>>( crossAxisAlignment: CrossAxisAlignment.stretch,
future: audio.getRecordedFiles(), children: [
builder: (context, snapshot) { FutureBuilder<List<String>>(
if (snapshot.connectionState == ConnectionState.waiting) { future: audio.getRecordedFiles(),
return const CircularProgressIndicator(); builder: (context, snapshot) {
} else if (snapshot.hasData) { if (snapshot.connectionState == ConnectionState.waiting) {
final recordedFiles = snapshot.data!; return const Center(
return DropdownButton<String>( child: CupertinoActivityIndicator());
value: audio.selectedFilePath, } else if (snapshot.hasData) {
onChanged: (value) { final recordedFiles = snapshot.data!;
audio.selectedFilePath = value; return Padding(
audio.playRecordedFile(value!); padding: const EdgeInsets.symmetric(horizontal: 16.0),
audio.update(); child: CupertinoButton(
}, padding: EdgeInsets.zero,
items: recordedFiles
.map((file) => DropdownMenuItem<String>(
value: file,
child: Text(path.basename(file)),
))
.toList(),
);
} else {
return Text('Error: ${snapshot.error}');
}
},
),
Slider(
value: audio.currentPosition,
max: audio.totalDuration,
inactiveColor: AppColor.accentColor,
label: audio.currentPosition.toString(),
onChanged: (value) {
audio.currentPosition = value;
audio.audioPlayer.seek(Duration(seconds: value.toInt()));
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(
audio.isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: () {
if (audio.isPlaying) {
audio.pausePlayback();
} else {
audio.resumePlayback();
}
audio.update();
},
),
IconButton(
icon: const Icon(Icons.stop),
onPressed: () {
audio.stopPlayback();
audio.update();
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
Get.defaultDialog(
title: 'Are you sure to delete recorded files'.tr,
content: Column(
children: [
IconButton(
onPressed: () {
Get.find<TextToSpeechController>().speakText(
'this will delete all files from your device'
.tr);
},
icon: const Icon(Icons.headphones),
),
Text(
'this will delete all files from your device'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
],
),
titleStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'Delete'.tr,
kolor: AppColor.redColor,
onPressed: () async { onPressed: () async {
await audio.deleteAllRecordedFiles(); String? selectedFile =
Get.back(); await showCupertinoModalPopup<String>(
Get.back(); context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Select a File'.tr),
actions: recordedFiles
.map(
(file) => CupertinoActionSheetAction(
child: Text(path.basename(file)),
onPressed: () {
Navigator.of(context).pop(file);
},
),
)
.toList(),
);
},
);
if (selectedFile != null) {
audio.selectedFilePath = selectedFile;
audio.playRecordedFile(selectedFile);
audio.update();
}
}, },
child: Text(
audio.selectedFilePath != null
? path.basename(audio.selectedFilePath!)
: 'Select a File'.tr,
style: CupertinoTheme.of(context)
.textTheme
.actionTextStyle
.copyWith(color: CupertinoColors.activeBlue),
),
), ),
); );
} else {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text('Error: ${snapshot.error}'),
);
}
},
),
// Cupertino-style slider for seeking audio
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CupertinoSlider(
value: audio.totalDuration > 0
? audio.currentPosition / audio.totalDuration
: 0.0, // Normalize to a value between 0.0 and 1.0
min: 0.0,
max: 1.0, // Maximum value is now 1.0
activeColor: CupertinoColors.activeBlue,
onChanged: (value) {
final newPosition = value * audio.totalDuration;
audio.currentPosition = newPosition;
audio.audioPlayer
.seek(Duration(seconds: newPosition.toInt()));
audio.update();
}, },
), ),
], ),
),
Align( // iOS-style playback controls
alignment: Alignment.bottomCenter, Padding(
child: Container( padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.all(16.0), vertical: 16.0, horizontal: 16.0),
color: Colors.grey[200],
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Text( CupertinoButton(
audio.selectedFilePath != null padding: EdgeInsets.zero,
? '${'Selected file:'.tr} ${path.basename(audio.selectedFilePath!)}' child: Icon(
: 'No file selected'.tr, audio.isPlaying
style: AppStyle.subtitle, ? CupertinoIcons.pause
), : CupertinoIcons.play_arrow,
if (audio.selectedFilePath != null) color: CupertinoColors.activeBlue,
IconButton(
icon: const Icon(Icons.share),
onPressed: () {
Share.shareFiles([audio.selectedFilePath!]);
},
), ),
onPressed: () {
if (audio.isPlaying) {
audio.pausePlayback();
} else {
audio.resumePlayback();
}
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.stop,
color: CupertinoColors.destructiveRed),
onPressed: () {
audio.stopPlayback();
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.delete,
color: CupertinoColors.destructiveRed),
onPressed: () async {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Are you sure?'.tr),
message: Text(
'This will delete all recorded files from your device.'
.tr,
textAlign: TextAlign.center,
),
actions: [
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
await audio.deleteAllRecordedFiles();
Navigator.pop(context);
audio.update();
},
child: Text('Delete'.tr),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel'.tr),
),
);
},
);
},
),
], ],
), ),
), ),
),
], // File selection and sharing
if (audio.selectedFilePath != null)
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(16.0),
color: CupertinoColors.systemGrey6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Selected file: ${path.basename(audio.selectedFilePath!)}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.share),
onPressed: () {
Share.shareFiles([audio.selectedFilePath!]);
},
),
],
),
),
),
],
),
); );
}), })
], ],
isleading: true); isleading: true);
} }

View File

@@ -2,17 +2,11 @@ import 'package:SEFER/controller/home/home_page_controller.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/views/lang/languages.dart'; import 'package:SEFER/views/lang/languages.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import '../../constant/box_name.dart';
import '../../controller/profile/profile_controller.dart';
import '../../main.dart';
import '../widgets/elevated_btn.dart';
import 'HomePage/about_page.dart'; import 'HomePage/about_page.dart';
import 'HomePage/frequentlyQuestionsPage.dart'; import 'HomePage/frequentlyQuestionsPage.dart';
import 'HomePage/share_app_page.dart';
import 'HomePage/trip_record_page.dart'; import 'HomePage/trip_record_page.dart';
import 'profile/passenger_profile_page.dart'; import 'profile/passenger_profile_page.dart';
@@ -22,104 +16,108 @@ class HomePage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(HomePageController()); Get.put(HomePageController());
final List<String> countryOptions = [ return CupertinoPageScaffold(
'Jordan', navigationBar: CupertinoNavigationBar(
'USA', middle: Text('Home Page'.tr),
'Egypt', leading: CupertinoButton(
'Turkey', padding: EdgeInsets.zero,
'Saudi Arabia', child: const Icon(CupertinoIcons.back),
'Qatar', onPressed: () {
'Bahrain', Navigator.pop(context);
'Kuwait', },
]; ),
return MyScafolld( ),
isleading: true, child: SafeArea(
title: 'Home Page'.tr, child: ListView(
body: [
Column(
children: [ children: [
ListTile( CupertinoListTile(
onTap: () { onTap: () {
Get.to(() => const Language()); Get.to(() => const Language());
}, },
title: Text( leading: const Icon(CupertinoIcons.globe,
'Language'.tr, color: CupertinoColors.activeBlue),
style: AppStyle.headTitle2, title: Text('Language'.tr),
), subtitle: Text('To change Language the App'.tr),
subtitle: Text( trailing: const CupertinoListTileChevron(),
'To change Language the App'.tr,
style: AppStyle.title,
),
trailing: const Icon(
Icons.arrow_forward_ios,
size: 30,
color: AppColor.primaryColor,
),
leading: const Icon(
Icons.language_sharp,
color: AppColor.primaryColor,
),
), ),
changeCountry(countryOptions), CupertinoListTile(
ListTile( onTap: () {
leading: const Icon(Icons.question_answer), Get.to(CupertinoPageScaffold(
title: Text( navigationBar: CupertinoNavigationBar(
'Frequently Questions'.tr, middle: Text('Change Country'.tr),
style: AppStyle.headTitle2,
),
subtitle: Text(
'You can change the Country to get all features'.tr,
style: AppStyle.title,
),
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
),
ListTile(
leading: const Icon(Icons.vibration),
title: GetBuilder<HomePageController>(builder: (controller) {
return SwitchListTile(
title: Text(
'Vibration'.tr,
style: AppStyle.headTitle2,
), ),
value: controller.isVibrate, child: SafeArea(
onChanged: controller.changeVibrateOption, child: CountryPickerFromSetting(),
activeColor: AppColor.primaryColor, ),
); ));
}), },
subtitle: Text( leading: const Icon(CupertinoIcons.location,
"You can change the vibration feedback for all buttons".tr, color: CupertinoColors.activeBlue),
style: AppStyle.title, title: Text('Change Country'.tr),
), subtitle:
onTap: () => Get.to(() => const FrequentlyQuestionsPage()), Text('You can change the Country to get all features'.tr),
trailing: const CupertinoListTileChevron(),
), ),
ListTile( CupertinoListTile(
leading: const Icon(Icons.record_voice_over_outlined), onTap: () {
title: Text( Get.to(() => const FrequentlyQuestionsPage());
'Trips recorded'.tr, },
style: AppStyle.headTitle2, leading: const Icon(CupertinoIcons.question,
), color: CupertinoColors.activeBlue),
subtitle: Text( title: Text('Frequently Questions'.tr),
'Here recorded trips audio'.tr, subtitle: Text('Find answers to common questions'.tr),
style: AppStyle.title, trailing: const CupertinoListTileChevron(),
), ),
onTap: () async { CupertinoListTile(
Get.to(() => TripsRecordedPage()); leading: const Icon(Icons.vibration,
}), color: CupertinoColors.activeBlue),
ListTile( title: Text('Vibration'.tr),
leading: const Icon(Icons.account_balance_outlined), trailing: GetBuilder<HomePageController>(
title: Text( builder: (controller) {
'About Us'.tr, return CupertinoSwitch(
style: AppStyle.headTitle2, value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
);
},
), ),
subtitle: Text( subtitle: Text(
'You can change the Country to get all features'.tr, 'You can change the vibration feedback for all buttons'.tr),
style: AppStyle.title, ),
), CupertinoListTile(
onTap: () => Get.to(() => const AboutPage()), onTap: () {
Get.to(() => const TripsRecordedPage());
},
leading: const Icon(CupertinoIcons.mic_circle,
color: CupertinoColors.activeBlue),
title: Text('Trips recorded'.tr),
subtitle: Text('Here recorded trips audio'.tr),
trailing: const CupertinoListTileChevron(),
),
CupertinoListTile(
onTap: () {
Get.to(() => const AboutPage());
},
leading: const Icon(CupertinoIcons.info_circle,
color: CupertinoColors.activeBlue),
title: Text('About Us'.tr),
subtitle: Text('Learn more about our app and mission'.tr),
trailing: const CupertinoListTileChevron(),
),
CupertinoListTile(
onTap: () {
Get.to(() => ShareAppPage());
},
leading: const Icon(CupertinoIcons.share,
color: CupertinoColors.activeBlue),
title: Text('Share App'.tr),
subtitle: Text(
'You can share the SEFER App with your friends and earn rewards for rides they take using your code'
.tr),
trailing: const CupertinoListTileChevron(),
), ),
], ],
), ),
], ),
); );
} }
} }

View File

@@ -23,6 +23,7 @@ import 'map_widget.dart/payment_method.page.dart';
import 'map_widget.dart/points_page_for_rider.dart'; import 'map_widget.dart/points_page_for_rider.dart';
import 'map_widget.dart/ride_from_start_app.dart'; import 'map_widget.dart/ride_from_start_app.dart';
import 'map_widget.dart/searching_captain_window.dart'; import 'map_widget.dart/searching_captain_window.dart';
import 'map_widget.dart/vip_begin.dart';
class MapPagePassenger extends StatelessWidget { class MapPagePassenger extends StatelessWidget {
const MapPagePassenger({super.key}); const MapPagePassenger({super.key});
@@ -51,6 +52,7 @@ class MapPagePassenger extends StatelessWidget {
CarDetailsTypeToChoose(), CarDetailsTypeToChoose(),
const HeaderDestination(), const HeaderDestination(),
const BurcMoney(), const BurcMoney(),
const PromoCode(),
const ApplyOrderWidget(), const MapMenuWidget(), const ApplyOrderWidget(), const MapMenuWidget(),
// hexagonClipper(), // hexagonClipper(),
const CancelRidePageShow(), const CancelRidePageShow(),
@@ -62,7 +64,9 @@ class MapPagePassenger extends StatelessWidget {
// const TimerToPassengerFromDriver(), // const TimerToPassengerFromDriver(),
const PassengerRideLocationWidget(), const PassengerRideLocationWidget(),
const RideBeginPassenger(), const RideBeginPassenger(),
const VipRideBeginPassenger(),
const RideFromStartApp(), const RideFromStartApp(),
cancelRidePage(), cancelRidePage(),
const MenuIconMapPageWidget(), const MenuIconMapPageWidget(),
PointsPageForRider() PointsPageForRider()
@@ -82,7 +86,7 @@ class CancelRidePageShow extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>( return GetBuilder<MapPassengerController>(
builder: (controller) => builder: (controller) =>
(controller.data.isNotEmpty && controller.remainingTime > 0) (controller.data.isNotEmpty && controller.statusRide != 'Begin')
// || // ||
// controller.timeToPassengerFromDriverAfterApplied == 0 // controller.timeToPassengerFromDriverAfterApplied == 0
? Positioned( ? Positioned(

View File

@@ -17,6 +17,21 @@ class ApplyOrderWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Color _parseColor(String colorHex) {
if (colorHex.isEmpty) {
return Colors.grey; // Fallback for empty color
}
// Ensure the string starts with '0xff' for ARGB format
String processedHex = colorHex.replaceFirst('#', '0xff').trim();
if (!processedHex.startsWith('0xff')) {
processedHex = '0xff$processedHex'; // Add '0xff' if missing
}
return Color(int.parse(processedHex));
}
return GetBuilder<MapPassengerController>(builder: (controller) { return GetBuilder<MapPassengerController>(builder: (controller) {
if (controller.statusRide == 'Apply' && if (controller.statusRide == 'Apply' &&
controller.isSearchingWindow == false) { controller.isSearchingWindow == false) {
@@ -27,12 +42,13 @@ class ApplyOrderWidget extends StatelessWidget {
right: 0, right: 0,
child: Container( child: Container(
decoration: AppStyle.boxDecoration, decoration: AppStyle.boxDecoration,
height: Get.height * .35, height: Get.height * .36,
child: ListView( child: ListView(
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
if (box.read(BoxName.carType) == 'Speed' || if (box.read(BoxName.carType) == 'Speed' ||
box.read(BoxName.carType) == 'Awfar Car' ||
box.read(BoxName.carType) == 'Delivery') { box.read(BoxName.carType) == 'Delivery') {
Get.snackbar( Get.snackbar(
'This price is'.tr + 'This price is'.tr +
@@ -86,7 +102,7 @@ class ApplyOrderWidget extends StatelessWidget {
width: 10, width: 10,
), ),
Container( Container(
height: Get.height * .3, height: Get.height * .31,
width: Get.width * .9, width: Get.width * .9,
decoration: AppStyle.boxDecoration, decoration: AppStyle.boxDecoration,
child: Column( child: Column(
@@ -107,28 +123,43 @@ class ApplyOrderWidget extends StatelessWidget {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Image.asset( // ColorFiltered(
box.read(BoxName.carType) == 'Comfort' // colorFilter: ColorFilter.mode(
? 'assets/images/blob.png' // _parseColor(controller.colorHex),
: box.read(BoxName.carType) == 'Lady' // BlendMode.srcIn,
? 'assets/images/lady.png' // Assuming there's an image for Lady // ),
: box.read(BoxName.carType) == 'Speed' // child: Image.asset(
? 'assets/images/carspeed.png' // box.read(BoxName.carType) == 'Comfort'
: box.read(BoxName.carType) == // ? 'assets/images/blob.png'
'Delivery' // : box.read(BoxName.carType) == 'Lady'
? 'assets/images/moto.png' // ? 'assets/images/lady.png' // Assuming there's an image for Lady
: box.read(BoxName.carType) == // : box.read(BoxName.carType) == 'Speed'
'Mashwari' // ? 'assets/images/carspeed.png'
? 'assets/images/freeRide.png' // : box.read(BoxName.carType) ==
: box.read(BoxName // 'Scooter'
.carType) == // ? 'assets/images/moto.png'
'Rayeh Gai' // : box.read(BoxName.carType) ==
? 'assets/images/roundtrip.png' // 'Mishwar Vip'
: 'assets/images/carspeed.png', // Default image if none of the above // ? 'assets/images/freeRide.png'
width: 80, // : box.read(BoxName
), // .carType) ==
// 'Awfar Car'
// ? 'assets/images/balash.png'
// : box.read(BoxName
// .carType) ==
// 'Pink Bike'
// ? 'assets/images/pinkBike.png'
// : box.read(BoxName
// .carType) ==
// 'Rayeh Gai'
// ? 'assets/images/roundtrip.png'
// : 'assets/images/carspeed.png', // Default image if none of the above
// width: 80,
// ),
// ),
Column( Column(
children: [ children: [
Text( Text(
@@ -143,14 +174,31 @@ class ApplyOrderWidget extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(
width: 10,
),
Text( Text(
// 'Black', // 'Black',
controller.carColor, controller.carColor.toString(),
style: AppStyle.title, style: AppStyle.title,
), ),
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),
ColorFiltered(
colorFilter: ColorFilter.mode(
_parseColor(controller.colorHex),
BlendMode.srcIn,
),
child: Image.asset(
box.read(BoxName.carType) == 'Scooter' ||
box.read(BoxName.carType) ==
'Pink Bike'
? 'assets/images/moto.png'
: 'assets/images/car3.png',
width: 80,
),
),
], ],
), ),
Padding( Padding(
@@ -159,17 +207,57 @@ class ApplyOrderWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
CircleAvatar( CircleAvatar(
radius: 30, radius: 25,
backgroundImage: NetworkImage( backgroundImage: NetworkImage(
// '', '${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg',
// ), ),
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg'), child: Builder(
builder: (context) {
return Image.network(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg',
fit: BoxFit.cover,
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child; // Image is loaded
} else {
return Center(
child: CircularProgressIndicator(
value: loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
(loadingProgress
.expectedTotalBytes ??
1)
: null,
),
);
}
},
errorBuilder: (BuildContext context,
Object error,
StackTrace? stackTrace) {
return const Icon(
Icons
.person, // Icon to show when image fails to load
size:
25, // Adjust the size as needed
color: AppColor
.blueColor, // Color for the error icon
);
},
);
},
),
), ),
Column( Column(
children: [ children: [
Text( Text(
// 'fadi ahmad', // 'fadi ahmad',
controller.firstName, controller.driverName,
style: AppStyle.title, style: AppStyle.title,
), ),
Text( Text(
@@ -182,7 +270,7 @@ class ApplyOrderWidget extends StatelessWidget {
IconButton( IconButton(
onPressed: () async { onPressed: () async {
Get.defaultDialog( Get.defaultDialog(
title: 'Select one message'.tr, title: 'Select one message',
titleStyle: AppStyle.title, titleStyle: AppStyle.title,
content: SizedBox( content: SizedBox(
width: 300, width: 300,
@@ -192,11 +280,13 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell( InkWell(
onTap: () { onTap: () {
FirebaseMessagesController() FirebaseMessagesController()
.sendNotificationToAnyWithoutData( .sendNotificationToDriverMAP(
'message From passenger', 'message From passenger',
'Hello, I\'m at the agreed-upon location' 'Hello, I\'m at the agreed-upon location'
.tr, .tr,
controller.driverToken, controller.driverToken
.toString(),
[],
'ding.wav', 'ding.wav',
); );
Get.back(); Get.back();
@@ -222,11 +312,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell( InkWell(
onTap: () { onTap: () {
FirebaseMessagesController() FirebaseMessagesController()
.sendNotificationToAnyWithoutData( .sendNotificationToDriverMAP(
'message From passenger'.tr, 'message From passenger',
'My location is correct. You can search for me using the navigation app' 'My location is correct. You can search for me using the navigation app'
.tr, .tr,
controller.driverToken, controller.driverToken,
[],
'ding.wav', 'ding.wav',
); );
Get.back(); Get.back();
@@ -252,11 +343,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell( InkWell(
onTap: () { onTap: () {
FirebaseMessagesController() FirebaseMessagesController()
.sendNotificationToAnyWithoutData( .sendNotificationToDriverMAP(
'message From passenger', 'message From passenger',
'My location is correct. You can search for me using the navigation app' 'My location is correct. You can search for me using the navigation app'
.tr, .tr,
controller.driverToken, controller.driverToken,
[],
'ding.wav', 'ding.wav',
); );
Get.back(); Get.back();
@@ -281,11 +373,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell( InkWell(
onTap: () { onTap: () {
FirebaseMessagesController() FirebaseMessagesController()
.sendNotificationToAnyWithoutData( .sendNotificationToDriverMAP(
'message From passenger', 'message From passenger',
"How much longer will you be?" "How much longer will you be?"
.tr, .tr,
controller.driverToken, controller.driverToken,
[],
'ding.wav', 'ding.wav',
); );
Get.back(); Get.back();
@@ -335,13 +428,14 @@ class ApplyOrderWidget extends StatelessWidget {
IconButton( IconButton(
onPressed: () { onPressed: () {
FirebaseMessagesController() FirebaseMessagesController()
.sendNotificationToAnyWithoutData( .sendNotificationToDriverMAP(
'message From passenger', 'message From passenger',
controller controller
.messageToDriver .messageToDriver
.text, .text,
controller controller
.driverToken, .driverToken,
[],
'ding.wav'); 'ding.wav');
controller controller
.messageToDriver .messageToDriver
@@ -463,7 +557,7 @@ class TimeDriverToPassenger extends StatelessWidget {
Container( Container(
decoration: AppStyle.boxDecoration, decoration: AppStyle.boxDecoration,
width: Get.width * .7, width: Get.width * .7,
height: 35, height: 15,
// color: AppColor.yellowColor, // color: AppColor.yellowColor,
), ),
Stack( Stack(

View File

@@ -3,11 +3,11 @@ import 'package:get/get.dart';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart'; import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/home/map_passenger_controller.dart'; import 'package:SEFER/controller/home/map_passenger_controller.dart';
import '../../widgets/elevated_btn.dart'; import '../../widgets/elevated_btn.dart';
GetBuilder<MapPassengerController> cancelRidePage() { GetBuilder<MapPassengerController> cancelRidePage() {
Get.put(MapPassengerController()); Get.put(MapPassengerController());
final List<String> reasons = [ final List<String> reasons = [
"I don't need a ride anymore".tr, "I don't need a ride anymore".tr,
"I was just trying the application".tr, "I was just trying the application".tr,
@@ -16,80 +16,74 @@ GetBuilder<MapPassengerController> cancelRidePage() {
"I don't have a reason".tr, "I don't have a reason".tr,
"Other".tr, "Other".tr,
]; ];
return GetBuilder<MapPassengerController>( return GetBuilder<MapPassengerController>(
builder: (controller) => controller.isCancelRidePageShown builder: (controller) => controller.isCancelRidePageShown
? Positioned( ? Positioned(
left: Get.width * .1, left: 20,
top: Get.width * .2, top: Get.height * 0.15,
right: Get.width * .1, right: 20,
bottom: Get.width * .15, bottom: Get.height * 0.15,
child: Container( child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.secondaryColor, color: Colors.white,
boxShadow: [ boxShadow: [
const BoxShadow(
color: AppColor.accentColor,
offset: Offset(2, 2),
blurRadius: 5),
BoxShadow( BoxShadow(
color: AppColor.accentColor.withOpacity(.4), color: Colors.black.withOpacity(0.2),
offset: const Offset(-2, -2), offset: const Offset(0, 8),
blurRadius: 5) blurRadius: 16,
),
], ],
borderRadius: const BorderRadius.all(Radius.circular(15)), borderRadius: BorderRadius.circular(20),
), ),
height: Get.height * .7,
width: Get.width * .7,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Padding( Text(
padding: const EdgeInsets.symmetric(horizontal: 10), 'Can we know why you want to cancel Ride ?'.tr,
child: Text( style: AppStyle.title
'Can we know why you want to cancel Ride ?'.tr, .copyWith(fontSize: 18, fontWeight: FontWeight.bold),
style: AppStyle.title, textAlign: TextAlign.center,
textAlign: TextAlign.center,
),
), ),
SizedBox( const SizedBox(height: 20),
height: 380, Expanded(
width: 300, child: ListView.separated(
child: ListView.builder(
itemCount: reasons.length, itemCount: reasons.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: InkWell( title: Text(
onTap: () { reasons[index],
controller.selectReason( style: AppStyle.title.copyWith(fontSize: 16),
index, ),
reasons[index].toString(),
);
},
child: Text(
reasons[index],
style: AppStyle.title,
)),
leading: Radio( leading: Radio(
value: index, value: index,
groupValue: controller.selectedReason, groupValue: controller.selectedReason,
onChanged: (int? value) { onChanged: (int? value) {
controller.selectReason( controller.selectReason(value!, reasons[index]);
value!,
reasons[index].toString(),
);
}, },
activeColor: AppColor.primaryColor,
), ),
onTap: () {
controller.selectReason(index, reasons[index]);
},
); );
}, },
), ),
), ),
const SizedBox(height: 20),
MyElevatedButton( MyElevatedButton(
title: 'Cancel Ride'.tr, title: 'Cancel Ride'.tr,
onPressed: () { onPressed: () {
if (controller.selectedReason == -1) { if (controller.selectedReason == -1) {
Get.snackbar('You Should be select reason.'.tr, '', Get.snackbar(
snackPosition: SnackPosition.BOTTOM, 'You Should be select reason.'.tr,
backgroundColor: AppColor.redColor); '',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.redColor,
colorText: Colors.white,
);
} else { } else {
controller.cancelRide(); controller.cancelRide();
} }

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@ class CashConfirmPageShown extends StatelessWidget {
? controller.cashConfirmPageShown ? controller.cashConfirmPageShown
: 0, : 0,
decoration: BoxDecoration( decoration: BoxDecoration(
color: box.read(BoxName.carType) == 'Lady' color: box.read(BoxName.carType) == 'Lady' ||
box.read(BoxName.carType) == 'Pink Bike'
? Colors.pink.shade100 ? Colors.pink.shade100
: AppColor.secondaryColor, : AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)), borderRadius: BorderRadius.circular(15)),
@@ -168,7 +169,8 @@ class CashConfirmPageShown extends StatelessWidget {
paymentController.update(); paymentController.update();
controller.changeCashConfirmPageShown(); controller.changeCashConfirmPageShown();
controller.isSearchingWindow = true; controller.isSearchingWindow = true;
controller.confirmRideForFirstDriver(); controller
.confirmRideForAllDriverAvailable();
controller.update(); controller.update();
}, },
), ),
@@ -179,7 +181,7 @@ class CashConfirmPageShown extends StatelessWidget {
onPressed: () { onPressed: () {
controller.changeCashConfirmPageShown(); controller.changeCashConfirmPageShown();
controller.isSearchingWindow = true; controller.isSearchingWindow = true;
controller.confirmRideForFirstDriver(); controller.confirmRideForAllDriverAvailable();
controller.update(); controller.update();
}, },
), // Add a fallback widget if none of the conditions are met ), // Add a fallback widget if none of the conditions are met

View File

@@ -242,109 +242,127 @@ GetBuilder<MapPassengerController> formSearchPlacesDestenation() {
// ) // )
// : const SizedBox(), // : const SizedBox(),
Container( Container(
height: controller.placesDestination.isNotEmpty height: controller.placesDestination.isNotEmpty
? controller.height ? controller.height
: 0, : 0,
color: AppColor.secondaryColor, color: AppColor.secondaryColor,
child: ListView.builder( child: ListView.builder(
itemCount: controller.placesDestination.length, itemCount: controller.placesDestination.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
var res = controller.placesDestination[index]; var res = controller.placesDestination[index];
return InkWell(
onTap: () async {
controller.changeHeightPlaces();
await sql.insertData({
'latitude': res['geometry']['location']['lat'],
'longitude': res['geometry']['location']['lng'],
'name': res['name'].toString(),
'rate': res['rating'].toString(),
}, TableName.recentLocations);
controller.changeHeightPlaces(); // Extract fields with null safety
var title = res['title']?.toString() ?? 'Unknown Place';
var position = res['position'];
var latitude = position?['lat'];
var longitude = position?['lng'];
var address =
res['address']?['label'] ?? 'Unknown Address';
var categories = res['categories'] ?? [];
var primaryCategory = categories.isNotEmpty
? categories[0]['name']
: 'Unknown Category';
controller.passengerLocation = controller.newMyLocation; return InkWell(
controller.myDestination = LatLng( onTap: () async {
double.parse( if (latitude != null && longitude != null) {
res['geometry']['location']['lat'].toString()), sql.insertMapLocation({
double.parse( 'latitude': latitude,
res['geometry']['location']['lng'].toString()), 'longitude': longitude,
); 'name': title,
controller.convertHintTextDestinationNewPlaces(index); 'rate': 'N/A',
'createdAt': DateTime.now().toIso8601String(),
// No rating in this structure, adjust as needed
}, TableName.recentLocations);
controller.placesDestination = []; controller.passengerLocation =
controller.placeDestinationController.clear(); controller.newMyLocation;
controller.changeMainBottomMenuMap(); controller.myDestination =
controller.passengerStartLocationFromMap = true; LatLng(latitude, longitude);
controller.isPickerShown = true; controller
}, .convertHintTextDestinationNewPlaces(index);
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10), controller.placesDestination = [];
child: Column( controller.placeDestinationController.clear();
children: [ controller.changeMainBottomMenuMap();
Row( controller.passengerStartLocationFromMap = true;
mainAxisAlignment: MainAxisAlignment.spaceBetween, controller.isPickerShown = true;
children: [ } else {
Column( Toast.show(
children: [ context,
Image.network( 'Invalid location data',
res['icon'], AppColor.redColor,
width: 20, );
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
const Icon(Icons.place,
size: 20), // Fallback icon for places
IconButton(
onPressed: () async {
if (latitude != null &&
longitude != null) {
await sql.insertMapLocation({
'latitude': latitude,
'longitude': longitude,
'name': title,
'rate': 'N/A',
}, TableName.placesFavorite);
Toast.show(
context,
'$title ${'Saved Successfully'.tr}',
AppColor.primaryColor,
);
} else {
Toast.show(
context,
'Invalid location data',
AppColor.redColor,
);
}
},
icon: const Icon(Icons.favorite_border),
),
],
),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
title,
style: AppStyle.title,
),
Text(
address,
style: AppStyle.subtitle,
),
Text(
primaryCategory,
style: AppStyle.subtitle,
),
],
), ),
IconButton( ),
onPressed: () async { ],
await sql.insertData({ ),
'latitude': res['geometry'] const Divider(thickness: 1),
['location']['lat'], ],
'longitude': res['geometry'] ),
['location']['lng'],
'name': res['name'].toString(),
'rate': res['rating'].toString(),
}, TableName.placesFavorite);
Toast.show(
context,
'${res['name']} ${'Saved Sucssefully'.tr}',
AppColor.primaryColor);
},
icon: const Icon(Icons.favorite_border),
),
],
),
Column(
children: [
Text(
res['name'].toString(),
style: AppStyle.title,
),
Text(
res['vicinity'].toString(),
style: AppStyle.subtitle,
),
],
),
Column(
children: [
Text(
'rate',
style: AppStyle.subtitle,
),
Text(
res['rating'].toString(),
style: AppStyle.subtitle,
),
],
),
],
),
const Divider(
thickness: 1,
)
],
), ),
), );
); },
}, ))
),
)
], ],
)); ));
} }

View File

@@ -103,7 +103,7 @@ GetBuilder<MapPassengerController> formSearchPlacesStart() {
// controller.myLocation = // controller.myLocation =
// controller.newStartPointLocation; // controller.newStartPointLocation;
// } // }
await sql.insertData({ await sql.insertMapLocation({
'latitude': res['geometry']['location']['lat'], 'latitude': res['geometry']['location']['lat'],
'longitude': res['geometry']['location']['lng'], 'longitude': res['geometry']['location']['lng'],
'name': res['name'].toString(), 'name': res['name'].toString(),
@@ -130,7 +130,7 @@ GetBuilder<MapPassengerController> formSearchPlacesStart() {
), ),
IconButton( IconButton(
onPressed: () async { onPressed: () async {
await sql.insertData({ await sql.insertMapLocation({
'latitude': res['geometry'] 'latitude': res['geometry']
['location']['lat'], ['location']['lat'],
'longitude': res['geometry'] 'longitude': res['geometry']

View File

@@ -109,7 +109,7 @@ GetBuilder<MapPassengerController> formSearchPlaces(int index) {
), ),
IconButton( IconButton(
onPressed: () async { onPressed: () async {
await sql.insertData({ await sql.insertMapLocation({
'latitude': res['geometry'] 'latitude': res['geometry']
['location']['lat'], ['location']['lat'],
'longitude': res['geometry'] 'longitude': res['geometry']

View File

@@ -318,7 +318,7 @@ class GoogleMapPassengerWidget extends StatelessWidget {
// icon: controller.endIcon, // icon: controller.endIcon,
// ), // ),
// }, // },
polygons: controller.polygons,
polylines: { polylines: {
Polyline( Polyline(
zIndex: 2, zIndex: 2,
@@ -419,7 +419,7 @@ class GoogleMapPassengerWidget extends StatelessWidget {
}, },
mapType: mapType:
controller.mapType ? MapType.satellite : MapType.normal, controller.mapType ? MapType.satellite : MapType.terrain,
myLocationButtonEnabled: true, myLocationButtonEnabled: true,
// liteModeEnabled: true, tiltGesturesEnabled: false, // liteModeEnabled: true, tiltGesturesEnabled: false,

View File

@@ -1,22 +1,14 @@
import 'package:SEFER/constant/api_key.dart';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:SEFER/views/auth/sms_verfy_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../constant/char_map.dart';
import '../../../constant/colors.dart'; import '../../../constant/colors.dart';
import '../../../constant/credential.dart';
import '../../../constant/links.dart';
import '../../../controller/firebase/firbase_messge.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/crud.dart';
import '../../../controller/functions/tts.dart'; import '../../../controller/functions/tts.dart';
import '../../../controller/home/map_passenger_controller.dart'; import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/home/vip_waitting_page.dart';
GetBuilder<MapPassengerController> leftMainMenuIcons() { GetBuilder<MapPassengerController> leftMainMenuIcons() {
final textToSpeechController = Get.put(TextToSpeechController()); Get.put(TextToSpeechController());
return GetBuilder<MapPassengerController>( return GetBuilder<MapPassengerController>(
builder: (controller) => Positioned( builder: (controller) => Positioned(
top: Get.height * .008, top: Get.height * .008,
@@ -99,20 +91,10 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
borderRadius: BorderRadius.circular(15)), borderRadius: BorderRadius.circular(15)),
child: IconButton( child: IconButton(
onPressed: () async { onPressed: () async {
FirebaseMessagesController().sendNotificationToAnyWithoutData( Get.to(() => VipWaittingPage());
'Order'.tr,
'from: ',
// jsonDecode(value)['message'].toString(),
'fBJObfCd9kHxnzMsEzeh2R:APA91bEE435Fvg1ixHs2_GPJJzz5CztswczqAi-PJfS6gSzg5U0eHvOi_v2J3imqPeWvkic-Dhhq2Pzrva2LncvS3MofCTJyM8AVScktGUuB6NvgyeK_5er8yDPrp2-2fqUz7VOXflni',
'order.wav'
// polylineCoordinates.toString()
);
// print(box.read(BoxName.tokenFCM));
//
}, },
icon: const Icon( icon: const Icon(
Icons.voice_chat, Octicons.watch, // Replace this with your desired VIP icon
size: 29, size: 29,
), ),
), ),
@@ -126,54 +108,13 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
// borderRadius: BorderRadius.circular(15)), // borderRadius: BorderRadius.circular(15)),
// child: IconButton( // child: IconButton(
// onPressed: () async { // onPressed: () async {
// Get.to(SmsSignupEgypt()); // controller.statusRide == 'Apply' &&
// controller.isSearchingWindow == false;
// controller.update();
// }, // },
// icon: const Icon( // icon: const Icon(
// Icons.chat, // Octicons
// size: 29, // .telescope, // Replace this with your desired VIP icon
// ),
// ),
// ),
// AnimatedContainer(
// duration: const Duration(microseconds: 200),
// width: controller.widthMapTypeAndTraffic,
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// border: Border.all(),
// borderRadius: BorderRadius.circular(15)),
// child: IconButton(
// onPressed: () async {
// await CRUD().allMethodForAI(
// 'name,fullName,address,idNumber,cardId,dob',
// AppLink.uploadEgypt,
// 'idFront');
// await ImageController().choosImage(
// 'https://api.sefer.live/sefer/uploadEgypt.php',
// 'FrontId');
// AC credentials = AC();
// String apiKey =
// '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';
// String convertedStringN = credentials.c(
// credentials.c(credentials.c(apiKey, cs), cC), cn);
// String retrievedStringS = credentials.r(
// credentials.r(credentials.r(convertedStringN, cn), cC),
// cs);
// //
// if (retrievedStringS == apiKey) {
// print('convertedStringN --- $convertedStringN');
// print('retrievedStringS ---$retrievedStringS');
// print('same');
// }
// await Get.find<PaymentController>()
// .payWithPayMob(context, '1100', 'EGP');
// Initiates a payment with a card using the FlutterPaymob instance
// },
// icon: const Icon(
// // Get.put(AudioRecorderController()).isRecording
// Icons.start,
// size: 29, // size: 29,
// ), // ),
// ), // ),

View File

@@ -1,4 +1,6 @@
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:SEFER/views/widgets/my_textField.dart'; import 'package:SEFER/views/widgets/my_textField.dart';
import 'package:SEFER/views/widgets/mysnakbar.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -14,6 +16,7 @@ import '../../../constant/colors.dart';
import '../../../constant/table_names.dart'; import '../../../constant/table_names.dart';
import '../../../controller/functions/toast.dart'; import '../../../controller/functions/toast.dart';
import '../../../controller/functions/tts.dart'; import '../../../controller/functions/tts.dart';
import '../../../print.dart';
import 'form_search_start.dart'; import 'form_search_start.dart';
class MainBottomMenuMap extends StatelessWidget { class MainBottomMenuMap extends StatelessWidget {
@@ -46,24 +49,34 @@ class MainBottomMenuMap extends StatelessWidget {
child: Container( child: Container(
width: Get.width * .8, width: Get.width * .8,
height: Get.height * .1, height: Get.height * .1,
decoration: const BoxDecoration( padding: const EdgeInsets.symmetric(
boxShadow: [ horizontal: 20, vertical: 10),
BoxShadow( decoration: BoxDecoration(
color: Color.fromARGB( gradient: LinearGradient(
255, 237, 230, 230), colors: [
blurRadius: 5, AppColor.blueColor.withOpacity(0.8),
offset: Offset(2, 4)), AppColor.blueColor.withOpacity(0.6),
BoxShadow( ],
color: Color.fromARGB( begin: Alignment.topLeft,
255, 242, 237, 237), end: Alignment.bottomRight,
blurRadius: 5,
offset: Offset(-2, -2))
],
color: AppColor.blueColor,
borderRadius: BorderRadius.all(
Radius.elliptical(15, 30),
), ),
boxShadow: const [
BoxShadow(
color: Color.fromARGB(
255, 237, 230, 230),
blurRadius: 8,
offset: Offset(4, 8),
),
BoxShadow(
color: Color.fromARGB(
255, 242, 237, 237),
blurRadius: 8,
offset: Offset(-4, -4),
),
],
borderRadius: BorderRadius.circular(30),
), ),
// decoration: AppStyle.boxDecoration1, // decoration: AppStyle.boxDecoration1,
child: DefaultTextStyle( child: DefaultTextStyle(
style: AppStyle.title.copyWith( style: AppStyle.title.copyWith(
@@ -338,7 +351,7 @@ class MainBottomMenuMap extends StatelessWidget {
title: 'Yes'.tr, title: 'Yes'.tr,
onPressed: () async { onPressed: () async {
Get.back(); Get.back();
controller.getLocation(); await controller.getLocation();
await controller.getMap( await controller.getMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}', '${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${controller.recentPlaces[index]['latitude']},${controller.recentPlaces[index]['longitude']}', '${controller.recentPlaces[index]['latitude']},${controller.recentPlaces[index]['longitude']}',
@@ -350,6 +363,18 @@ class MainBottomMenuMap extends StatelessWidget {
}, },
)); ));
}, },
onLongPress: () {
MyDialog().getDialog(
"Are you sure to delete this location?".tr, '', () {
sql.deleteData(TableName.recentLocations,
controller.recentPlaces[index]['id']);
controller.getFavioratePlaces();
controller.update();
Get.back();
mySnackbarSuccess('deleted'.tr);
});
},
child: Container( child: Container(
decoration: AppStyle.boxDecoration1, decoration: AppStyle.boxDecoration1,
child: Padding( child: Padding(
@@ -606,7 +631,7 @@ class FaviouratePlacesDialog extends StatelessWidget {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
Get.back(); Get.back();
controller.getLocation(); await controller.getLocation();
await controller.getMap( await controller.getMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}', '${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}', '${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}',

View File

@@ -1,3 +1,4 @@
import 'package:SEFER/views/home/HomePage/contact_us.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart'; import 'package:flutter_font_icons/flutter_font_icons.dart';
@@ -110,10 +111,10 @@ class MapMenuWidget extends StatelessWidget {
), ),
IconMainPageMap( IconMainPageMap(
onTap: () { onTap: () {
Get.to(() => const TaarifPage()); Get.to(() => ContactUsPage());
}, },
title: 'Tariff'.tr, title: "Contact Us".tr,
icon: Icons.money, icon: Icons.contact_page,
), ),
], ],
), ),

View File

@@ -10,6 +10,7 @@ import 'package:SEFER/main.dart';
import '../../../constant/colors.dart'; import '../../../constant/colors.dart';
import '../../../constant/style.dart'; import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart'; import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart'; import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart'; import '../../../controller/home/map_passenger_controller.dart';
@@ -29,7 +30,7 @@ class RideBeginPassenger extends StatelessWidget {
return Positioned( return Positioned(
left: 10, left: 10,
right: 10, right: 10,
bottom: 4, bottom: 10,
child: Container( child: Container(
decoration: AppStyle.boxDecoration, decoration: AppStyle.boxDecoration,
height: controller.statusRide == 'Begin' ? Get.height * .33 : 0, height: controller.statusRide == 'Begin' ? Get.height * .33 : 0,
@@ -52,7 +53,7 @@ class RideBeginPassenger extends StatelessWidget {
Container( Container(
decoration: AppStyle.boxDecoration, decoration: AppStyle.boxDecoration,
child: Text( child: Text(
controller.firstName, controller.driverName,
style: AppStyle.title, style: AppStyle.title,
), ),
), ),
@@ -248,8 +249,8 @@ class RideBeginPassenger extends StatelessWidget {
profileController.prfoileData['sosPhone']); profileController.prfoileData['sosPhone']);
} }
} else { } else {
controller makePhoneCall('122');
.sendSMS(box.read(BoxName.sosPhonePassenger)); // box.read(BoxName.sosPhonePassenger));
} }
}, },
icon: const Icon( icon: const Icon(

View File

@@ -8,6 +8,121 @@ import 'package:SEFER/views/widgets/my_textField.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../../constant/links.dart';
// class SearchingCaptainWindow extends StatelessWidget {
// const SearchingCaptainWindow({super.key});
// Widget _buildDriverAvatars(MapPassengerController controller) {
// // If no drivers yet, show loading indicator
// if (controller.isSearchingWindow) {
// // Check if dataCarsLocationByPassenger or its 'data' is null
// if (controller.dataCarsLocationByPassenger == null ||
// controller.dataCarsLocationByPassenger['data'] == null ||
// controller.dataCarsLocationByPassenger['data'].isEmpty) {
// return const SizedBox(
// height: 60,
// child: Center(
// child: CircularProgressIndicator(
// valueColor:
// AlwaysStoppedAnimation<Color>(AppColor.secondaryColor),
// ),
// ),
// );
// }
// }
// return SizedBox(
// height: 60,
// child: ListView.builder(
// scrollDirection: Axis.horizontal,
// itemCount: controller.dataCarsLocationByPassenger['data'].length,
// padding: const EdgeInsets.symmetric(horizontal: 16),
// itemBuilder: (context, index) {
// final driver = controller.dataCarsLocationByPassenger['data'][index];
// return Padding(
// padding: const EdgeInsets.only(right: 8),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// CircleAvatar(
// radius: 25,
// backgroundColor: AppColor.secondaryColor,
// child: ClipOval(
// child: Image.network(
// '${AppLink.server}/portrate_captain_image/${driver['driver_id']}.jpg',
// width: 50,
// height: 50,
// fit: BoxFit.cover,
// errorBuilder: (context, error, stackTrace) {
// return const Icon(
// Icons.person,
// color: Colors.white,
// size: 30,
// );
// },
// ),
// ),
// ),
// ],
// ),
// );
// },
// ),
// );
// }
// @override
// Widget build(BuildContext context) {
// return GetBuilder<MapPassengerController>(
// builder: (mapPassengerController) {
// return mapPassengerController.isSearchingWindow
// ? Positioned(
// bottom: 0,
// left: 0,
// right: 0,
// child: Container(
// decoration: AppStyle.boxDecoration1,
// height: Get.height *
// .3, // Increased height to accommodate avatars
// child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// SizedBox(
// width: Get.width * .7,
// child: const LinearProgressIndicator(
// minHeight: 6,
// backgroundColor: AppColor.yellowColor,
// color: AppColor.secondaryColor,
// ),
// ),
// mapPassengerController.driverOrderStatus == 'recive'
// ? Text(
// "Drivers received orders".tr,
// style: AppStyle.title,
// )
// : Text(
// "We are searching for the nearest driver to you"
// .tr,
// style: AppStyle.title,
// ),
// Text(
// 'please wait till driver accept your order'.tr,
// style: AppStyle.title,
// ),
// // New: Driver avatars section
// _buildDriverAvatars(mapPassengerController),
// _buildTimer(mapPassengerController),
// ],
// ),
// ),
// )
// : const SizedBox();
// },
// );
// }
// }
class SearchingCaptainWindow extends StatelessWidget { class SearchingCaptainWindow extends StatelessWidget {
const SearchingCaptainWindow({super.key}); const SearchingCaptainWindow({super.key});
@@ -22,11 +137,13 @@ class SearchingCaptainWindow extends StatelessWidget {
right: 0, right: 0,
child: Container( child: Container(
decoration: AppStyle.boxDecoration1, decoration: AppStyle.boxDecoration1,
height: Get.height * .2, height: Get.height * .25,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
// Use Stack for overlapping widgets // Use Stack for overlapping widgets
children: [ children: [
// Text elements // Text elements
SizedBox( SizedBox(
width: Get.width * .7, width: Get.width * .7,
child: const LinearProgressIndicator( child: const LinearProgressIndicator(
@@ -36,9 +153,13 @@ class SearchingCaptainWindow extends StatelessWidget {
), ),
), ),
Text( Text(
"We are searching for the nearest driver to you".tr, mapPassengerController.driversStatusForSearchWindow,
style: AppStyle.title, style: AppStyle.title,
), ),
// Text(
// "We are searching for the nearest driver to you".tr,
// style: AppStyle.title,
// ),
Text( Text(
'please wait till driver accept your order'.tr, 'please wait till driver accept your order'.tr,
style: AppStyle.title, style: AppStyle.title,

View File

@@ -1,13 +1,13 @@
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart'; import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/home/map_passenger_controller.dart'; import 'package:SEFER/controller/home/map_passenger_controller.dart';
import 'package:SEFER/env/env.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart'; import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../../constant/api_key.dart'; import '../../../constant/api_key.dart';
import '../../../constant/links.dart';
import '../../../print.dart'; import '../../../print.dart';
class CupertinoDriverListWidget extends StatelessWidget { class CupertinoDriverListWidget extends StatelessWidget {
@@ -17,154 +17,214 @@ class CupertinoDriverListWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CupertinoPageScaffold( return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
middle: Text('Driver List'.tr), middle: Text('Driver List'.tr), // Ensure text is properly localized
), ),
child: SafeArea( child: SafeArea(
child: ListView.separated( child: mapPassengerController.driversForMishwari.isEmpty
itemCount: mapPassengerController.driversForMishwari.length, ? Center(
separatorBuilder: (context, index) => const Divider(height: 1), child: Text(
itemBuilder: (context, index) { 'No drivers available at the moment. Please try again later.'
var driver = mapPassengerController.driversForMishwari[index]; .tr,
return Container( style: const TextStyle(
decoration: AppStyle.boxDecoration1, fontSize: 18, // Adjust the size as needed
child: CupertinoListTile( fontWeight: FontWeight.w600,
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), color: CupertinoColors.inactiveGray, // Customize color
leading: CircleAvatar( ),
radius: 25, textAlign: TextAlign.center, // Center-align the text
backgroundImage: NetworkImage(
'${Env.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg',
),
backgroundColor: CupertinoColors.systemGrey5,
),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${driver['NAME'].toString().split(' ')[0]} ${driver['NAME'].toString().split(' ')[1]}',
style: const TextStyle(fontWeight: FontWeight.bold),
), ),
Text('${'Age'.tr}: ${driver['age'].toString()}'), )
Row( : ListView.separated(
children: [ itemCount: mapPassengerController.driversForMishwari.length,
const Icon(CupertinoIcons.star_fill, separatorBuilder: (context, index) =>
size: 16, color: CupertinoColors.systemYellow), const Divider(height: 1),
const SizedBox(width: 4), itemBuilder: (context, index) {
Text(driver['rating']?.toStringAsFixed(1) ?? 'N/A'.tr), var driver =
const SizedBox(width: 8), mapPassengerController.driversForMishwari[index];
Text('${'Rides'.tr}: ${driver['countRide']}'), return Container(
], decoration: AppStyle.boxDecoration1,
), child: CupertinoListTile(
], padding: const EdgeInsets.symmetric(
), vertical: 4, horizontal: 8),
subtitle: Column( leading: CircleAvatar(
crossAxisAlignment: CrossAxisAlignment.start, radius: 25,
children: [ backgroundImage: NetworkImage(
Row( '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg',
mainAxisAlignment: MainAxisAlignment.spaceBetween, ),
children: [ child: Builder(
Text( builder: (context) {
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), return Image.network(
Text('${'Plate'.tr}: ${driver['car_plate']}'), '${AppLink.seferCairoServer}/portrate_captain_image/${driver['id']}.jpg',
], fit: BoxFit.cover,
), loadingBuilder: (BuildContext context,
Row( Widget child,
mainAxisAlignment: MainAxisAlignment.spaceBetween, ImageChunkEvent? loadingProgress) {
children: [ if (loadingProgress == null) {
Text('${'Education'.tr}: ${driver['education']}'), return child; // Image is loaded
], } else {
), return Center(
Row( child: CircularProgressIndicator(
mainAxisAlignment: MainAxisAlignment.spaceBetween, value: loadingProgress
children: [ .expectedTotalBytes !=
SizedBox( null
// width: Get.width * .3, ? loadingProgress
child: Row( .cumulativeBytesLoaded /
mainAxisAlignment: MainAxisAlignment.spaceBetween, (loadingProgress
children: [ .expectedTotalBytes ??
Text('${'Color'.tr}: ${driver['color']}'), 1)
const SizedBox(width: 8), : null,
Container( ),
width: 20, );
height: 20, }
decoration: BoxDecoration( },
color: hexToColor( errorBuilder: (BuildContext context,
driver['color_hex'].toString()) ?? Object error, StackTrace? stackTrace) {
Colors.amber, return const Icon(
borderRadius: BorderRadius.circular(4), Icons
border: Border.all(), .person, // Icon to show when image fails to load
), size: 25, // Adjust the size as needed
), color: AppColor
], .blueColor, // Color for the error icon
);
},
);
},
),
), ),
), title: Row(
],
),
],
),
onTap: () {
// Handle driver selection
Get.defaultDialog(
title: '${'Selected driver'.tr}: ${driver['NAME']}',
content: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'), '${driver['NAME'].toString().split(' ')[0]} ${driver['NAME'].toString().split(' ')[1]}',
Text('${'Plate'.tr}: ${driver['car_plate']}'), style:
const TextStyle(fontWeight: FontWeight.bold),
),
Text('${'Age'.tr}: ${driver['age'].toString()}'),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text('${'Color'.tr}: ${driver['color']}'), const Icon(CupertinoIcons.star_fill,
size: 16,
color: CupertinoColors.systemYellow),
const SizedBox(width: 4),
Text(driver['rating']?.toStringAsFixed(1) ??
'N/A'.tr),
const SizedBox(width: 8), const SizedBox(width: 8),
Container( Text('${'Rides'.tr}: ${driver['ride_count']}'),
width: 20, ],
height: 20, ),
decoration: BoxDecoration( ],
color: hexToColor( ),
driver['color_hex'].toString()) ?? subtitle: Column(
AppColor.bronze, crossAxisAlignment: CrossAxisAlignment.start,
borderRadius: BorderRadius.circular(4), children: [
border: Border.all(), Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'),
Text('${'Plate'.tr}: ${driver['car_plate']}'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
// width: Get.width * .3,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('${'Color'.tr}: ${driver['color']}'),
const SizedBox(width: 8),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: driver['color_hex']
.toString() ==
'null'
? Colors.amber
: hexToColor(driver['color_hex']
.toString()),
borderRadius:
BorderRadius.circular(4),
border: Border.all(),
),
),
],
), ),
), ),
], ],
), ),
], ],
), ),
], onTap: () {
), Log.print(' driver["id"]: ${driver['driver_id']}');
confirm: MyElevatedButton( Get.find<MapPassengerController>().driverIdVip =
title: 'OK'.tr, driver['driver_id'];
onPressed: () {
Get.back(); // Handle driver selection
showDateTimePickerDialog(driver); Get.defaultDialog(
Log.print('driver: ${driver}'); title:
})); '${'Selected driver'.tr}: ${driver['NAME']}',
print('${'Selected driver'.tr}: ${driver['NAME']}'); content: Column(
// Get.back(); // Close the dialog children: [
}, Column(
), mainAxisAlignment:
); MainAxisAlignment.spaceBetween,
}, children: [
)), Text(
'${'Car'.tr}: ${driver['make']} ${driver['model']} (${driver['year']})'),
Text(
'${'Plate'.tr}: ${driver['car_plate']}'),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
'${'Color'.tr}: ${driver['color']}'),
const SizedBox(width: 8),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: driver['color_hex']
.toString() ==
'null'
? Colors.amber
: hexToColor(
driver['color_hex']
.toString()),
borderRadius:
BorderRadius.circular(4),
border: Border.all(),
),
),
],
),
],
),
],
),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
showDateTimePickerDialog(driver);
}));
print('${'Selected driver'.tr}: ${driver['NAME']}');
// Get.back(); // Close the dialog
},
),
);
},
)),
); );
} }
Color hexToColor(String hexColor) { Color hexToColor(String hexColor) {
if (hexColor == null || hexColor.isEmpty || hexColor == 'null') {
// Return a default color if the hex color is invalid
return Colors.grey;
}
hexColor = hexColor.replaceAll("#", ""); hexColor = hexColor.replaceAll("#", "");
if (hexColor.length == 6) { String colorString = "ff$hexColor";
hexColor = "ff$hexColor"; return Color(int.parse(colorString, radix: 16));
} else if (hexColor.length != 8) {
// Return a default color if the hex color is not in the valid format
return Colors.grey;
}
return Color(int.parse(hexColor, radix: 16));
} }
void showDriverSelectionDialog(Map<String, dynamic> driver) { void showDriverSelectionDialog(Map<String, dynamic> driver) {
@@ -185,7 +245,9 @@ class CupertinoDriverListWidget extends StatelessWidget {
width: 20, width: 20,
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
color: hexToColor(driver['color_hex'].toString()), color: driver['color_hex'].toString() == 'null'
? Colors.amber
: hexToColor(driver['color_hex'].toString()),
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
border: Border.all(), border: Border.all(),
), ),
@@ -204,28 +266,12 @@ class CupertinoDriverListWidget extends StatelessWidget {
); );
} }
Future<void> confirmTripData(
Map<String, dynamic> driver, DateTime selectedDateTime) async {
try {
// Save trip data and set up notifications
// Log.print('selectedDateTime: $selectedDateTime');
// Log.print('driver: $driver');
await mapPassengerController.saveTripData(driver, selectedDateTime);
Get.back(); // Close the dialog
} catch (e) {
// Handle any errors that occur during the save process
Log.print('Error saving trip data: $e');
Get.snackbar('Error', 'Failed to save trip data');
}
}
void showDateTimePickerDialog(Map<String, dynamic> driver) { void showDateTimePickerDialog(Map<String, dynamic> driver) {
Log.print('driver: ${driver}');
DateTime selectedDateTime = DateTime.now(); DateTime selectedDateTime = DateTime.now();
Get.defaultDialog( Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
title: 'select date and time of trip'.tr, title: "Select date and time of trip".tr,
content: SizedBox( content: SizedBox(
// height: 400, // Adjust height as needed // height: 400, // Adjust height as needed
width: double.maxFinite, width: double.maxFinite,
@@ -237,10 +283,19 @@ class CupertinoDriverListWidget extends StatelessWidget {
), ),
confirm: MyElevatedButton( confirm: MyElevatedButton(
title: 'Confirm Trip'.tr, title: 'Confirm Trip'.tr,
onPressed: () { onPressed: () async {
DateTime selectedDateTime = DateTime selectedDateTime =
mapPassengerController.selectedDateTime.value; mapPassengerController.selectedDateTime.value;
confirmTripData(driver, selectedDateTime); // Save trip data and set up notifications
Get.back();
await mapPassengerController.saveTripData(driver, selectedDateTime);
},
),
cancel: MyElevatedButton(
kolor: AppColor.redColor,
title: 'Cancel'.tr,
onPressed: () {
Get.back();
}, },
), ),
); );
@@ -253,10 +308,10 @@ class DateTimePickerWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CupertinoPageScaffold( return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
transitionBetweenRoutes: false, transitionBetweenRoutes: false,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
middle: Text('Date and Time Picker'), middle: Text('Date and Time Picker'.tr),
), ),
child: SafeArea( child: SafeArea(
child: Column( child: Column(

View File

@@ -0,0 +1,319 @@
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/views/home/profile/complaint_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/controller/profile/profile_controller.dart';
import 'package:SEFER/main.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/launch.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/map_passenger_controller.dart';
class VipRideBeginPassenger extends StatelessWidget {
const VipRideBeginPassenger({
super.key,
});
@override
Widget build(BuildContext context) {
ProfileController profileController = Get.put(ProfileController());
AudioRecorderController audioController =
Get.put(AudioRecorderController());
// Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(builder: (controller) {
if (controller.statusRideVip == 'Begin' ||
!controller.statusRideFromStart) {
return Positioned(
left: 10,
right: 10,
bottom: 10,
child: Container(
decoration: AppStyle.boxDecoration,
height: controller.statusRideVip == 'Begin' ? Get.height * .33 : 0,
// width: 100,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'${AppLink.server}/portrate_captain_image/${controller.driverId}.jpg',
),
onBackgroundImageError: (_, __) {
// Handle error here
},
backgroundColor: Colors.grey,
child: const Icon(
Icons.person, // Default icon or placeholder
size: 30,
color: Colors.white,
), // Placeholder background color
),
Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
child: Text(
controller.driverName,
style: AppStyle.title,
),
),
),
const SizedBox(
height: 10,
),
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
controller.make,
style: AppStyle.title,
),
const SizedBox(
width: 10,
),
Text(
controller.model,
style: AppStyle.title,
),
],
),
),
),
],
),
Column(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(3),
child: Text(
'vip',
style: AppStyle.title,
),
),
),
Text(
'${controller.driverRate} 📈',
style: AppStyle.title,
),
],
),
],
),
// SizedBox(
// height: 5,
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration,
child: IconButton(
onPressed: () => Get.to(
() => ComplaintPage(),
transition: Transition.downToUp,
),
icon: const Icon(
Icons.note_add,
color: AppColor.redColor,
),
tooltip: ' Add Note', // Optional tooltip for clarity
),
),
Container(
width: Get.width * .15,
decoration: AppStyle.boxDecoration,
child: audioController.isRecording == false
? IconButton(
onPressed: () async {
await audioController.startRecording();
Toast.show(context, 'Start Record'.tr,
AppColor.greenColor);
},
icon: const Icon(
Icons.play_circle_fill_outlined,
color: AppColor.greenColor,
),
tooltip:
' Add Note', // Optional tooltip for clarity
)
: IconButton(
onPressed: () async {
await audioController.stopRecording();
Toast.show(context, 'Record saved'.tr,
AppColor.greenColor);
},
icon: const Icon(
Icons.stop_circle,
color: AppColor.greenColor,
),
tooltip:
' Add Note', // Optional tooltip for clarity
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null) {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
makePhoneCall('122');
// box.read(BoxName.sosPhonePassenger));
}
},
icon: const Icon(
Icons.sos_rounded,
color: AppColor.redColor,
),
),
),
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
if (box.read(BoxName.sosPhonePassenger) == null ||
box.read(BoxName.sosPhonePassenger) == 'sos') {
{
await profileController.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
}
} else {
String phoneNumber = box
.read(BoxName.sosPhonePassenger)
.toString();
// phoneNumber = phoneNumber.replaceAll('0', '');
var phone = box.read(BoxName.countryCode) ==
'Egypt'
? '+2${box.read(BoxName.sosPhonePassenger)}'
: '+962${box.read(BoxName.sosPhonePassenger)}';
controller.sendWhatsapp(phone);
}
},
icon: const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
),
),
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .15,
child: IconButton(
onPressed: () async {
await controller.getTokenForParent();
},
icon: const Icon(
AntDesign.Safety,
color: AppColor.blueColor,
),
),
),
],
),
Stack(
children: [
// StreamCounter(),
LinearProgressIndicator(
backgroundColor: AppColor.accentColor,
color:
// controller.remainingTimeTimerRideBegin < 60
// ? AppColor.redColor
// :
AppColor.greenColor,
minHeight: 25,
borderRadius: BorderRadius.circular(15),
value:
24 //controller.progressTimerRideBegin.toDouble(),
),
Center(
child: Text(
controller.stringElapsedTimeRideBeginVip,
style: AppStyle.title,
),
)
],
)
],
),
),
),
);
} else {
return const SizedBox();
}
});
}
}
class StreamCounter extends StatelessWidget {
const StreamCounter({Key? key}) : super(key: key);
@override
// Build the UI based on the timer value
Widget build(BuildContext context) {
Get.put(MapPassengerController());
return GetBuilder<MapPassengerController>(builder: (controller) {
return StreamBuilder<int>(
initialData: 0,
stream: controller.timerController.stream,
builder: (context, snapshot) {
// Calculate the remaining time based on the current tick
final remainingTime = controller.durationToRide - snapshot.data!;
// Format the remaining time as a string
final formattedRemainingTime =
'${(remainingTime / 60).floor()}:${(remainingTime % 60).toString().padLeft(2, '0')}';
// Return the UI widgets based on the remaining time
return Column(
children: [
Text(formattedRemainingTime),
// ElevatedButton(
// onPressed: () {
// // Handle button press here
// },
// ),
],
);
},
);
});
}
}

View File

@@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -6,6 +7,7 @@ import '../../../constant/box_name.dart';
import '../../../constant/colors.dart'; import '../../../constant/colors.dart';
import '../../../constant/info.dart'; import '../../../constant/info.dart';
import '../../../constant/style.dart'; import '../../../constant/style.dart';
import '../../../controller/functions/toast.dart';
import '../../../controller/home/payment/credit_card_controller.dart'; import '../../../controller/home/payment/credit_card_controller.dart';
import '../../../controller/payment/payment_controller.dart'; import '../../../controller/payment/payment_controller.dart';
import '../../../main.dart'; import '../../../main.dart';
@@ -27,27 +29,51 @@ class PassengerWallet extends StatelessWidget {
GetBuilder<PaymentController>( GetBuilder<PaymentController>(
builder: (controller) => Column( builder: (controller) => Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const CardSeferWallet(), const CardSeferWallet(),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 30), padding:
child: Row( const EdgeInsets.symmetric(horizontal: 80, vertical: 10),
children: [ child: MyElevatedButton(
MyElevatedButton( kolor: AppColor.blueColor,
kolor: AppColor.blueColor, title: 'Payment History'.tr,
title: 'Payment History'.tr, onPressed: () {
onPressed: () { Get.to(() => const PaymentHistoryPassengerPage(),
Get.to(() => const PaymentHistoryPassengerPage(), transition: Transition.size);
transition: Transition.size); },
},
),
],
), ),
) ),
// Padding(
// padding:
// const EdgeInsets.symmetric(horizontal: 80, vertical: 10),
// child: MyElevatedButton(
// kolor: AppColor.yellowColor,
// title: 'Bonus gift'.tr,
// onPressed: () {
// Get.dialog(
// AlertDialog(
// contentPadding: EdgeInsets
// .zero, // Removes the padding around the content
// content: SizedBox(
// width: 300, // Match the width of PromoBanner
// // height: 250, // Match the height of PromoBanner
// child: PromoBanner(
// promoCode: box.read(BoxName.promo),
// discountPercentage: box.read(BoxName.discount),
// validity: box.read(BoxName.validity),
// ),
// ),
// ),
// );
// Log.print(
// 'box.read(BoxName.isGiftToken).toString(): ${box.read(BoxName.isGiftToken).toString()}');
// },
// ),
// )
], ],
), ),
), ),
@@ -62,11 +88,77 @@ class PassengerWallet extends StatelessWidget {
bottom: Get.height * .2, bottom: Get.height * .2,
left: Get.width * .2, left: Get.width * .2,
right: Get.width * .2, right: Get.width * .2,
child: MyElevatedButton( child: Column(
title: 'Show Promos to Charge'.tr, crossAxisAlignment: CrossAxisAlignment.stretch,
onPressed: () { children: [
controller.changePromoSheetDialogue(); MyElevatedButton(
}, title: 'Show Promos to Charge'.tr,
onPressed: () {
// controller.changePromoSheetDialogue();
showPaymentBottomSheet(context);
},
),
const SizedBox(
height: 20,
),
MyElevatedButton(
kolor: AppColor.deepPurpleAccent,
title: "Add wallet phone you use".tr,
onPressed: () {
Get.dialog(
CupertinoAlertDialog(
title: Text('Insert Wallet phone number'.tr),
content: Column(
children: [
const SizedBox(height: 10),
Form(
key: controller.formKey,
child: CupertinoTextField(
controller:
controller.walletphoneController,
placeholder:
'Insert Wallet phone number'.tr,
keyboardType: TextInputType.phone,
padding: const EdgeInsets.symmetric(
vertical: 12, horizontal: 10),
),
),
],
),
actions: <Widget>[
CupertinoDialogAction(
child: Text('Cancel'.tr,
style: const TextStyle(
color: CupertinoColors
.destructiveRed)),
onPressed: () {
Get.back(); // Dismiss the dialog
},
),
CupertinoDialogAction(
child: Text('OK'.tr,
style: const TextStyle(
color:
CupertinoColors.activeGreen)),
onPressed: () async {
Get.back(); // Close the dialog
box.write(
BoxName.phoneWallet,
controller
.walletphoneController.text);
Toast.show(
context,
'Phone Wallet Saved Successfully'.tr,
AppColor.greenColor);
},
),
],
),
barrierDismissible:
false, // Set to prevent dismissing by tapping outside
);
})
],
), ),
)), )),
const PassengerWalletDialog(), const PassengerWalletDialog(),
@@ -76,70 +168,71 @@ class PassengerWallet extends StatelessWidget {
} }
class CardSeferWallet extends StatelessWidget { class CardSeferWallet extends StatelessWidget {
const CardSeferWallet({ const CardSeferWallet({super.key});
super.key,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<PaymentController>(builder: (paymentController) { return GetBuilder<PaymentController>(
return Row( builder: (paymentController) {
mainAxisAlignment: MainAxisAlignment.center, return Container(
children: [ width: Get.width * 0.9,
Container( height: Get.height * 0.25,
width: Get.width * .85, margin: const EdgeInsets.all(16.0),
height: Get.height * .3, decoration: BoxDecoration(
decoration: BoxDecoration( color: CupertinoColors.extraLightBackgroundGray,
color: AppColor.twitterColor.withOpacity(.8), borderRadius: BorderRadius.circular(12),
borderRadius: const BorderRadius.all(Radius.circular(12)), boxShadow: [
gradient: const LinearGradient(colors: [ BoxShadow(
AppColor.redColor, color: Colors.black.withOpacity(0.1),
AppColor.yellowColor, offset: const Offset(0, 10),
AppColor.yellowColor, blurRadius: 20,
]), ),
), ],
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( // Wallet Title
padding: const EdgeInsets.all(8.0), Text(
child: Row( '${AppInformation.appName} Wallet',
children: [ style: AppStyle.headTitle.copyWith(
Text( color: CupertinoColors.label,
'${AppInformation.appName} Wallet', fontSize: 20,
style: AppStyle.headTitle fontWeight: FontWeight.bold,
.copyWith(color: AppColor.writeColor),
)
],
), ),
), ),
Row(
mainAxisAlignment: MainAxisAlignment.center, // Wallet Balance
children: [ Center(
Text( child: Text(
'${box.read(BoxName.passengerWalletTotal)} \$' ?? '${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'LE'.tr}',
'0.0 \$', style: AppStyle.headTitle2.copyWith(
style: AppStyle.headTitle2, color: CupertinoColors.label,
) fontSize: 36,
], fontWeight: FontWeight.w600,
), ),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
box.read(BoxName.name),
style: AppStyle.title,
)
],
), ),
) ),
// User Name (Bottom Right)
Align(
alignment: Alignment.bottomRight,
child: Text(
box.read(BoxName.name),
style: AppStyle.title.copyWith(
color: CupertinoColors.secondaryLabel,
fontSize: 16,
),
),
),
], ],
), ),
), ),
], );
); },
}); );
} }
} }

View File

@@ -1,11 +1,11 @@
import 'package:SEFER/constant/box_name.dart'; import 'package:SEFER/constant/style.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/toast.dart'; import 'package:SEFER/controller/functions/toast.dart';
import 'package:SEFER/controller/payment/payment_controller.dart'; import 'package:SEFER/controller/payment/payment_controller.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import '../../../main.dart'; import '../../../main.dart';
@@ -18,249 +18,344 @@ class PassengerWalletDialog extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<PaymentController>( return GetBuilder<PaymentController>(
builder: (controller) => Positioned( builder: (controller) => Positioned(
top: Get.height * .1, top: Get.height * .1,
right: Get.width * .15, right: Get.width * .15,
left: Get.width * .15, left: Get.width * .15,
bottom: Get.height * .1, bottom: Get.height * .1,
child: controller.isPromoSheetDialogue child: controller.isPromoSheetDialogue
? Container( ? CupertinoActionSheet(
decoration: const BoxDecoration( title: Text('Select Payment Amount'.tr),
borderRadius: BorderRadius.all(Radius.circular(12)), actions: [
color: AppColor.secondaryColor, CupertinoActionSheetAction(
boxShadow: [ onPressed: () {
BoxShadow( controller.updateSelectedAmount(
color: AppColor.accentColor, box.read(BoxName.countryCode) == 'Egypt' ? 100 : 10,
offset: Offset(-1, -1), );
blurRadius: 0, showPaymentOptions(context, controller);
spreadRadius: 0, },
blurStyle: BlurStyle.normal), child: Text(
BoxShadow( box.read(BoxName.countryCode) == 'Egypt'
color: AppColor.accentColor, ? '100 ${'LE'.tr}'
offset: Offset(3, 3), : '10 ${'JOD'.tr}',
blurRadius: 1, ),
spreadRadius: 0, ),
blurStyle: BlurStyle.normal) CupertinoActionSheetAction(
]), onPressed: () {
child: Padding( controller.updateSelectedAmount(
padding: const EdgeInsets.all(6), box.read(BoxName.countryCode) == 'Egypt' ? 200 : 20,
child: Column( );
crossAxisAlignment: CrossAxisAlignment.center, showPaymentOptions(context, controller);
children: [ },
GestureDetector( child: Text(
onTap: () { box.read(BoxName.countryCode) == 'Egypt'
controller.updateSelectedAmount(10); ? '200 ${'LE'.tr} = 205 ${'LE'.tr}'
}, : '20 ${'JOD'.tr}',
child: Row( ),
children: [ ),
Radio( CupertinoActionSheetAction(
value: box.read(BoxName.countryCode) == 'Egypt' onPressed: () {
? 100 controller.updateSelectedAmount(
: 10, box.read(BoxName.countryCode) == 'Egypt' ? 400 : 40,
groupValue: controller.selectedAmount, );
onChanged: (value) { showPaymentOptions(context, controller);
controller.updateSelectedAmount(value as int); },
}, child: Text(
), box.read(BoxName.countryCode) == 'Egypt'
Text( ? '400 ${'LE'.tr} = 415 ${'LE'.tr}'
box.read(BoxName.countryCode) == 'Egypt' : '40 ${'JOD'.tr}',
? '100 ${'LE'.tr}'.tr ),
: '10 ${'JOD'.tr}'.tr, ),
style: AppStyle.title, CupertinoActionSheetAction(
), onPressed: () {
], controller.updateSelectedAmount(
box.read(BoxName.countryCode) == 'Egypt' ? 1000 : 50,
);
showPaymentOptions(context, controller);
},
child: Text(
box.read(BoxName.countryCode) == 'Egypt'
? '1000 ${'LE'.tr} = 1100 ${'LE'.tr}'
: '50 ${'JOD'.tr}',
),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
controller.changePromoSheetDialogue();
},
child: Text('Cancel'.tr),
),
)
: const SizedBox(),
),
);
}
}
// class PassengerWalletDialog extends StatelessWidget {
// const PassengerWalletDialog({
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// return GetBuilder<PaymentController>(
// builder: (controller) {
// return Positioned(
// top: Get.height * .1,
// right: Get.width * .15,
// left: Get.width * .15,
// bottom: Get.height * .1,
// child: controller.isPromoSheetDialogue
// ? Container()
// : SizedBox
// .shrink(), // If condition is false, return an empty widget
// );
// },
// );
// }
// }
void showPaymentBottomSheet(BuildContext context) {
final controller = Get.find<PaymentController>();
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
Get.back();
return false;
},
child: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Select Payment Amount'.tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
const SizedBox(height: 16.0),
// Payment Options List
_buildPaymentOption(
context: context,
controller: controller,
amount: box.read(BoxName.countryCode) == 'Egypt' ? 100 : 10,
bonusAmount: 0,
currency: box.read(BoxName.countryCode) == 'Egypt'
? 'LE'.tr
: 'JOD'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: box.read(BoxName.countryCode) == 'Egypt' ? 200 : 20,
bonusAmount: box.read(BoxName.countryCode) == 'Egypt' ? 5 : 1,
currency: box.read(BoxName.countryCode) == 'Egypt'
? 'LE'.tr
: 'JOD'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: box.read(BoxName.countryCode) == 'Egypt' ? 400 : 40,
bonusAmount:
box.read(BoxName.countryCode) == 'Egypt' ? 15 : 2.5,
currency: box.read(BoxName.countryCode) == 'Egypt'
? 'LE'.tr
: 'JOD'.tr,
),
const SizedBox(height: 8.0),
_buildPaymentOption(
context: context,
controller: controller,
amount: box.read(BoxName.countryCode) == 'Egypt' ? 1000 : 50,
bonusAmount: box.read(BoxName.countryCode) == 'Egypt' ? 100 : 6,
currency: box.read(BoxName.countryCode) == 'Egypt'
? 'LE'.tr
: 'JOD'.tr,
),
const SizedBox(height: 16.0),
TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr),
),
],
),
),
);
},
);
}
Widget _buildPaymentOption({
required BuildContext context,
required PaymentController controller,
required int amount,
required double bonusAmount,
required String currency,
}) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
controller.updateSelectedAmount(amount);
Get.back();
showPaymentOptions(context, controller);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
bonusAmount > 0
? '${'Pay'.tr} $amount $currency, ${'Get'.tr} ${amount + bonusAmount} $currency'
: '$amount $currency',
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
),
);
}
void showPaymentOptions(BuildContext context, PaymentController controller) {
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: Text('Payment Options'.tr),
actions: [
box.read(BoxName.countryCode) == 'Egypt'
? CupertinoActionSheetAction(
child: Text('💳 Pay with Credit Card'.tr),
onPressed: () {
if (controller.selectedAmount != 0) {
controller.payWithPayMob(
context,
controller.selectedAmount.toString(),
box.read(BoxName.countryCode) == 'Egypt' ? 'EGP' : 'JOD',
() async {
await controller.addPassengerWallet();
controller.changePromoSheetDialogue();
await controller.getPassengerWallet();
},
);
} else {
Toast.show(context, '⚠️ You need to choose an amount!'.tr,
AppColor.redColor);
}
},
)
: const SizedBox(),
box.read(BoxName.countryCode) != 'Egypt'
? CupertinoActionSheetAction(
child: Text('Pay with PayPal'.tr),
onPressed: () {
if (controller.selectedAmount != 0) {
controller.makePaymentPayPal(context);
} else {
Toast.show(context, 'You will choose one of above!'.tr,
AppColor.redColor);
}
},
)
: const SizedBox(),
box.read(BoxName.phoneWallet) != null
? CupertinoActionSheetAction(
child: Text('💰 Pay with Wallet'.tr),
onPressed: () {
if (controller.selectedAmount != 0) {
controller.isLoading = true;
controller.update();
controller.payWithPayMobWallet(
context,
controller.selectedAmount.toString(),
box.read(BoxName.countryCode) == 'Egypt' ? 'EGP' : 'JOD',
() async {
await controller.addPassengerWallet();
controller.changePromoSheetDialogue();
await controller.getPassengerWallet();
},
);
controller.isLoading = false;
controller.update();
} else {
Toast.show(context, '⚠️ You need to choose an amount!'.tr,
AppColor.redColor);
}
},
)
: CupertinoActionSheetAction(
child: Text('Add wallet phone you use'.tr),
onPressed: () {
Get.dialog(
CupertinoAlertDialog(
title: Text('Insert Wallet phone number'.tr),
content: Column(
children: [
const SizedBox(height: 10),
CupertinoTextField(
controller: controller.walletphoneController,
placeholder: 'Insert Wallet phone number'.tr,
keyboardType: TextInputType.phone,
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 10,
),
), ),
), ],
GestureDetector( ),
onTap: () { actions: [
controller.updateSelectedAmount(20); CupertinoDialogAction(
}, child: Text('Cancel'.tr,
child: Row( style: const TextStyle(
children: [ color: CupertinoColors.destructiveRed)),
Radio(
value:
box.read(BoxName.countryCode) == 'Egypt'
? 200
: 20,
groupValue: controller.selectedAmount,
onChanged: (value) {
controller
.updateSelectedAmount(value as int);
},
),
Text(
box.read(BoxName.countryCode) == 'Egypt'
? '200 ${'LE'.tr} '.tr
: '20 ${'JOD'.tr}'.tr,
style: AppStyle.title,
),
],
)),
GestureDetector(
onTap: () {
controller.updateSelectedAmount(40);
},
child: Row(
children: [
Radio(
value:
box.read(BoxName.countryCode) == 'Egypt'
? 400
: 40,
groupValue: controller.selectedAmount,
onChanged: (value) {
controller
.updateSelectedAmount(value as int);
},
),
Text(
box.read(BoxName.countryCode) == 'Egypt'
? '400 ${'LE'.tr} '.tr
: '40 ${'JOD'.tr}'.tr,
style: AppStyle.title,
),
],
)),
GestureDetector(
onTap: () {
controller.updateSelectedAmount(100);
},
child: Row(
children: [
Radio(
value:
box.read(BoxName.countryCode) == 'Egypt'
? 1000
: 50,
groupValue: controller.selectedAmount,
onChanged: (value) {
controller
.updateSelectedAmount(value as int);
},
),
Text(
box.read(BoxName.countryCode) == 'Egypt'
? '1000 ${'LE'.tr} '.tr
: '50 ${'JOD'.tr}'.tr,
style: AppStyle.title,
),
],
)),
const Spacer(),
box.read(BoxName.countryCode) == 'Egypt'
? const SizedBox()
: MyElevatedButton(
kolor: AppColor.blueColor,
title: '${'Pay with Your'.tr} PayPal',
onPressed: () {
if (controller.selectedAmount != 0) {
controller.makePaymentPayPal(context);
} else {
Toast.show(
context,
'You will choose one of above !'.tr,
AppColor.redColor);
}
},
),
box.read(BoxName.countryCode) == 'Egypt'
? Column(
children: [
MyElevatedButton(
title: '💳 Pay with Credit Card'.tr,
onPressed: () {
if (controller.selectedAmount != 0) {
controller.payWithPayMob(
context,
controller.selectedAmount
.toString(), // Convert int to double
box.read(BoxName.countryCode) ==
'Egypt'
? 'EGP'
: 'JOD',
() async {
await controller
.addPassengerWallet();
controller
.changePromoSheetDialogue();
await controller
.getPassengerWallet();
},
);
} else {
Toast.show(
context,
'⚠️ You need to choose an amount!'.tr,
AppColor.redColor,
);
}
},
),
// Add some spacing between buttons
MyElevatedButton(
kolor: AppColor.yellowColor,
title: '💰 Pay with Wallet'.tr,
onPressed: () {
if (controller.selectedAmount != 0) {
controller.payWithPayMobWallet(
context,
controller.selectedAmount
.toString(), // Convert int to double
box.read(BoxName.countryCode) ==
'Egypt'
? 'EGP'
: 'JOD',
() async {
await controller
.addPassengerWallet();
controller
.changePromoSheetDialogue();
await controller
.getPassengerWallet();
},
);
} else {
Toast.show(
context,
'⚠️ You need to choose an amount!'.tr,
AppColor.redColor,
);
}
},
),
],
)
: MyElevatedButton(
title: 'Pay with Credit Card'.tr,
onPressed: () {
if (controller.selectedAmount != 0) {
controller.makePaymentStripe(
controller.selectedAmount!
.toDouble(), // Convert int to double
box.read(BoxName.countryCode) != 'Egypt'
? 'usd'
: 'jod', () {
controller.addPassengerWallet();
controller.changePromoSheetDialogue();
controller.getPassengerWallet();
});
} else {
Toast.show(
context,
'You will choose one of above !'.tr,
AppColor.redColor);
}
}),
MyElevatedButton(
title: 'Cancel'.tr,
kolor: AppColor.redColor,
onPressed: () { onPressed: () {
controller.changePromoSheetDialogue(); Get.back();
},
),
CupertinoDialogAction(
child: Text('OK'.tr,
style: const TextStyle(
color: CupertinoColors.activeGreen)),
onPressed: () async {
Get.back();
box.write(BoxName.phoneWallet,
controller.walletphoneController.text);
Toast.show(
context,
'Phone Wallet Saved Successfully'.tr,
AppColor.greenColor);
}, },
), ),
], ],
), ),
)) barrierDismissible: false,
: const SizedBox()), );
); },
} ),
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () {
// controller.changePromoSheetDialogue();
Get.back();
},
),
),
);
} }

View File

@@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
@@ -17,7 +18,7 @@ class PaymentHistoryPassengerPage extends StatelessWidget {
body: [ body: [
GetBuilder<PassengerWalletHistoryController>( GetBuilder<PassengerWalletHistoryController>(
builder: (controller) => controller.isLoading builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator() ? const MyCircularProgressIndicator() // iOS-style loading indicator
: controller.archive.isEmpty : controller.archive.isEmpty
? Center( ? Center(
child: Text( child: Text(
@@ -25,37 +26,33 @@ class PaymentHistoryPassengerPage extends StatelessWidget {
style: AppStyle.title, style: AppStyle.title,
), ),
) )
: ListView.builder( : CupertinoListSection.insetGrouped(
itemCount: controller.archive.length, children: List.generate(
itemBuilder: (BuildContext context, int index) { controller.archive.length,
var list = controller.archive[index]; (index) {
return Padding( var list = controller.archive[index];
padding: const EdgeInsets.all(4), return CupertinoListTile(
child: Container( backgroundColor: double.parse(list['balance']) < 0
decoration: BoxDecoration( ? AppColor.redColor.withOpacity(.2)
color: double.parse(list['balance']) < 0 : AppColor.greenColor.withOpacity(.2),
? AppColor.redColor.withOpacity(.4) title: Text(
: AppColor.greenColor.withOpacity(.4)), list['balance'],
child: Padding( style: AppStyle.title.copyWith(
padding: const EdgeInsets.all(8.0), color: CupertinoColors.black,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
list['balance'],
style: AppStyle.title,
),
Text(
list['created_at'],
style: AppStyle.title,
),
],
), ),
), ),
), additionalInfo: Text(
); list['created_at'],
}, style: AppStyle.title.copyWith(
fontSize: 12,
color: CupertinoColors.systemGrey,
),
),
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
);
},
),
), ),
) )
], ],

View File

@@ -1,64 +1,199 @@
import 'package:flutter/material.dart'; import 'dart:convert';
import 'package:get/get.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:SEFER/views/widgets/mycircular.dart';
import '../../../controller/home/profile/complaint_controller.dart'; import 'package:SEFER/constant/colors.dart';
import '../../widgets/elevated_btn.dart'; import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/controller/home/profile/complaint_controller.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'dart:io';
import '../../../controller/functions/audio_record1.dart';
class ComplaintPage extends StatelessWidget { class ComplaintPage extends StatelessWidget {
// Rename class final ComplaintController complaintController =
ComplaintPage({super.key}); Get.put(ComplaintController());
ComplaintController complaintController = final AudioRecorderController audioRecorderController =
Get.put(ComplaintController()); // Update controller instance Get.put(AudioRecorderController());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MyScafolld( return GetBuilder<ComplaintController>(builder: (complaintController) {
title: 'Complaint'.tr, return CupertinoPageScaffold(
body: [ navigationBar: CupertinoNavigationBar(
Padding( middle: Text('Complaint'.tr, style: AppStyle.title),
padding: const EdgeInsets.all(26),
child: Form(
key: complaintController.formKey,
child: Column(
children: [
TextFormField(
controller: complaintController.complaintController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: 'Enter your complaint here'.tr,
labelText: 'Complaint'.tr, // Update label
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your complaint.'.tr;
}
return null;
},
),
const SizedBox(height: 20),
complaintController.isLoading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () {
if (complaintController.formKey.currentState!
.validate()) {
complaintController
.addComplaint(); // Update method name
// Clear the complaint form
complaintController.formKey.currentState!.reset();
}
},
title: 'Submit'.tr,
),
],
),
),
), ),
], child: complaintController.isLoading
isleading: true, ? const Center(child: CupertinoActivityIndicator())
); : SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: complaintController.formKey,
child: ListView(
children: [
// Complaint Text Field
CupertinoFormSection(
header: Text('Submit Your Complaint'.tr),
children: [
CupertinoTextField(
controller:
complaintController.complaintController,
placeholder: 'Enter your complaint here'.tr,
padding: const EdgeInsets.symmetric(
vertical: 12, horizontal: 16),
maxLines: 5,
decoration: BoxDecoration(
border: Border.all(
color: CupertinoColors.systemGrey4),
borderRadius: BorderRadius.circular(10),
color: CupertinoColors.white,
),
style: AppStyle.subtitle,
),
],
),
const SizedBox(height: 24),
// FutureBuilder to load recorded audio files
FutureBuilder<List<String>>(
future: audioRecorderController.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CupertinoActivityIndicator());
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}',
style: AppStyle.subtitle);
} else if (snapshot.hasData &&
snapshot.data!.isEmpty) {
return Text('No audio files recorded.'.tr,
style: AppStyle.subtitle);
}
// List of recorded audio files
return CupertinoFormSection(
header: Text('attach audio of complain'.tr),
children: snapshot.data!.map((audioFilePath) {
final audioFile = File(audioFilePath);
return CupertinoListTile(
title: Text(audioFilePath.split('/').last,
style: AppStyle.title),
trailing: const Icon(
CupertinoIcons.play_arrow,
color: AppColor.accentColor),
onTap: () async {
MyDialogContent().getDialog(
'be sure'.tr,
Text('attach correct audio'.tr),
() async {
await complaintController
.uploadAudioFile(audioFile);
},
);
},
);
}).toList(),
);
},
),
const SizedBox(height: 24),
// Trip Details Section
CupertinoFormSection(
header: Text('Trip Details'.tr),
children: [
CupertinoListTile(
title: complaintController.feedBack.isEmpty
? Text('No Ride found yet'.tr)
: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Date'.tr}: ${complaintController.feedBack[0]['date']}',
style: AppStyle.title),
Text(
'${'Price'.tr}: ${complaintController.feedBack[0]['price']}',
style: AppStyle.title),
],
),
),
],
),
const SizedBox(height: 24),
CupertinoFormSection(
header: Text('SEFER answer'.tr),
children: [
SizedBox(
height: 100,
child: ListView(
children: [
// Check if passengerReport is not null
if (complaintController.passengerReport !=
null)
// Access the 'solution' key safely
Text(
complaintController
.passengerReport!['solution']
?.toString() ??
'No solution available',
style: AppStyle.title,
)
else
const SizedBox(), // Fallback if passengerReport is null
],
),
),
],
),
const SizedBox(height: 24),
// Submit Button
CupertinoButton(
color: AppColor.blueColor,
padding: const EdgeInsets.symmetric(
vertical: 14, horizontal: 30),
onPressed: () async {
if (complaintController.formKey.currentState!
.validate()) {
if (complaintController.audioLink.toString() ==
'') {
MyDialogContent().getDialog(
'title',
Text(
'the audio file not uploaded yet \nDo you want to upload without audio file'
.tr), () async {
await complaintController.geminiAudio(
jsonEncode(complaintController.feedBack),
complaintController.audioLink,
complaintController
.complaintController.text);
complaintController.formKey.currentState!
.reset();
});
Get.back();
} else {
await complaintController.geminiAudio(
jsonEncode(complaintController.feedBack),
complaintController.audioLink,
complaintController
.complaintController.text);
complaintController.formKey.currentState!
.reset();
}
complaintController.addComplaint();
}
},
child: Text('Submit'.tr, style: AppStyle.title),
),
],
),
),
),
),
);
});
} }
} }

View File

@@ -393,92 +393,6 @@ class CountryPicker extends StatelessWidget {
} }
} }
ListTile changeCountry(List<String> countryOptions) {
return ListTile(
leading: const Icon(Icons.location_city_outlined),
title: Text(
'Change Country'.tr,
style: AppStyle.headTitle2,
),
subtitle: Text(
'You can change the Country to get all features'.tr,
style: AppStyle.title,
),
onTap: () => Get.to(MyScafolld(
title: 'Change Country'.tr,
// body: [],
body: [
GetBuilder<ProfileController>(builder: (controller) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: ListView(
children: [
const SizedBox(
height: 20,
),
Text(
"Select Your Country".tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
// const SizedBox(
// height: 20,
// ),
Padding(
padding: const EdgeInsets.all(10),
child: Text(
"To ensure you receive the most accurate information for your location, please select your country below. This will help tailor the app experience and content to your country."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
SizedBox(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setCountry(countryOptions[index]);
box.write(BoxName.countryCode,
countryOptions[index]); // Save in English
},
children: List.generate(
countryOptions.length,
(index) => Center(
child: Text(
countryOptions[index]
.tr, // Display translated if not English
style: AppStyle.title,
),
),
),
),
),
MyElevatedButton(
title:
'Select Country'.tr, // Use translated text for button
onPressed: () async {
// No conversion needed
box.write(
BoxName.countryCode, //
controller
.selectedCountry); // Already saved in English
Get.snackbar(controller.selectedCountry.toString().tr, '',
backgroundColor: AppColor.greenColor);
// Get.back();//
// Get.back();
},
)
],
),
);
})
],
isleading: true)),
);
}
class CountryPickerFromSetting extends StatelessWidget { class CountryPickerFromSetting extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController()); final ProfileController controller = Get.put(ProfileController());
final LoginController loginController = Get.put(LoginController()); final LoginController loginController = Get.put(LoginController());
@@ -548,7 +462,8 @@ class CountryPickerFromSetting extends StatelessWidget {
MyElevatedButton( MyElevatedButton(
title: 'Select Country'.tr, // Use translated text for button title: 'Select Country'.tr, // Use translated text for button
onPressed: () async { onPressed: () async {
// No conversion needed loginController.saveCountryCode(controller.selectedCountry
.toString()); // No conversion needed
box.write( box.write(
BoxName.countryCode, // BoxName.countryCode, //
controller.selectedCountry); // Already saved in English controller.selectedCountry); // Already saved in English

View File

@@ -1,5 +1,7 @@
import 'package:animated_text_kit/animated_text_kit.dart'; import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/controller/home/profile/promos_controller.dart'; import 'package:SEFER/controller/home/profile/promos_controller.dart';
import 'package:SEFER/views/widgets/my_scafold.dart'; import 'package:SEFER/views/widgets/my_scafold.dart';
@@ -18,160 +20,211 @@ class PromosPassengerPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Get.put(PromosController()); Get.put(PromosController());
return MyScafolld( return MyScafolld(
title: 'Promos For today'.tr, title: "Promos For Today".tr,
isleading: true, isleading: true,
body: [ body: [
GetBuilder<PromosController>( GetBuilder<PromosController>(
builder: (orderHistoryController) => orderHistoryController.isLoading builder: (orderHistoryController) => orderHistoryController.isLoading
? const MyCircularProgressIndicator() ? const MyCircularProgressIndicator()
: ListView.builder( : ListView.builder(
itemCount: orderHistoryController.promoList.length + itemCount: orderHistoryController
1, // Adding 1 for the ad .promoList.length, // Adding 1 for the ad
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
if (index == 0) { // if (index == 0) {
// Ad at the beginning // // Ad at the beginning
return Padding( // return Padding(
padding: const EdgeInsets.all(8.0), // padding: const EdgeInsets.all(8.0),
child: Container( // child: Container(
height: 120, // Adjust the height of the ad container // height: 120, // Adjust the height of the ad container
decoration: BoxDecoration( // decoration: BoxDecoration(
color: // color:
Colors.grey[200], // Background color for the ad // Colors.grey[200], // Background color for the ad
borderRadius: BorderRadius.circular(10), // borderRadius: BorderRadius.circular(10),
), // ),
child: Center( // child: Center(
child: Container( // child: Container(
decoration: AppStyle.boxDecoration, // decoration: AppStyle.boxDecoration,
height: Get.height * .19, // height: Get.height * .19,
child: ListView( // child: ListView(
scrollDirection: Axis.horizontal, // scrollDirection: Axis.horizontal,
children: [ // children: [
PointsCaptain( // PointsCaptain(
kolor: AppColor.greyColor, // kolor: AppColor.greyColor,
pricePoint: // pricePoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? 5 // ? 5
: 100, // : 100,
countPoint: // countPoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? '300' // ? '300'
: '100', // : '100',
), // ),
PointsCaptain( // PointsCaptain(
kolor: AppColor.bronze, // kolor: AppColor.bronze,
pricePoint: // pricePoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? 10 // ? 10
: 200, // : 200,
countPoint: // countPoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? '1040' // ? '1040'
: '210', // : '210',
), // ),
PointsCaptain( // PointsCaptain(
kolor: AppColor.goldenBronze, // kolor: AppColor.goldenBronze,
pricePoint: // pricePoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? 22 // ? 22
: 400, // : 400,
countPoint: // countPoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? '2300' // ? '2300'
: '450', // : '450',
), // ),
PointsCaptain( // PointsCaptain(
kolor: AppColor.gold, // kolor: AppColor.gold,
pricePoint: // pricePoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? 50 // ? 50
: 1000, // : 1000,
countPoint: // countPoint:
box.read(BoxName.countryCode) == // box.read(BoxName.countryCode) ==
'Jordan' // 'Jordan'
? '55000' // ? '55000'
: '1200', // : '1200',
), // ),
], // ],
)), // )),
), // ),
// ),
// );
// } else {
// Promo items
final rides = orderHistoryController.promoList[index];
return Padding(
padding: const EdgeInsets.all(12.0),
child: Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color:
CupertinoColors.systemGrey.withOpacity(0.5),
blurRadius: 8,
offset: Offset(0, 4),
),
],
), ),
); child: Padding(
} else { padding: const EdgeInsets.all(16.0),
// Promo items child: Column(
final rides = orderHistoryController.promoList[index - 1]; crossAxisAlignment: CrossAxisAlignment.start,
return Padding( children: [
padding: const EdgeInsets.all(8.0), Row(
child: Container( mainAxisAlignment:
decoration: AppStyle.boxDecoration, MainAxisAlignment.spaceBetween,
child: Padding( children: [
padding: const EdgeInsets.all(8.0), Column(
child: Column( crossAxisAlignment:
children: [ CrossAxisAlignment.start,
Row( children: [
mainAxisAlignment: GestureDetector(
MainAxisAlignment.spaceBetween, onTap: () {},
children: [ child: AnimatedTextKit(
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
AnimatedTextKit(
animatedTexts: [ animatedTexts: [
ScaleAnimatedText( ScaleAnimatedText(
rides['promo_code'], rides['promo_code'],
textStyle: AppStyle.title), textStyle:
AppStyle.title.copyWith(
fontSize:
32, // Increased font size for emphasis
color:
CupertinoColors.activeBlue,
fontWeight: FontWeight.bold,
),
),
WavyAnimatedText( WavyAnimatedText(
rides['promo_code'], rides['promo_code'],
textStyle: AppStyle.title), textStyle:
FlickerAnimatedText( AppStyle.title.copyWith(
rides['promo_code'], fontSize:
textStyle: AppStyle.title), 32, // Increased font size for emphasis
WavyAnimatedText( color:
rides['promo_code'], CupertinoColors.activeBlue,
textStyle: AppStyle.title), fontWeight: FontWeight.bold,
),
),
], ],
isRepeatingAnimation: true, isRepeatingAnimation: true,
onTap: () {},
), ),
Text( ),
rides['description'], const SizedBox(height: 8),
style: AppStyle.title, // Description Text
Text(
rides['description'],
style: AppStyle.title.copyWith(
fontSize: 22,
color: CupertinoColors.systemGrey,
), ),
], ),
],
),
Column(
children: [
// Only displaying end date
Text(
'${'Valid Until:'.tr} ${rides['validity_end_date']}',
style: AppStyle.subtitle.copyWith(
fontWeight: FontWeight.bold,
fontSize: 20,
color: CupertinoColors.systemGrey,
),
),
],
),
],
),
// const SizedBox(height: 16),
// Copy Promo Text
Center(
child: GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: rides['promo_code']));
Get.snackbar(
'Promo Copied!'.tr,
'You have copied the promo code.'.tr,
snackPosition: SnackPosition.BOTTOM,
backgroundColor:
CupertinoColors.systemGrey,
colorText: CupertinoColors.white,
);
},
child: Text(
'Copy Code'.tr,
textAlign: TextAlign.center,
style: AppStyle.headTitle2.copyWith(
color: CupertinoColors.systemBlue,
fontWeight: FontWeight.bold,
), ),
Column( ),
children: [
Text(
rides['validity_start_date'],
style: AppStyle.title,
),
Text(
rides['validity_end_date'],
style: AppStyle.title,
),
],
),
],
), ),
Text( ),
'Copy this Promo to use it in your Ride!'.tr, ],
textAlign: TextAlign.center,
style: AppStyle.headTitle2
.copyWith(color: AppColor.accentColor),
)
],
),
), ),
), ),
); ),
} );
// }
}, },
), ),
) )

View File

@@ -1,10 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:SEFER/constant/colors.dart'; import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart'; import 'package:SEFER/constant/style.dart';
import '../../controller/notification/passenger_notification_controller.dart'; import '../../controller/notification/passenger_notification_controller.dart';
import '../widgets/elevated_btn.dart';
import '../widgets/my_scafold.dart'; import '../widgets/my_scafold.dart';
import '../widgets/mycircular.dart'; import '../widgets/mycircular.dart';
@@ -19,64 +19,82 @@ class NotificationPage extends StatelessWidget {
title: 'Notifications', title: 'Notifications',
body: [ body: [
GetBuilder<PassengerNotificationController>( GetBuilder<PassengerNotificationController>(
builder: (notificationCaptainController) => builder: (notificationCaptainController) => notificationCaptainController
notificationCaptainController.isloading .isloading
? const MyCircularProgressIndicator() ? const MyCircularProgressIndicator() // iOS-style loading indicator
: SafeArea( : SafeArea(
child: ListView.builder( child: ListView.builder(
itemCount: notificationCaptainController itemCount: notificationCaptainController
.notificationData['message'].length, .notificationData['message'].length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
if (notificationCaptainController if (notificationCaptainController
.notificationData['message'] == .notificationData['message'] ==
"No notification data found") { "No notification data found") {
Get.defaultDialog(); Get.defaultDialog(
} title: 'No Notifications'.tr,
var res = notificationCaptainController content: Text(
.notificationData['message'][index]; 'No notification data found.'.tr,
return Card( ),
elevation: 4, );
color: res['isShown'] == 'true' }
? AppColor.secondaryColor.withOpacity(.5) var res = notificationCaptainController
: AppColor.secondaryColor.withOpacity(.9), .notificationData['message'][index];
child: ListTile( return Padding(
onTap: () { padding: const EdgeInsets.symmetric(
Get.defaultDialog( horizontal: 8, vertical: 4),
title: res['title'], child: CupertinoListTile(
titleStyle: AppStyle.title, backgroundColor: res['isShown'] == 'true'
content: SizedBox( ? AppColor.secondaryColor.withOpacity(.2)
width: Get.width * .8, : AppColor.secondaryColor.withOpacity(.8),
// height: Get.height * .4, leading: res['isShown'] == 'true'
child: Text( ? const Icon(CupertinoIcons.bell_slash_fill)
res['body'], : const Icon(CupertinoIcons.bell_fill),
style: AppStyle.title, title: Text(
), res['title'],
), style: AppStyle.title.copyWith(
confirm: MyElevatedButton( color: CupertinoColors.black,
title: 'Ok', ),
onPressed: () { ),
notificationCaptainController subtitle: Text(
.updateNotification( res['body'],
res['id'].toString()); style: AppStyle.subtitle.copyWith(
})); color: CupertinoColors.systemGrey,
}, ),
leading: res['isShown'] == 'true' ),
? const Icon( onTap: () {
Icons.notifications_off_outlined) showCupertinoDialog(
: const Icon(Icons.notifications_active), context: context,
title: Text( builder: (BuildContext context) {
res['title'], return CupertinoAlertDialog(
style: AppStyle.title, title: Text(
), res['title'],
subtitle: Text( style: AppStyle.title,
res['body'], ),
style: AppStyle.subtitle, content: Text(
), res['body'],
), style: AppStyle.subtitle,
),
actions: [
CupertinoDialogAction(
child: const Text('Ok'),
onPressed: () {
notificationCaptainController
.updateNotification(
res['id'].toString());
Get.back();
},
),
],
);
},
); );
}, },
), ),
)) );
},
),
),
)
], ],
); );
} }

View File

@@ -1,5 +1,6 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -11,34 +12,98 @@ import 'elevated_btn.dart';
class MyDialog extends GetxController { class MyDialog extends GetxController {
void getDialog(String title, String? midTitle, VoidCallback onPressed) { void getDialog(String title, String? midTitle, VoidCallback onPressed) {
final textToSpeechController = Get.put(TextToSpeechController()); final textToSpeechController = Get.put(TextToSpeechController());
Get.defaultDialog(
title: title, Get.dialog(
titleStyle: AppStyle.title, BackdropFilter(
barrierDismissible: false, filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
middleTextStyle: AppStyle.title, child: CupertinoAlertDialog(
content: Column( title: Text(
children: [ title,
IconButton( style: AppStyle.title.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
content: Column(
children: [
CupertinoButton(
onPressed: () async { onPressed: () async {
await textToSpeechController.speakText(title ?? midTitle!); await textToSpeechController.speakText(title ?? midTitle!);
}, },
icon: const Icon(Icons.headphones)), child: const Icon(CupertinoIcons.headphones,
Text( color: AppColor.primaryColor),
midTitle!, ),
style: AppStyle.title, Text(
) midTitle!,
style: AppStyle.title.copyWith(fontSize: 16),
),
],
),
actions: [
CupertinoDialogAction(
child: const Text('Cancel',
style: TextStyle(color: AppColor.redColor)),
onPressed: () {
Get.back();
},
),
CupertinoDialogAction(
onPressed: onPressed,
child: Text('OK'.tr,
style: const TextStyle(color: AppColor.greenColor)),
),
], ],
), ),
confirm: MyElevatedButton( ),
title: 'Ok'.tr, barrierDismissible: false,
onPressed: onPressed, );
kolor: AppColor.greenColor, }
), }
cancel: MyElevatedButton(
title: 'Cancel', class MyDialogContent extends GetxController {
kolor: AppColor.redColor, void getDialog(String title, Widget? content, VoidCallback onPressed) {
onPressed: () { final textToSpeechController = Get.put(TextToSpeechController());
Get.back();
})); Get.dialog(
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: CupertinoAlertDialog(
title: Text(
title,
style: AppStyle.title.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
content: Column(
children: [
CupertinoButton(
onPressed: () async {
await textToSpeechController.speakText(title);
},
child: const Icon(CupertinoIcons.speaker_2,
color: AppColor.primaryColor),
),
content!
],
),
actions: [
CupertinoDialogAction(
child: const Text('Cancel',
style: TextStyle(color: AppColor.redColor)),
onPressed: () {
Get.back();
},
),
CupertinoDialogAction(
onPressed: onPressed,
child: Text('OK'.tr,
style: const TextStyle(color: AppColor.greenColor)),
),
],
),
),
barrierDismissible: false,
);
} }
} }

View File

@@ -27,7 +27,7 @@ class MyTextForm extends StatelessWidget {
children: [ children: [
Text( Text(
label.tr, label.tr,
style: TextStyle( style: const TextStyle(
color: CupertinoColors.label, color: CupertinoColors.label,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

View File

@@ -12,8 +12,8 @@ class MyCircularProgressIndicator extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return Center(
child: Container( child: Container(
width: 110, width: 140,
height: 110, height: 140,
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor, color: backgroundColor,
shape: BoxShape.circle, shape: BoxShape.circle,
@@ -21,13 +21,11 @@ class MyCircularProgressIndicator extends StatelessWidget {
child: Stack( child: Stack(
children: [ children: [
const Center(child: CircularProgressIndicator()), const Center(child: CircularProgressIndicator()),
Column( Image.asset(
children: [ 'assets/images/logo.gif',
Align( width: 140,
alignment: Alignment.center, height: 140,
child: Image.asset('assets/images/logo.gif'), fit: BoxFit.contain,
),
],
), ),
], ],
), ),

View File

@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
SnackbarController mySnackeBarError(String message) {
return Get.snackbar(
'Error'.tr,
message,
backgroundColor: AppColor.redColor.withOpacity(0.9),
colorText: AppColor.secondaryColor,
icon: const Icon(Icons.error, color: AppColor.secondaryColor),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: const EdgeInsets.all(10),
borderRadius: 10,
animationDuration: const Duration(milliseconds: 500),
forwardAnimationCurve: Curves.easeOutBack,
reverseAnimationCurve: Curves.easeInBack,
titleText: Text(
'Error'.tr,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 16,
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
),
);
}
SnackbarController mySnackbarSuccess(String message) {
return Get.snackbar(
'Success'.tr,
message,
backgroundColor: AppColor.greenColor
.withOpacity(0.9), // Assuming green color for success
colorText: AppColor.secondaryColor,
icon: const Icon(Icons.check_circle, color: AppColor.secondaryColor),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: const EdgeInsets.all(10),
borderRadius: 10,
animationDuration: const Duration(milliseconds: 500),
forwardAnimationCurve: Curves.easeOutBack,
reverseAnimationCurve: Curves.easeInBack,
titleText: Text(
'Success'.tr,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 16,
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
),
),
);
}

View File

@@ -27,6 +27,7 @@ import sqflite
import url_launcher_macos import url_launcher_macos
import video_player_avfoundation import video_player_avfoundation
import wakelock_plus import wakelock_plus
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
@@ -51,4 +52,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
} }

View File

@@ -1,7 +1,7 @@
import Cocoa import Cocoa
import FlutterMacOS import FlutterMacOS
@NSApplicationMain @main
class AppDelegate: FlutterAppDelegate { class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true return true

View File

@@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "67.0.0" version: "72.0.0"
_flutterfire_internals: _flutterfire_internals:
dependency: transitive dependency: transitive
description: description:
@@ -17,14 +17,19 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.41" version: "1.3.41"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.4.1" version: "6.7.0"
animated_text_kit: animated_text_kit:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -109,18 +114,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.11" version: "2.4.12"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.3.1" version: "7.3.2"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -141,26 +146,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cached_network_image name: cached_network_image
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.0" version: "3.3.1"
cached_network_image_platform_interface: cached_network_image_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: cached_network_image_platform_interface name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.1" version: "4.0.0"
cached_network_image_web: cached_network_image_web:
dependency: transitive dependency: transitive
description: description:
name: cached_network_image_web name: cached_network_image_web
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.2.0"
calendar_builder: calendar_builder:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -189,10 +194,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: chewie name: chewie
sha256: "8210c6e8702ddae9b3337aad0d539a9ff128cc4a5baaadc0174edbd0f99b0125" sha256: e53da939709efb9aad0f3d72a69a8d05f889168b7a138af60ce78bab5c94b135
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.4" version: "1.8.1"
cli_util: cli_util:
dependency: transitive dependency: transitive
description: description:
@@ -269,10 +274,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.6" version: "2.3.7"
dbus: dbus:
dependency: transitive dependency: transitive
description: description:
@@ -482,10 +487,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_cache_manager name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" sha256: ceff65d74d907b1b772e22cf04daad60fb472461638977d9fae8b00a63e01e3d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.1" version: "3.3.3"
flutter_confetti:
dependency: "direct main"
description:
name: flutter_confetti
sha256: "22fc66984c17aea73e3cd300666609c4581176c00c31d4513b1e16758f75cb08"
url: "https://pub.dev"
source: hosted
version: "0.3.4"
flutter_contacts:
dependency: "direct main"
description:
name: flutter_contacts
sha256: "388d32cd33f16640ee169570128c933b45f3259bddbfae7a100bb49e5ffea9ae"
url: "https://pub.dev"
source: hosted
version: "1.1.9+2"
flutter_font_icons: flutter_font_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -836,10 +857,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: google_maps_flutter_android name: google_maps_flutter_android
sha256: "075f550650a907a85d00d1e48f135e9cc326f1519652eb5f8caafebacabf7727" sha256: bccf64ccbb2ea672dc62a61177b315a340af86b0228564484b023657544a3fd5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.14.5" version: "2.14.11"
google_maps_flutter_ios: google_maps_flutter_ios:
dependency: transitive dependency: transitive
description: description:
@@ -852,10 +873,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: google_maps_flutter_platform_interface name: google_maps_flutter_platform_interface
sha256: "8ba5daee8b5d2230fea17a7903047ce4dc45fb1f0726356a1d8eb541e30c7d05" sha256: a951981c22d790848efb9f114f81794945bc5c06bc566238a419a92f110af6cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.1" version: "2.9.5"
google_maps_flutter_web: google_maps_flutter_web:
dependency: transitive dependency: transitive
description: description:
@@ -961,7 +982,7 @@ packages:
source: hosted source: hosted
version: "3.2.1" version: "3.2.1"
http_parser: http_parser:
dependency: transitive dependency: "direct main"
description: description:
name: http_parser name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
@@ -1012,10 +1033,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56" sha256: a26dc9a03fe042440c1e4be554fb0fceae2bf6d887d7467fc48c688fa4a81889
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.12+12" version: "0.8.12+7"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@@ -1164,10 +1185,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: local_auth_android name: local_auth_android
sha256: e99c44ca0bce08f26f25e2a2e07d3b443d69986e1c3acf67c1449f7d847e3625 sha256: "33fcebe9c3cf1bb0033bc85caed354c1e75ff7f7670918a571bd3152a2b65bf4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.43" version: "1.0.42"
local_auth_darwin: local_auth_darwin:
dependency: transitive dependency: transitive
description: description:
@@ -1224,6 +1245,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
macros:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@@ -1249,7 +1278,7 @@ packages:
source: hosted source: hosted
version: "1.15.0" version: "1.15.0"
mime: mime:
dependency: transitive dependency: "direct main"
description: description:
name: mime name: mime
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
@@ -1396,10 +1425,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.2" version: "4.2.1"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
@@ -1640,10 +1669,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.4" version: "2.5.4+2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -1704,10 +1733,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: synchronized name: synchronized
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0+1" version: "3.2.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -1725,7 +1754,7 @@ packages:
source: hosted source: hosted
version: "0.7.2" version: "0.7.2"
timezone: timezone:
dependency: "direct main" dependency: transitive
description: description:
name: timezone name: timezone
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
@@ -1784,10 +1813,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79 sha256: "95d8027db36a0e52caf55680f91e33ea6aa12a3ce608c90b06f4e429a21067ac"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.9" version: "6.3.5"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@@ -1904,10 +1933,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: video_player_android name: video_player_android
sha256: e343701aa890b74a863fa460f5c0e628127ed06a975d7d9af6b697133fb25bdf sha256: fdc0331ce9f808cc2714014cb8126bd6369943affefd54f8fdab0ea0bb617b7f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.7.1" version: "2.5.2"
video_player_avfoundation: video_player_avfoundation:
dependency: transitive dependency: transitive
description: description:
@@ -1992,10 +2021,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" sha256: ec81f57aa1611f8ebecf1d2259da4ef052281cb5ad624131c93546c79ccc7736
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.8.0" version: "4.9.0"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: transitive
description: description:
@@ -2016,10 +2045,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: "9c62cc46fa4f2d41e10ab81014c1de470a6c6f26051a2de32111b2ee55287feb" sha256: "1942a12224ab31e9508cf00c0c6347b931b023b8a4f0811e5dec3b06f94f117d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.14.0" version: "3.15.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
@@ -2032,10 +2061,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.4" version: "1.1.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@@ -2061,5 +2090,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.4.0 <4.0.0" dart: ">=3.5.0 <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.24.0"

View File

@@ -61,7 +61,11 @@ dependencies:
package_info_plus: ^8.0.0 package_info_plus: ^8.0.0
uni_links: ^0.5.1 uni_links: ^0.5.1
googleapis_auth: ^1.6.0 googleapis_auth: ^1.6.0
timezone: ^0.9.4 flutter_confetti: ^0.3.0
# intl_phone_field: ^3.1.0
flutter_contacts: ^1.1.8
mime: ^1.0.6
http_parser: ^4.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

BIN
ride9-11.zip Normal file

Binary file not shown.

View File

@@ -1,11 +1,11 @@
# This file is used to configure the Shorebird updater used by your app. # This file is used to configure the Shorebird updater used by your app.
# Learn more at https://docs.shorebird.dev # Learn more at https://docs.shorebird.dev
# This file should be checked into version control. # This file does not contain any sensitive information and should be checked into version control.
# This is the unique identifier assigned to your app. # Your app_id is the unique identifier assigned to your app.
# Your app_id is not a secret and is just used to identify your app # It is used to identify your app when requesting patches from Shorebird's servers.
# when requesting patches from Shorebird's servers. # It is not a secret and can be shared publicly.
app_id: 8a571d7f-dfbf-4a65-be62-17eed08cbd5c app_id: 496cb3ac-c25c-455d-9d2e-5ebbea13ab03
# auto_update controls if Shorebird should automatically update in the background on launch. # auto_update controls if Shorebird should automatically update in the background on launch.
# If auto_update: false, you will need to use package:shorebird_code_push to trigger updates. # If auto_update: false, you will need to use package:shorebird_code_push to trigger updates.