├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── najeeb │ │ │ │ └── aslan │ │ │ │ └── issue │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-hdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-mdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night-xhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── raw │ │ │ └── notification_sound.wav │ │ │ ├── values-ar │ │ │ └── strings.xml │ │ │ ├── values-en │ │ │ └── strings.xml │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── dark_splash.png ├── fonts │ ├── IBMPlexSansArabicBold.ttf │ ├── IBMPlexSansArabicMedium.ttf │ └── IBMPlexSansArabicRegular.ttf ├── images │ ├── arabic_language.png │ ├── backup_automatically.png │ ├── backup_manually.png │ ├── download_backup.png │ ├── folders.png │ ├── googleDrive.png │ ├── logo.png │ ├── logo_without_background.png │ ├── onboarding_add_criminals.png │ ├── onboarding_save_your_time.png │ └── profile.jpg ├── splash.png ├── svg │ ├── apple_logo.svg │ ├── apple_logo_light.svg │ ├── face_id.svg │ ├── failure.svg │ ├── forgot_password.svg │ ├── google_logo.svg │ ├── not_found_users.json │ ├── onboarding_notifications.svg │ ├── onboarding_upload_backups.svg │ ├── question.svg │ ├── saudi_arabia_flag.svg │ ├── success.svg │ ├── us_flag.svg │ ├── warning.svg │ └── whatsapp_logo.svg └── translations │ ├── ar-SA.json │ └── en-US.json ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Resources │ └── notification_sound.wav ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-20@2x.png │ │ │ ├── AppIcon-20@2x~ipad.png │ │ │ ├── AppIcon-20@3x.png │ │ │ ├── AppIcon-20~ipad.png │ │ │ ├── AppIcon-29.png │ │ │ ├── AppIcon-29@2x.png │ │ │ ├── AppIcon-29@2x~ipad.png │ │ │ ├── AppIcon-29@3x.png │ │ │ ├── AppIcon-29~ipad.png │ │ │ ├── AppIcon-40@2x.png │ │ │ ├── AppIcon-40@2x~ipad.png │ │ │ ├── AppIcon-40@3x.png │ │ │ ├── AppIcon-40~ipad.png │ │ │ ├── AppIcon-60@2x~car.png │ │ │ ├── AppIcon-60@3x~car.png │ │ │ ├── AppIcon-83.5@2x~ipad.png │ │ │ ├── AppIcon@2x.png │ │ │ ├── AppIcon@2x~ipad.png │ │ │ ├── AppIcon@3x.png │ │ │ ├── AppIcon~ios-marketing.png │ │ │ ├── AppIcon~ipad.png │ │ │ └── Contents.json │ │ ├── LaunchBackground.imageset │ │ │ ├── Contents.json │ │ │ ├── background.png │ │ │ └── darkbackground.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── LaunchImageDark.png │ │ │ ├── LaunchImageDark@2x.png │ │ │ ├── LaunchImageDark@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ ├── ar.lproj │ │ ├── InfoPlist.strings │ │ ├── LaunchScreen.strings │ │ └── Main.strings │ └── en.lproj │ │ └── InfoPlist.strings ├── RunnerTests │ └── RunnerTests.swift └── notification_sound.wav ├── lib ├── app.dart ├── core │ ├── constants │ │ ├── app_icons.dart │ │ ├── assets_constants.dart │ │ └── default_settings.dart │ ├── extensions │ │ ├── context_extension.dart │ │ ├── date_time_extension.dart │ │ ├── iterable_extension.dart │ │ └── string_extension.dart │ ├── helpers │ │ ├── helper_user.dart │ │ ├── shared_prefs_service.dart │ │ └── task_scheduler.dart │ ├── networking │ │ ├── handel_firebase_errors.dart │ │ ├── network_info.dart │ │ └── type_response.dart │ ├── router │ │ ├── routes_constants.dart │ │ └── routes_manager.dart │ ├── services │ │ ├── encryption_service.dart │ │ ├── launch_uri.dart │ │ ├── local_auth_service.dart │ │ ├── notifications │ │ │ ├── android_notifications_channel.dart │ │ │ ├── local_notifications.dart │ │ │ └── payload_model.dart │ │ ├── services_locator.dart │ │ └── shard_pdf_service.dart │ ├── theme │ │ ├── app_colors.dart │ │ ├── color_hex.dart │ │ ├── custom_colors.dart │ │ └── theme.dart │ ├── utils │ │ ├── alarms_days.dart │ │ ├── app_theme_and_languages_notifier │ │ │ ├── app_theme_and_languages_cubit.dart │ │ │ └── app_theme_and_languages_state.dart │ │ ├── bloc_observer.dart │ │ └── validate.dart │ └── widgets │ │ ├── accused_input_field.dart │ │ ├── adaptive_them_container.dart │ │ ├── animations │ │ ├── animated_circle_dialog.dart │ │ ├── animation_dialog.dart │ │ ├── animation_switch_language.dart │ │ ├── custom_slide_transition.dart │ │ └── hero │ │ │ ├── custom_rect_tween.dart │ │ │ └── hero_dialog_route.dart │ │ ├── back_icon_button.dart │ │ ├── base_bottom_sheet.dart │ │ ├── base_divider.dart │ │ ├── cache_image.dart │ │ ├── custom_app_bar.dart │ │ ├── custom_button.dart │ │ ├── custom_focused_menu.dart │ │ ├── custom_text_field.dart │ │ ├── custom_toast.dart │ │ ├── custom_upgrade_alert.dart │ │ ├── image │ │ ├── select_image_cubit │ │ │ ├── select_image_cubit.dart │ │ │ └── select_image_state.dart │ │ └── upload_image.dart │ │ ├── not_found_data.dart │ │ ├── percent_indicator.dart │ │ ├── shimmer.dart │ │ └── show_notification_details.dart ├── data │ ├── data_base │ │ └── db_helper.dart │ ├── models │ │ ├── accuse_model.dart │ │ ├── data_trial_mode.dart │ │ ├── notification_model.dart │ │ └── profile_model.dart │ └── repositories │ │ ├── auth_repository.dart │ │ ├── google_drive_repository.dart │ │ ├── profile_repository.dart │ │ └── sync_users_repository.dart ├── features │ ├── accused │ │ ├── accused_cubit │ │ │ ├── accused_cubit.dart │ │ │ └── accused_state.dart │ │ ├── accused_details_view.dart │ │ ├── add_accused_view.dart │ │ ├── logic │ │ │ └── share_accused.dart │ │ └── widgets │ │ │ ├── accused_agent.dart │ │ │ ├── add_accused_bloc_listener.dart │ │ │ ├── alarm_control_panel.dart │ │ │ ├── chart_circle_alarms.dart │ │ │ ├── form_add_accused.dart │ │ │ ├── individual_alarm_control.dart │ │ │ └── popup_menu_details_accused.dart │ ├── auth │ │ ├── auth_cubit │ │ │ ├── auth_cubit.dart │ │ │ └── auth_state.dart │ │ ├── views │ │ │ ├── forget_password_view.dart │ │ │ ├── sign_in_view.dart │ │ │ └── sign_up_view.dart │ │ └── widgets │ │ │ ├── auth_background.dart │ │ │ ├── auth_form.dart │ │ │ ├── background_for_gor_password.dart │ │ │ ├── forgot_password │ │ │ ├── forgot_passowrd_bloc_listener.dart │ │ │ ├── forgot_password_form.dart │ │ │ └── switch_auth_views_button.dart │ │ │ ├── is_have_account.dart │ │ │ ├── register_with_third_party.dart │ │ │ ├── sign_in_bloc_listener.dart │ │ │ ├── sign_up_bloc_listener.dart │ │ │ └── title_auth.dart │ ├── backup │ │ ├── backup_cubit │ │ │ ├── backup_cubit.dart │ │ │ └── backup_state.dart │ │ ├── logic │ │ │ └── auto_upload_backup.dart │ │ ├── views │ │ │ ├── backups_settings_view.dart │ │ │ └── get_backups_view.dart │ │ └── widgets │ │ │ ├── backup_elevated_button.dart │ │ │ ├── backup_header_title.dart │ │ │ ├── backup_list.dart │ │ │ ├── confirm_restor_backup_bottom_sheet.dart │ │ │ ├── let_started_setting_google_drive.dart │ │ │ ├── loading_backups.dart │ │ │ └── select_backup_method.dart │ ├── filter │ │ ├── filter_cubit │ │ │ ├── filter_cubit.dart │ │ │ └── filter_state.dart │ │ ├── views │ │ │ └── filter_view.dart │ │ └── widget │ │ │ ├── filter_and_sort_bottom_sheets.dart │ │ │ ├── filter_app_bar.dart │ │ │ ├── filter_body.dart │ │ │ ├── filter_list_tile.dart │ │ │ └── list_issue_numbers.dart │ ├── home │ │ ├── views │ │ │ ├── home_view.dart │ │ │ └── navigation_bar_view.dart │ │ └── widgets │ │ │ ├── accused_details_card.dart │ │ │ ├── accused_details_card_content.dart │ │ │ ├── base_hero_flip_animation.dart │ │ │ ├── circle_charts_alarm.dart │ │ │ ├── highlight_painter.dart │ │ │ ├── home_app_bar.dart │ │ │ ├── line_chart_alarm.dart │ │ │ ├── material_indicator.dart │ │ │ ├── search_app_bar │ │ │ └── accuse_search_bpp_bar.dart │ │ │ └── trial_mode_overlay.dart │ ├── notifications │ │ ├── notifications_cubit │ │ │ ├── notifications_cubit.dart │ │ │ └── notifications_state.dart │ │ ├── views │ │ │ └── notifications_view.dart │ │ └── widgets │ │ │ └── list_view_notifications.dart │ ├── onboarding │ │ ├── animated_pages.dart │ │ ├── animation_circular_indicator.dart │ │ ├── change_language_icon_button.dart │ │ ├── on_boarding_data.dart │ │ ├── on_boarding_item.dart │ │ └── onboarding_view.dart │ └── settings │ │ ├── profile_cubit │ │ ├── profile_cubit.dart │ │ └── profile_state.dart │ │ ├── views │ │ ├── about_app_view.dart │ │ ├── profile_view.dart │ │ └── setting_view.dart │ │ └── widgets │ │ ├── biometrics_auth.dart │ │ ├── change_language.dart │ │ ├── contact_us.dart │ │ ├── list_tile_setting.dart │ │ ├── logout_button.dart │ │ ├── notification_cleanup.dart │ │ └── profile │ │ ├── confirm_delete_account_bottom_sheet.dart │ │ ├── delete_account_dialog.dart │ │ ├── edit_profile_bottom_sheet_content.dart │ │ ├── profile_bloc_consumer.dart │ │ ├── profile_image_widget.dart │ │ └── update_profile_bloc_listener.dart ├── firebase_options.dart ├── main.dart └── splash_view.dart ├── pubspec.yaml ├── screen_shoots ├── abdout_app_dark.png ├── add_accused.png ├── biometrics.png ├── change_language.png ├── choose_backup.png ├── choose_type_backup.png ├── create_backup_dark.png ├── delete_my_account.png ├── details_accused.png ├── details_accused_dark.png ├── empty_filter_dark.png ├── empty_home_view.png ├── filter_bottom_sheet.png ├── filter_view.png ├── first_step.png ├── for_got_password.png ├── for_got_password_dark.png ├── get_backups.png ├── home_dark.png ├── home_view.png ├── loading_dark.png ├── login.png ├── notification_view_dark.png ├── notifications_view.png ├── onboarding1.png ├── onboarding1_dark.png ├── onboarding2.png ├── onboarding3.png ├── onboarding4.png ├── onboarding5.png ├── profile.png ├── result.png ├── search_history_dark.png ├── second_step.png ├── selected_accused.png ├── selected_backup_dark.png ├── selected_local_auth_dark.png ├── setting_english.png ├── settings.png ├── setup_AppDelegate.png ├── signup.png ├── signup_dark.png └── upload_image_profile_light.png └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | **/android/key.properties 48 | **/android/app/upload-keystore.jks 49 | /lib/env/ 50 | .env.dev 51 | 52 | # FVM Version Cache 53 | .fvm/ -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 17 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 18 | - platform: android 19 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 20 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 21 | - platform: ios 22 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 23 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | import java.io.FileInputStream 3 | 4 | plugins { 5 | id "com.android.application" 6 | // START: FlutterFire Configuration 7 | id 'com.google.gms.google-services' 8 | // END: FlutterFire Configuration 9 | id "kotlin-android" 10 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 11 | id "dev.flutter.flutter-gradle-plugin" 12 | } 13 | 14 | def keystoreProperties = new Properties() 15 | def keystorePropertiesFile = rootProject.file('key.properties') 16 | if (keystorePropertiesFile.exists()) { 17 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 18 | } 19 | 20 | 21 | android { 22 | namespace = "najeeb.aslan.issue" 23 | compileSdk = 34 24 | ndkVersion = "25.1.8937393" 25 | compileOptions { 26 | sourceCompatibility = JavaVersion.VERSION_1_8 27 | targetCompatibility = JavaVersion.VERSION_1_8 28 | } 29 | 30 | kotlinOptions { 31 | jvmTarget = JavaVersion.VERSION_1_8 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId = "najeeb.aslan.issue" 37 | // You can update the following values to match your application needs. 38 | // For more information, see: https://flutter.dev/to/review-gradle-config. 39 | minSdk = 23 40 | targetSdk = 34 41 | multiDexEnabled true 42 | versionCode = flutter.versionCode 43 | versionName = flutter.versionName 44 | } 45 | signingConfigs { 46 | release { 47 | keyAlias = keystoreProperties['keyAlias'] 48 | keyPassword = keystoreProperties['keyPassword'] 49 | storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 50 | storePassword = keystoreProperties['storePassword'] 51 | } 52 | } 53 | 54 | buildTypes { 55 | release { 56 | //! Enable in debug mode 57 | signingConfig = signingConfigs.getByName("debug") 58 | //! Enable in release mode 59 | signingConfig = signingConfigs.getByName("release") 60 | // signingConfig = signingConfigs.debug //! Enable in debug mode 61 | // signingConfig = signingConfigs.release //! Enable in release mode 62 | } 63 | } 64 | 65 | } 66 | 67 | flutter { 68 | source = "../.." 69 | } 70 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/najeeb/aslan/issue/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package najeeb.aslan.issue 2 | 3 | 4 | import io.flutter.embedding.android.FlutterFragmentActivity 5 | 6 | class MainActivity: FlutterFragmentActivity() { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/notification_sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/android/app/src/main/res/raw/notification_sound.wav -------------------------------------------------------------------------------- /android/app/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | مواعيد تمديدات الحبس الاحتياطي 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dates Extensions Preventive Detention 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #28223436 4 | #3d63ff 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | مواعيد تمديدات الحبس الاحتياطي 6 | 7 | 8 | najeeb.aslan.issue 9 | 13 | 14 | issue 15 | 16 | 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | # org.gradle.java.home=/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | // START: FlutterFire Configuration 23 | id "com.google.gms.google-services" version "4.3.15" apply false 24 | // END: FlutterFire Configuration 25 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 26 | } 27 | 28 | include ":app" 29 | -------------------------------------------------------------------------------- /assets/dark_splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/dark_splash.png -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSansArabicBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/fonts/IBMPlexSansArabicBold.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSansArabicMedium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/fonts/IBMPlexSansArabicMedium.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSansArabicRegular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/fonts/IBMPlexSansArabicRegular.ttf -------------------------------------------------------------------------------- /assets/images/arabic_language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/arabic_language.png -------------------------------------------------------------------------------- /assets/images/backup_automatically.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/backup_automatically.png -------------------------------------------------------------------------------- /assets/images/backup_manually.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/backup_manually.png -------------------------------------------------------------------------------- /assets/images/download_backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/download_backup.png -------------------------------------------------------------------------------- /assets/images/folders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/folders.png -------------------------------------------------------------------------------- /assets/images/googleDrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/googleDrive.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/logo_without_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/logo_without_background.png -------------------------------------------------------------------------------- /assets/images/onboarding_add_criminals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/onboarding_add_criminals.png -------------------------------------------------------------------------------- /assets/images/onboarding_save_your_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/onboarding_save_your_time.png -------------------------------------------------------------------------------- /assets/images/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/images/profile.jpg -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/assets/splash.png -------------------------------------------------------------------------------- /assets/svg/apple_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /assets/svg/apple_logo_light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/svg/face_id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/failure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/google_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/svg/question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/svg/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/whatsapp_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Resources/notification_sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Resources/notification_sound.wav -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import workmanager // Import the Workmanager plugin 4 | import flutter_local_notifications 5 | 6 | @main 7 | @objc class AppDelegate: FlutterAppDelegate { 8 | override func application( 9 | _ application: UIApplication, 10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 | ) -> Bool { 12 | GeneratedPluginRegistrant.register(with: self) 13 | 14 | 15 | /* [START]LocalNotifications */ 16 | // This is required to make any communication available in the action isolate. 17 | FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in 18 | GeneratedPluginRegistrant.register(with: registry) 19 | } 20 | 21 | if #available(iOS 10.0, *) { 22 | UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate 23 | } 24 | /* [END]LocalNotifications */ 25 | 26 | 27 | //START Workmanager 28 | UNUserNotificationCenter.current().delegate = self 29 | WorkmanagerPlugin.setPluginRegistrantCallback { registry in 30 | // Registry in this case is the FlutterEngine that is created in Workmanager's 31 | // performFetchWithCompletionHandler or BGAppRefreshTask. 32 | // This will make other plugins available during a background operation. 33 | GeneratedPluginRegistrant.register(with: registry) 34 | } 35 | 36 | //END Workmanager 37 | 38 | 39 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 40 | } 41 | //START Workmanager 42 | override func userNotificationCenter( 43 | _ center: UNUserNotificationCenter, 44 | willPresent notification: UNNotification, 45 | withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { 46 | completionHandler([.alert, .badge, .sound]) // shows banner even if app is in foreground 47 | } 48 | //END Workmanager 49 | } 50 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "darkbackground.png", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "LaunchImageDark.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "LaunchImage@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "LaunchImageDark@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "LaunchImage@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "LaunchImageDark@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/ar.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Runner 4 | 5 | Created by najeeb on 28/10/2024. 6 | 7 | */ 8 | 9 | //START Localized Application Name 10 | "CFBundleDisplayName" = "مواعيد تمديدات الحبس الاحتياطي"; 11 | //END Localized Application Name 12 | -------------------------------------------------------------------------------- /ios/Runner/ar.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ios/Runner/ar.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ios/Runner/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Runner 4 | 5 | Created by najeeb on 28/10/2024. 6 | 7 | */ 8 | 9 | //START Localized Application Name 10 | "CFBundleDisplayName" = "Dates Extensions Preventive Detention"; 11 | //END Localized Application Name 12 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /ios/notification_sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/ios/notification_sound.wav -------------------------------------------------------------------------------- /lib/core/constants/assets_constants.dart: -------------------------------------------------------------------------------- 1 | class ImagesConstants { 2 | static const _baseImages = 'assets/images/'; 3 | static const _baseSvg = 'assets/svg/'; 4 | static const googleLogo = '${_baseSvg}google_logo.svg'; 5 | static const appleLogo = '${_baseSvg}apple_logo.svg'; 6 | static const appleLogoLight = '${_baseSvg}apple_logo_light.svg'; 7 | static const forgotPassword = '${_baseSvg}forgot_password.svg'; 8 | static const failure = '${_baseSvg}failure.svg'; 9 | static const question = '${_baseSvg}question.svg'; 10 | static const success = '${_baseSvg}success.svg'; 11 | static const warning = '${_baseSvg}warning.svg'; 12 | static const saudiArabiaFlag = '${_baseSvg}saudi_arabia_flag.svg'; 13 | static const noFoundUsers = 'assets/svg/not_found_users.json'; 14 | static const unitedStateFlag = '${_baseSvg}us_flag.svg'; 15 | static const whatsappLogo = '${_baseSvg}whatsapp_logo.svg'; 16 | static const faceID = '${_baseSvg}face_id.svg'; 17 | static const profileImage = '${_baseImages}profile.jpg'; 18 | static const folders = '${_baseImages}folders.png'; 19 | static const googleDrive = '${_baseImages}googleDrive.png'; 20 | static const downloadBackup = '${_baseImages}download_backup.png'; 21 | static const arabicLanguage = '${_baseImages}arabic_language.png'; 22 | static const backupManually = '${_baseImages}backup_manually.png'; 23 | static const backupAutomatically = '${_baseImages}backup_automatically.png'; 24 | static const logo = '${_baseImages}logo.png'; 25 | static const logoWithoutBackground = '${_baseImages}logo_without_background.png'; 26 | static const onboardingManageCriminals = '${_baseImages}onboarding_add_criminals.png'; 27 | static const onboardingSaveYourTime = '${_baseImages}onboarding_save_your_time.png'; 28 | static const onboardingNotifications = '${_baseSvg}onboarding_notifications.svg'; 29 | static const onboardingAutoBackups = '${_baseSvg}onboarding_upload_backups.svg'; 30 | static const onboardingShareSuccess = '${_baseImages}logo.png'; 31 | } 32 | -------------------------------------------------------------------------------- /lib/core/constants/default_settings.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../features/backup/backup_cubit/backup_cubit.dart'; 4 | import '../../features/settings/widgets/notification_cleanup.dart'; 5 | 6 | class DefaultSettings { 7 | static const String theme = 'light'; 8 | static const double fontSize = 16.0; 9 | static const String biometric = 'empty'; 10 | static const bool notificationState = true; 11 | static const bool isUserInTrialMode = true; 12 | static const String fontType = 'IBM Plex Sans Arabic'; 13 | static const Locale languageCode = Locale('ar', 'SA'); 14 | static const String supportPhoneNumber = '+967773228315'; 15 | static const String supportEmail = 'najeebaslan2019@gmail.com'; 16 | static const String nameCollectionUsersInFirebase = 'users'; 17 | static const BackupSyncTypes backupSync = BackupSyncTypes.auto; 18 | static const BackupOptions backupOptions = BackupOptions.weekly; 19 | static const notificationCleanupOptions = NotificationCleanupOptions.monthly; 20 | 21 | /// Please do not change this name, and if you change it, 22 | /// the application will be able to obtain backup files from Google Drive 23 | static const String nameFolderBackupsInGoogleDrive = 'appDataFolder'; 24 | } 25 | -------------------------------------------------------------------------------- /lib/core/extensions/date_time_extension.dart: -------------------------------------------------------------------------------- 1 | extension CustomDateExtension on DateTime { 2 | int daysBetween(DateTime to) { 3 | to = DateTime(to.year, to.month, to.day); 4 | final form = DateTime(year, month, day); 5 | return to.difference(form).inDays; 6 | } 7 | 8 | int getRemainingDays({required DateTime time}) { 9 | final remaining = daysBetween(time); 10 | if (remaining < 0) return 0; 11 | return remaining; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/core/extensions/iterable_extension.dart: -------------------------------------------------------------------------------- 1 | //iterableExtension 2 | extension IterableChecker on Iterable? { 3 | /// Checks if the iterable is null or empty. 4 | bool get isEmptyOrNull => this?.isEmpty ?? true; 5 | 6 | /// Gets the first element of the iterable, or null if it's empty or null. 7 | T? firstOrNull() { 8 | if (this == null || this?.isEmpty == true) { 9 | return null; 10 | } else { 11 | return this!.first as T; // Cast to the desired type (T) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/core/extensions/string_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart' as intl; 4 | 5 | extension CustomFormatDate on String? { 6 | ///format data for year - month -day -hour 7 | String formatDateTime(BuildContext context) { 8 | DateTime dateTime = DateTime.parse(this!).toLocal(); 9 | String formatString = intl.DateFormat.MONTH_DAY; 10 | if (!isCurrentYear(dateTime.year)) { 11 | formatString = intl.DateFormat.YEAR_NUM_MONTH_DAY; 12 | } 13 | final intl.DateFormat formatter = intl.DateFormat( 14 | formatString, 15 | context.locale.languageCode, 16 | ); 17 | return formatter.format(dateTime.toLocal()); 18 | } 19 | 20 | bool isCurrentYear(int year) { 21 | DateTime now = DateTime.now(); 22 | final check = DateTime(year).difference(DateTime(now.year)); 23 | if (check.toString().startsWith('0')) { 24 | return true; 25 | } else { 26 | return false; 27 | } 28 | } 29 | 30 | String formatDate({String? languageCode}) { 31 | return intl.DateFormat( 32 | intl.DateFormat.YEAR_NUM_MONTH_DAY, 33 | languageCode, 34 | ).format(DateTime.parse(this!)); 35 | } 36 | 37 | String formatWithEnglishLanguage({String? languageCode}) { 38 | return intl.DateFormat(intl.DateFormat.YEAR_NUM_MONTH_DAY, 'en').format(DateTime.parse(this!)); 39 | } 40 | 41 | /// Returns true if given String is null or isEmpty 42 | bool get isEmptyOrNull => 43 | this == null || (this != null && this!.isEmpty) || (this != null && this! == 'null'); 44 | 45 | // Check null string, return given value if null 46 | String validate({String value = ''}) { 47 | if (isEmptyOrNull) { 48 | return value; 49 | } else { 50 | return this!; 51 | } 52 | } 53 | 54 | ///Name Abbreviation 55 | String? getFirstAndLastName({String value = '', int countTack = 2}) { 56 | List names = this?.split(" ") ?? []; 57 | if (names.length >= 3) { 58 | List firstThreeNames = names.take(countTack).toList(); 59 | return firstThreeNames.join(" "); 60 | } else { 61 | return this; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/core/networking/handel_firebase_errors.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:issue/core/helpers/helper_user.dart'; 3 | import 'package:issue/core/services/services_locator.dart'; 4 | 5 | class FirebaseErrorHandler { 6 | static String filterError(errorCode) { 7 | switch (errorCode is FirebaseAuthException ? errorCode.code : errorCode) { 8 | case "ERROR_EMAIL_ALREADY_IN_USE": 9 | case "account-exists-with-different-credential": 10 | case "email-already-in-use": 11 | return _adaptiveErrorMessages( 12 | ar: "البريد الإلكتروني مستخدم بالفعل. انتقل إلى صفحة تسجيل الدخول.", 13 | en: 'Email already in use', 14 | ); 15 | 16 | case "ERROR_WRONG_PASSWORD": 17 | case "wrong-password": 18 | return _adaptiveErrorMessages( 19 | ar: 'كلمة المرور غير صالحة أو المستخدم ليس لديه كلمة مرور.', 20 | en: 'The password is invalid or the user does not have a password.', 21 | ); 22 | case "invalid-credential": 23 | return _adaptiveErrorMessages( 24 | ar: 'خطأ في اسم المستخدم او كلمة المرور', 25 | en: 'Error email or password', 26 | ); 27 | 28 | case "ERROR_USER_NOT_FOUND": 29 | case "user-not-found": 30 | return _adaptiveErrorMessages( 31 | ar: 'لم يتم العثور على مستخدم بهذا البريد الإلكتروني.', 32 | en: 'User not found', 33 | ); 34 | 35 | case "INVALID_LOGIN_CREDENTIALS": 36 | return _adaptiveErrorMessages( 37 | ar: 'المستخدم غير موجود.', 38 | en: 'User not found', 39 | ); 40 | case "ERROR_USER_DISABLED": 41 | case "user-disabled": 42 | return _adaptiveErrorMessages( 43 | ar: 'تم تعطيل المستخدم.', 44 | en: 'User disabled', 45 | ); 46 | case "firebase_storage": 47 | case "image_upload_error": 48 | return _adaptiveErrorMessages( 49 | ar: 'هناك خطأ في تحميل الصورة، قد يكون ذلك بسبب ضعف الاتصال بالإنترنت.', 50 | en: 'Image upload error', 51 | ); 52 | case "ERROR_TOO_MANY_REQUESTS": 53 | case "operation-not-allowed": 54 | case "ERROR_OPERATION_NOT_ALLOWED": 55 | return _adaptiveErrorMessages( 56 | ar: 'تم تجاوز الحد المسموح به لعدد طلبات تسجيل الدخول لهذا الحساب.', 57 | en: 'Operation not allowed', 58 | ); 59 | 60 | case "network-request-failed": 61 | return _adaptiveErrorMessages( 62 | ar: 'هناك خطأ في الشبكة، يرجى التحقق من اتصالك بالإنترنت.', 63 | en: 'Network request failed', 64 | ); 65 | case "ERROR_INVALID_EMAIL": 66 | case "invalid-email": 67 | return _adaptiveErrorMessages( 68 | ar: 'عنوان البريد الإلكتروني غير صالح.', 69 | en: 'Invalid email', 70 | ); 71 | default: 72 | return errorCode.toString(); 73 | } 74 | } 75 | 76 | static String _adaptiveErrorMessages({required String ar, required String en}) { 77 | bool isArabic = getIt.get().getAppLanguage().startsWith('ar'); 78 | return isArabic ? ar : en; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/core/networking/network_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 2 | 3 | abstract class NetworkInfo { 4 | Future get isConnected; 5 | } 6 | 7 | class NetworkInfoImpl implements NetworkInfo { 8 | final InternetConnectionChecker connectionChecker; 9 | 10 | NetworkInfoImpl(this.connectionChecker); 11 | @override 12 | Future get isConnected => connectionChecker.hasConnection; 13 | } 14 | -------------------------------------------------------------------------------- /lib/core/networking/type_response.dart: -------------------------------------------------------------------------------- 1 | class ResponseResult { 2 | final F? failure; 3 | final S? success; 4 | ResponseResult({this.failure, this.success}); 5 | } 6 | 7 | class Success implements ResponseResult { 8 | @override 9 | final S success; 10 | Success(this.success); 11 | 12 | @override 13 | F? get failure => null; 14 | } 15 | 16 | class Failure implements ResponseResult { 17 | @override 18 | final F failure; 19 | Failure(this.failure); 20 | 21 | @override 22 | S? get success => null; 23 | } 24 | -------------------------------------------------------------------------------- /lib/core/router/routes_constants.dart: -------------------------------------------------------------------------------- 1 | class AppRoutesConstants { 2 | static const String splashView = '/splash'; 3 | static const String signInView = '/sign-in'; 4 | static const String signUpView = '/sign-up'; 5 | static const String navigationBar = '/navigation-bar'; 6 | static const String backupsSettingsView = '/backups-settings'; 7 | static const String getBackupsView = '/get-backups'; 8 | static const String addAccusedView = '/add-accused'; 9 | static const String accusedDetailsView = '/accused-details'; 10 | static const String forgotPasswordView = '/forgot-password'; 11 | static const String aboutApp = '/about-app'; 12 | static const String profileView = '/profile'; 13 | static const String onboardingView = '/onboarding'; 14 | static const String notificationsView = '/notifications'; 15 | static const String showNotificationDetails = '/show-notification-details'; 16 | } 17 | -------------------------------------------------------------------------------- /lib/core/services/encryption_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:encrypt/encrypt.dart' as encrypt; 2 | import 'package:issue/core/networking/type_response.dart'; 3 | 4 | import '../../env/env.dart'; 5 | 6 | class EncryptionService { 7 | // The singleton instance 8 | static final EncryptionService _instance = EncryptionService._internal(); 9 | 10 | // Private constructor 11 | EncryptionService._internal(); 12 | 13 | // Factory constructor to return the same instance 14 | factory EncryptionService() { 15 | return _instance; 16 | } 17 | 18 | // Encryption key Base64 19 | final encrypt.Key _key = encrypt.Key.fromBase64(Env.encryptionKey); 20 | 21 | // Method to encrypt data 22 | String encryptData(String plainText) { 23 | final iv = encrypt.IV.fromLength(16); // Generate a random IV 24 | final encrypter = encrypt.Encrypter(encrypt.AES(_key, mode: encrypt.AESMode.cbc)); 25 | 26 | final encrypted = encrypter.encrypt(plainText, iv: iv); 27 | final ivBase64 = iv.base64; 28 | final encryptedBase64 = encrypted.base64; 29 | 30 | return '$ivBase64::$encryptedBase64'; // Store IV and ciphertext together 31 | } 32 | 33 | // Method to decrypt data 34 | ResponseResult decryptData(String encryptedData) { 35 | if (encryptedData.contains('::')) { 36 | final parts = encryptedData.split('::'); 37 | final iv = encrypt.IV.fromBase64(parts[0]); // Extract the IV 38 | final encrypted = encrypt.Encrypted.fromBase64(parts[1]); 39 | 40 | final encrypter = encrypt.Encrypter(encrypt.AES(_key, mode: encrypt.AESMode.cbc)); 41 | final decrypted = encrypter.decrypt(encrypted, iv: iv); 42 | 43 | return Success(decrypted); 44 | } else { 45 | return Failure('this backup is not valid'); 46 | } 47 | } 48 | } 49 | 50 | // Method to generate a random key 51 | String generateRandomKey() { 52 | encrypt.Key? key = encrypt.Key.fromSecureRandom(32); 53 | return key.base64; 54 | } 55 | -------------------------------------------------------------------------------- /lib/core/services/local_auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:local_auth/local_auth.dart'; 5 | import 'package:local_auth_android/local_auth_android.dart' show AndroidAuthMessages; 6 | import 'package:local_auth_darwin/local_auth_darwin.dart' show IOSAuthMessages; 7 | 8 | import '../helpers/helper_user.dart'; 9 | import 'services_locator.dart'; 10 | 11 | class LocalAuthService { 12 | final LocalAuthentication auth; 13 | LocalAuthService(this.auth); 14 | Future isDeviceSupported() async { 15 | return await auth.isDeviceSupported(); 16 | } 17 | 18 | Future> getAvailableBiometrics() async { 19 | final auth = getIt.get(); 20 | final List availableBiometrics = await auth.getAvailableBiometrics(); 21 | 22 | return availableBiometrics; 23 | } 24 | 25 | void stopAuthentication() async { 26 | await auth.stopAuthentication(); 27 | } 28 | 29 | Future authenticateWithBiometrics(void Function() handleNavigator, 30 | {bool? stickyAuth}) async { 31 | if (await isDeviceSupported() && getIt.get().isEnableLocalAuth()) { 32 | try { 33 | final authenticated = await auth.authenticate( 34 | localizedReason: 'localizedReason'.tr(), 35 | authMessages: [ 36 | AndroidAuthMessages( 37 | signInTitle: 'signInTitle'.tr(), 38 | biometricHint: 'biometricHint'.tr(), 39 | cancelButton: 'cancelButton'.tr(), 40 | deviceCredentialsRequiredTitle: 'deviceCredentialsRequiredTitle'.tr(), 41 | biometricNotRecognized: 'biometricNotRecognized'.tr(), 42 | biometricRequiredTitle: 'biometricRequiredTitle'.tr(), 43 | biometricSuccess: 'biometricSuccess'.tr(), 44 | deviceCredentialsSetupDescription: 'deviceCredentialsSetupDescription'.tr(), 45 | goToSettingsButton: 'goToSettingsButton'.tr(), 46 | goToSettingsDescription: 'goToSettingsDescription'.tr(), 47 | ), 48 | IOSAuthMessages( 49 | cancelButton: 'cancelButton'.tr(), 50 | goToSettingsButton: 'goToSettingsButton'.tr(), 51 | goToSettingsDescription: 'goToSettingsDescription'.tr(), 52 | localizedFallbackTitle: 'localizedReason'.tr(), 53 | ), 54 | ], 55 | options: const AuthenticationOptions( 56 | stickyAuth: false, 57 | useErrorDialogs: true, 58 | ), 59 | ); 60 | if (authenticated) return handleNavigator(); 61 | } on PlatformException catch (e) { 62 | debugPrint(e.toString()); 63 | return; 64 | } 65 | } else { 66 | return handleNavigator(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/core/services/notifications/android_notifications_channel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 3 | 4 | import '../../theme/app_colors.dart'; 5 | 6 | class AndroidNotificationsChannel { 7 | static const String channelId = 'najeeb.aslan.issue'; 8 | static const String channelName = 'issue'; 9 | static const String channelDescription = 'Issue Notifications'; 10 | 11 | static Future setupAndroidChannel( 12 | FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin, 13 | String nameSoundNotificationFile) async { 14 | if (!kIsWeb) { 15 | await flutterLocalNotificationsPlugin 16 | .resolvePlatformSpecificImplementation() 17 | ?.createNotificationChannel( 18 | AndroidNotificationChannel( 19 | channelId, 20 | channelName, 21 | description: channelDescription, 22 | importance: Importance.high, 23 | playSound: true, 24 | ledColor: AppColors.kPrimaryColor, 25 | enableLights: false, 26 | audioAttributesUsage: AudioAttributesUsage.alarm, 27 | vibrationPattern: null, 28 | sound: RawResourceAndroidNotificationSound(nameSoundNotificationFile), 29 | showBadge: true, 30 | ), 31 | ); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/core/services/notifications/payload_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class PayloadModel { 4 | final String id; 5 | final String title; 6 | final String body; 7 | 8 | PayloadModel({ 9 | required this.id, 10 | required this.title, 11 | required this.body, 12 | }); 13 | 14 | Map toMap() { 15 | return { 16 | 'id': id, 17 | 'title': title, 18 | 'body': body, 19 | }; 20 | } 21 | 22 | factory PayloadModel.fromMap(Map map) { 23 | return PayloadModel( 24 | id: map['id'] as String, 25 | title: map['title'] as String, 26 | body: map['body'] as String, 27 | ); 28 | } 29 | 30 | String toJson() => json.encode(toMap()); 31 | 32 | factory PayloadModel.fromJson(String source) => 33 | PayloadModel.fromMap(json.decode(source) as Map); 34 | } 35 | -------------------------------------------------------------------------------- /lib/core/theme/app_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'color_hex.dart'; 4 | 5 | class AppColors { 6 | AppColors._(); 7 | 8 | static Color kPrimaryColor = const Color(0xff3d63ff); 9 | static Color kPrimaryColor200 = const Color(0xFFDDE3FB); 10 | static Color firstAlarmColor = HexColor('#87A0E5'); 11 | static Color nextAlarmColor = HexColor('#F56E98'); 12 | static Color thirdAlarmColor = HexColor('#F1B440'); 13 | static const Color white = Color(0xFFFFFFFF); 14 | static const Color background = Color(0xFFF2F3F8); 15 | static const Color kSecondColor = Color(0xff161A3A); 16 | static const Color cardDarkBackgroundColor = Color(0xff1B2349); 17 | static Color black = const Color(0xFF171717); 18 | static Color iconColor = Colors.grey[700]!; 19 | static Color errorColor = HexColor('#ff7a7a'); 20 | static Color errorDeepColor = HexColor('#ed4954'); 21 | static Color successColor = Colors.green; 22 | static const Color grey = Color(0xFF3A5160); 23 | static Color get gray800 => Colors.black.withValues(alpha: 0.80); 24 | } 25 | -------------------------------------------------------------------------------- /lib/core/theme/color_hex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HexColor extends Color { 4 | HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); 5 | 6 | static int _getColorFromHex(String hexColor) { 7 | hexColor = hexColor.toUpperCase().replaceAll('#', ''); 8 | if (hexColor.length == 6) { 9 | hexColor = 'FF$hexColor'; 10 | } 11 | return int.parse(hexColor, radix: 16); 12 | } 13 | } 14 | 15 | Color hexToColor(String hexColor) { 16 | final hexCode = hexColor.replaceAll('#', ''); 17 | return Color(int.parse('FF$hexCode', radix: 16)); 18 | } 19 | -------------------------------------------------------------------------------- /lib/core/utils/alarms_days.dart: -------------------------------------------------------------------------------- 1 | enum AlarmLevel { 2 | /// First Weak 3 | first, 4 | 5 | ///First 45 Day 6 | next, 7 | 8 | ///Next 45 Day 9 | third, 10 | } 11 | 12 | enum AlarmTypes { 13 | isCompleted, 14 | firstAlarm, 15 | nextAlarm, 16 | thirdAlert, 17 | } 18 | 19 | /// This AlarmsDays static from business logic 20 | class AlarmsDays { 21 | static const int _firstAlarm = 7; 22 | static const int _nextAlarm = _firstAlarm + 45; 23 | static const int _thirdAlarm = _nextAlarm + 45; 24 | 25 | static int calculateLavalDays(AlarmLevel alarmLevel) { 26 | int endResult = 0; 27 | 28 | switch (alarmLevel) { 29 | case AlarmLevel.first: 30 | endResult = _firstAlarm; 31 | break; 32 | case AlarmLevel.next: 33 | endResult = _nextAlarm; 34 | break; 35 | case AlarmLevel.third: 36 | endResult = _thirdAlarm; 37 | break; 38 | } 39 | return endResult; 40 | } 41 | 42 | static String getLevelName(int remainingDays) { 43 | if (remainingDays <= _firstAlarm) { 44 | return 'first'; 45 | } else if (remainingDays > _firstAlarm && remainingDays <= _nextAlarm) { 46 | return 'second'; 47 | } else { 48 | return 'third'; 49 | } 50 | } 51 | 52 | static String getLevelNameFromAlarmLevelThird(int remainingDays) { 53 | if (remainingDays > _nextAlarm) { 54 | return 'first'; 55 | } else if (remainingDays < _nextAlarm && remainingDays < _firstAlarm) { 56 | return 'second'; 57 | } else { 58 | return 'first'; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/core/utils/app_theme_and_languages_notifier/app_theme_and_languages_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/helpers/helper_user.dart'; 4 | import 'package:issue/core/services/services_locator.dart'; 5 | 6 | import '../../constants/default_settings.dart'; 7 | 8 | part 'app_theme_and_languages_state.dart'; 9 | 10 | enum ThemeType { light, dark, system } 11 | 12 | enum LanguageType { arabic, english } 13 | 14 | class AppThemeAndLanguagesCubit extends Cubit { 15 | AppThemeAndLanguagesCubit() : super(AppThemeInitial()); 16 | 17 | String _userTheme = getIt.get().getAppTheme(); 18 | String get userTheme => _userTheme; 19 | 20 | String _userLanguage = getIt.get().getAppLanguage(); 21 | 22 | String get userLanguage => _userLanguage; 23 | 24 | set userTheme(String value) { 25 | _userTheme = value; 26 | emit(ChangeThemeState()); 27 | } 28 | 29 | set userLanguage(String languageCode) { 30 | _userLanguage = languageCode; 31 | emit(ChangeLanguageState()); 32 | } 33 | 34 | Locale get locale { 35 | switch (userLanguage) { 36 | case 'arabic': 37 | return const Locale('ar', 'SA'); 38 | case 'english': 39 | return const Locale('en', 'US'); 40 | default: 41 | return DefaultSettings.languageCode; 42 | } 43 | } 44 | 45 | ThemeMode get theme { 46 | switch (userTheme) { 47 | case 'dark': 48 | return ThemeMode.dark; 49 | case 'light': 50 | return ThemeMode.light; 51 | default: 52 | return ThemeMode.system; 53 | } 54 | } 55 | 56 | void changeTheme(ThemeType themeType) async { 57 | await getIt.get().saveAppTheme(themeType); 58 | userTheme = themeType.name; 59 | } 60 | 61 | Future changeLanguage({required LanguageType languageType}) async { 62 | await getIt.get().saveAppLanguage(languageType); 63 | userLanguage = languageType.name; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/core/utils/app_theme_and_languages_notifier/app_theme_and_languages_state.dart: -------------------------------------------------------------------------------- 1 | part of 'app_theme_and_languages_cubit.dart'; 2 | 3 | sealed class AppThemeAndLanguagesState {} 4 | 5 | final class AppThemeInitial extends AppThemeAndLanguagesState {} 6 | 7 | final class ChangeThemeState extends AppThemeAndLanguagesState {} 8 | 9 | final class ChangeLanguageState extends AppThemeAndLanguagesState {} 10 | -------------------------------------------------------------------------------- /lib/core/utils/bloc_observer.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | class AppBlocObserver extends BlocObserver { 7 | @override 8 | void onCreate(BlocBase bloc) { 9 | super.onCreate(bloc); 10 | if (kDebugMode) { 11 | debugPrint('onCreate -- ${bloc.runtimeType}'); 12 | } 13 | } 14 | 15 | @override 16 | void onChange(BlocBase bloc, Change change) { 17 | super.onChange(bloc, change); 18 | if (kDebugMode) { 19 | debugPrint('onChange -- ${bloc.runtimeType}, $change'); 20 | } 21 | } 22 | 23 | @override 24 | void onError(BlocBase bloc, Object error, StackTrace stackTrace) { 25 | if (kDebugMode) { 26 | debugPrint('onError -- ${bloc.runtimeType}, $error'); 27 | } 28 | super.onError(bloc, error, stackTrace); 29 | } 30 | 31 | @override 32 | void onClose(BlocBase bloc) { 33 | super.onClose(bloc); 34 | if (kDebugMode) { 35 | debugPrint('onClose -- ${bloc.runtimeType}'); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /lib/core/utils/validate.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class InputValidation { 6 | //this is Global validation 7 | static String? validationTextField({ 8 | required var controller, 9 | required String error, 10 | required String lengthMinError, 11 | required String lengthMaxError, 12 | required int main, 13 | required int max, 14 | }) { 15 | ///this is for check if controller equal TextEditingController or no 16 | /// for if controller equal TextEditingController do it [controller.text] else do it [controller.trim() or anything] 17 | if (controller is TextEditingController) { 18 | ///if text is equal Empty return error 19 | if (controller.text.trim().isEmpty) { 20 | return error.toString(); 21 | 22 | ///if the length of the text is less than the minimum 23 | } else if (controller.text.trim().length < main) { 24 | return lengthMinError.toString(); 25 | 26 | ///If the length of the text is greater than the maximum 27 | } else if (controller.text.trim().length > max) { 28 | return lengthMaxError.toString(); 29 | } 30 | 31 | ///if text is equal Empty return error 32 | } else if (controller.trim() != null && controller.trim().isEmpty) { 33 | return error.toString(); 34 | 35 | ///if the length of the text is less than the minimum 36 | } else if (controller.trim().length < main) { 37 | return lengthMinError.toString(); 38 | 39 | ///If the length of the text is greater than the maximum 40 | } else if (controller.trim().length > max) { 41 | return lengthMaxError.toString(); 42 | } 43 | return null; 44 | } 45 | bool isValidEmail(String email) { 46 | String pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; 47 | RegExp regex = RegExp(pattern); 48 | return regex.hasMatch(email); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /lib/core/widgets/accused_input_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:issue/core/extensions/context_extension.dart'; 6 | import 'package:issue/core/widgets/custom_text_field.dart'; 7 | 8 | class AccusedInputField extends StatelessWidget { 9 | final String hint; 10 | final TextEditingController? controller; 11 | final TextInputType textInputType; 12 | final int? maxLength; 13 | final String? Function(String?)? validator; 14 | final TextInputAction? textInputAction; 15 | final List? inputFormatters; 16 | 17 | const AccusedInputField({ 18 | super.key, 19 | required this.hint, 20 | this.controller, 21 | this.textInputAction, 22 | this.maxLength, 23 | this.inputFormatters, 24 | this.textInputType = TextInputType.text, 25 | this.validator, 26 | }); 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Padding( 31 | padding: EdgeInsets.symmetric(vertical: 20.h), 32 | child: CustomTextField( 33 | textInputAction: textInputAction ?? TextInputAction.next, 34 | keyboardType: textInputType, 35 | inputFormatters: inputFormatters, 36 | borderRadius: BorderRadius.circular(30), 37 | placeholder: hint.tr(), 38 | hintStyle: context.textTheme.bodyMedium, 39 | padding: EdgeInsets.symmetric(horizontal: 15.w), 40 | maxLength: maxLength ?? 40, 41 | controller: controller, 42 | validator: validator, 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/core/widgets/adaptive_them_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../theme/app_colors.dart'; 6 | 7 | class AdaptiveThemeContainer extends StatelessWidget { 8 | const AdaptiveThemeContainer({ 9 | super.key, 10 | required this.child, 11 | this.enableBoxShadow = true, 12 | this.borderRadius, 13 | this.padding, 14 | this.height, 15 | this.border, 16 | this.width, 17 | this.margin, 18 | this.alignment, 19 | }); 20 | final Widget child; 21 | final double? height; 22 | final double? width; 23 | final EdgeInsetsGeometry? margin; 24 | final EdgeInsetsGeometry? padding; 25 | final bool enableBoxShadow; 26 | final BoxBorder? border; 27 | final BorderRadiusGeometry? borderRadius; 28 | final AlignmentGeometry? alignment; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Container( 33 | height: height, 34 | width: width, 35 | margin: margin, 36 | alignment: alignment, 37 | padding: padding ?? EdgeInsets.symmetric(vertical: 10.h), 38 | decoration: BoxDecoration( 39 | borderRadius: borderRadius ?? BorderRadius.circular(15.0), 40 | color: Colors.white, 41 | border: border, 42 | gradient: LinearGradient( 43 | begin: Alignment.topCenter, 44 | end: Alignment.bottomCenter, 45 | colors: Theme.of(context).brightness == Brightness.light 46 | ? [ 47 | Colors.white, 48 | const Color(0xfff1f1f1), 49 | ] 50 | : [ 51 | const Color(0xff1B2349), 52 | const Color(0xff161A3A), 53 | ], 54 | ), 55 | boxShadow: _getBoxShadow(context), 56 | ), 57 | child: child, 58 | ); 59 | } 60 | 61 | List? _getBoxShadow(BuildContext context) { 62 | if (enableBoxShadow) { 63 | return [ 64 | if (context.isDark) 65 | BoxShadow( 66 | color: Colors.black.withValues(alpha: 0.2), 67 | blurRadius: 6.0, 68 | ) 69 | else 70 | BoxShadow( 71 | color: AppColors.grey.withValues(alpha: 0.2), 72 | offset: const Offset(1.1, 1.1), 73 | blurRadius: 10.0, 74 | ), 75 | ]; 76 | } else { 77 | return null; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/core/widgets/animations/animated_circle_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:flutter_svg/svg.dart'; 4 | 5 | class AnimatedCircleDialog extends StatefulWidget { 6 | const AnimatedCircleDialog({ 7 | super.key, 8 | required this.imageUri, 9 | required this.backgroundCircleColor, 10 | }); 11 | final String imageUri; 12 | final Color backgroundCircleColor; 13 | @override 14 | State createState() => _AnimatedCircleDialogState(); 15 | } 16 | 17 | class _AnimatedCircleDialogState extends State 18 | with SingleTickerProviderStateMixin { 19 | late AnimationController _animationController; 20 | late Animation scaleAnimation; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | 26 | _animationController = AnimationController( 27 | vsync: this, 28 | duration: const Duration(milliseconds: 250), 29 | ); 30 | 31 | scaleAnimation = Tween(begin: 0.1, end: 1.0).animate( 32 | CurvedAnimation( 33 | curve: Curves.easeOutBack, 34 | parent: _animationController, 35 | ), 36 | ); 37 | 38 | Future.delayed( 39 | const Duration(milliseconds: 200), 40 | () => _animationController.forward(), 41 | ); 42 | } 43 | 44 | @override 45 | void dispose() { 46 | _animationController.dispose(); 47 | super.dispose(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return AnimatedBuilder( 53 | animation: _animationController, 54 | builder: (context, child) { 55 | return Transform.scale( 56 | scale: scaleAnimation.value, 57 | child: CircleAvatar( 58 | backgroundColor: widget.backgroundCircleColor, 59 | radius: 45.h, 60 | child: SvgPicture.asset( 61 | widget.imageUri, 62 | height: 50.h, 63 | width: 50.w, 64 | )), 65 | ); 66 | }, 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/core/widgets/animations/custom_slide_transition.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | enum SlideDirection { left, up } 6 | 7 | class CustomSlideTransition extends StatefulWidget { 8 | final Widget child; 9 | final int delay; 10 | final SlideDirection direction; 11 | final Curve? curve; 12 | final double? startFromBottom; 13 | final double? startFromLift; 14 | 15 | final int? milliseconds; 16 | const CustomSlideTransition({ 17 | super.key, 18 | this.curve, 19 | this.milliseconds, 20 | required this.child, 21 | required this.delay, 22 | this.startFromLift, 23 | this.startFromBottom, 24 | required this.direction, 25 | }); 26 | 27 | @override 28 | State createState() => _CustomSlideTransitionState(); 29 | } 30 | 31 | class _CustomSlideTransitionState extends State 32 | with TickerProviderStateMixin { 33 | late AnimationController _controller; 34 | late Animation _animOffset; 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | 40 | _controller = AnimationController( 41 | vsync: this, 42 | duration: Duration(milliseconds: widget.milliseconds ?? 800), 43 | ); 44 | final curve = CurvedAnimation( 45 | curve: widget.curve ?? Curves.decelerate, 46 | parent: _controller, 47 | ); 48 | 49 | if (widget.direction == SlideDirection.left) { 50 | _animOffset = Tween( 51 | begin: Offset(widget.startFromLift ?? 0.35, 0.0), 52 | end: Offset.zero, 53 | ).animate(curve); 54 | } else { 55 | _animOffset = Tween( 56 | begin: Offset(0.0, widget.startFromBottom ?? 0.35), 57 | end: Offset.zero, 58 | ).animate(curve); 59 | } 60 | 61 | Timer(Duration(milliseconds: widget.delay), () { 62 | if (mounted) _controller.forward(); 63 | }); 64 | } 65 | 66 | @override 67 | void dispose() { 68 | _controller.dispose(); 69 | super.dispose(); 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return FadeTransition( 75 | opacity: _controller, 76 | child: SlideTransition( 77 | position: _animOffset, 78 | child: widget.child, 79 | ), 80 | ); 81 | } 82 | } 83 | 84 | class ConstantsDelayAnimations { 85 | static int delay10ms = 10; 86 | static int delay30ms = 30; 87 | static int delay50ms = 50; 88 | static int delay100ms = 100; 89 | static int delay300ms = 300; 90 | static int delay400ms = 400; 91 | static int delay500ms = 500; 92 | static int delay800ms = 800; 93 | static int delay1200ms = 1200; 94 | } 95 | -------------------------------------------------------------------------------- /lib/core/widgets/animations/hero/custom_rect_tween.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/animation.dart'; 4 | 5 | class CustomRectTween extends RectTween { 6 | CustomRectTween({ 7 | required Rect begin, 8 | required Rect end, 9 | }) : super(begin: begin, end: end); 10 | 11 | @override 12 | Rect lerp(double t) { 13 | final elasticCurveValue = Curves.easeOut.transform(t); 14 | return Rect.fromLTRB( 15 | lerpDouble(begin!.left, end!.left, elasticCurveValue)!, 16 | lerpDouble(begin!.top, end!.top, elasticCurveValue)!, 17 | lerpDouble(begin!.right, end!.right, elasticCurveValue)!, 18 | lerpDouble(begin!.bottom, end!.bottom, elasticCurveValue)!, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/core/widgets/animations/hero/hero_dialog_route.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HeroDialogRoute extends PageRoute { 4 | HeroDialogRoute({ 5 | required WidgetBuilder builder, 6 | super.settings, 7 | super.fullscreenDialog, 8 | }) : _builder = builder; 9 | 10 | final WidgetBuilder _builder; 11 | 12 | @override 13 | bool get opaque => false; 14 | 15 | @override 16 | bool get barrierDismissible => true; 17 | 18 | @override 19 | Duration get transitionDuration => const Duration(milliseconds: 300); 20 | 21 | @override 22 | bool get maintainState => true; 23 | 24 | @override 25 | Color get barrierColor => Colors.black54; 26 | 27 | @override 28 | Widget buildTransitions(BuildContext context, Animation animation, 29 | Animation secondaryAnimation, Widget child) { 30 | return child; 31 | } 32 | 33 | @override 34 | Widget buildPage( 35 | BuildContext context, Animation animation, Animation secondaryAnimation) { 36 | return _builder(context); 37 | } 38 | 39 | @override 40 | String get barrierLabel => 'Popup dialog open'; 41 | } 42 | -------------------------------------------------------------------------------- /lib/core/widgets/back_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | class CustomBackIconButton extends StatelessWidget { 6 | const CustomBackIconButton({ 7 | super.key, 8 | this.color, 9 | this.onPressed, 10 | this.textDirection, 11 | }); 12 | final Color? color; 13 | final VoidCallback? onPressed; 14 | final TextDirection? textDirection; 15 | @override 16 | Widget build(BuildContext context) { 17 | return Padding( 18 | padding: EdgeInsets.symmetric( 19 | horizontal: 10.w, 20 | vertical: 5.h, 21 | ), 22 | child: MaterialButton( 23 | minWidth: 50.w, 24 | elevation: 0, 25 | padding: EdgeInsets.zero, 26 | onPressed: onPressed ?? () => Navigator.pop(context), 27 | shape: const CircleBorder(), 28 | color: context.themeData.iconTheme.color?.withValues( 29 | alpha: context.isDark ? 0.10 : 0.2, 30 | ), 31 | child: Icon( 32 | Icons.arrow_back_ios_sharp, 33 | color: context.customColors?.blackAndWhite?.withValues(alpha: 0.5), 34 | size: 18, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/core/widgets/base_divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:issue/core/extensions/context_extension.dart'; 3 | 4 | class BaseDivider extends StatelessWidget { 5 | const BaseDivider({super.key, this.thickness, this.padding}); 6 | final double? thickness; 7 | final double? padding; 8 | @override 9 | Widget build(BuildContext context) { 10 | return Divider( 11 | height: padding ?? (context.isDark ? 0 : 1.0), 12 | thickness: thickness ?? 0.4, 13 | color: context.customColors?.dividerColor, 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/core/widgets/cache_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:issue/core/widgets/shimmer.dart'; 4 | 5 | class CacheImageWidget extends StatelessWidget { 6 | const CacheImageWidget({ 7 | super.key, 8 | required this.url, 9 | this.height, 10 | this.width, 11 | this.fit, 12 | this.widgetLoading, 13 | this.isCircleShimmer = false, 14 | this.tagHero, 15 | this.errorWidget, 16 | }); 17 | final String url; 18 | 19 | final String? tagHero; 20 | final double? height; 21 | final double? width; 22 | final BoxFit? fit; 23 | final bool isCircleShimmer; 24 | final Widget? widgetLoading; 25 | final Widget? errorWidget; 26 | @override 27 | Widget build(BuildContext context) { 28 | return LayoutBuilder( 29 | builder: (context, constraints) { 30 | final size = Size(constraints.maxWidth, constraints.maxHeight); 31 | return tagHero != null ? Hero(tag: tagHero!, child: contentBody(size)) : contentBody(size); 32 | }, 33 | ); 34 | } 35 | 36 | CachedNetworkImage contentBody(Size size) { 37 | return CachedNetworkImage( 38 | placeholder: (context, url) => 39 | widgetLoading ?? 40 | CustomShimmer( 41 | height: height ?? size.height, 42 | width: width ?? size.width, 43 | isCircle: false, 44 | ), 45 | errorWidget: (context, url, error) => errorWidget ?? const Icon(Icons.error), 46 | imageUrl: url, 47 | height: height, 48 | width: width, 49 | fit: fit, 50 | ); 51 | } 52 | 53 | Function(BuildContext, String) placeholderWidgetFn() { 54 | return (_, s) => placeholderWidget(); 55 | } 56 | 57 | Widget placeholderWidget() { 58 | return Container(color: Colors.grey.withValues(alpha: 0.1)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/core/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | import 'package:issue/core/widgets/back_icon_button.dart'; 5 | 6 | class CustomAppBar extends StatelessWidget 7 | implements PreferredSizeWidget, ObstructingPreferredSizeWidget { 8 | const CustomAppBar({ 9 | super.key, 10 | required this.size, 11 | this.title, 12 | this.customAppBar, 13 | this.onClickBack, 14 | this.useCustomBackIconButton = true, 15 | }); 16 | final Size size; 17 | final Widget? title; 18 | final Widget? customAppBar; 19 | final VoidCallback? onClickBack; 20 | final bool? useCustomBackIconButton; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | if (customAppBar != null) return customAppBar!; 25 | 26 | return AppBar( 27 | title: title, 28 | elevation: 0, 29 | leading: 30 | useCustomBackIconButton == true ? CustomBackIconButton(onPressed: onClickBack) : null, 31 | backgroundColor: context.themeData.appBarTheme.backgroundColor, 32 | ); 33 | } 34 | 35 | @override 36 | Size get preferredSize => size; 37 | 38 | @override 39 | bool shouldFullyObstruct(BuildContext context) => true; 40 | } 41 | -------------------------------------------------------------------------------- /lib/core/widgets/custom_focused_menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:focused_menu/focused_menu.dart'; 3 | import 'package:focused_menu/modals.dart'; 4 | 5 | class CustomFocusedMenuHolder extends StatelessWidget { 6 | const CustomFocusedMenuHolder({ 7 | super.key, 8 | required this.child, 9 | required this.onPressed, 10 | required this.menuItems, 11 | }); 12 | final Widget child; 13 | final Function onPressed; 14 | final List menuItems; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return FocusedMenuHolder( 19 | menuItems: menuItems, 20 | menuWidth: MediaQuery.of(context).size.width * 0.50, 21 | blurSize: 5.0, 22 | menuItemExtent: 45, 23 | menuBoxDecoration: const BoxDecoration( 24 | color: Colors.grey, 25 | borderRadius: BorderRadius.all( 26 | Radius.circular(15.0), 27 | ), 28 | ), 29 | duration: const Duration(milliseconds: 100), 30 | animateMenuItems: true, 31 | blurBackgroundColor: Colors.black54, 32 | openWithTap: false, 33 | menuOffset: 10.0, 34 | bottomOffsetHeight: 80.0, 35 | onPressed: onPressed, 36 | child: child, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/core/widgets/custom_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | 4 | import '../theme/app_colors.dart'; 5 | 6 | class CustomToast { 7 | static _baseToast(String message, Color backgroundColor) { 8 | Fluttertoast.showToast( 9 | msg: message, 10 | fontSize: 16, 11 | gravity: ToastGravity.CENTER, 12 | backgroundColor: backgroundColor, 13 | textColor: Colors.white, 14 | ); 15 | } 16 | //najeebaslan2019@gmail.com 17 | static showErrorToast(String message) { 18 | return _baseToast(message, AppColors.errorDeepColor); 19 | } 20 | 21 | static showSuccessToast(String message) { 22 | return _baseToast(message, AppColors.successColor); 23 | } 24 | 25 | static showDefaultToast(String message) { 26 | return _baseToast(message, AppColors.kPrimaryColor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/core/widgets/custom_upgrade_alert.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | import 'package:upgrader/upgrader.dart'; 5 | 6 | import '../utils/app_theme_and_languages_notifier/app_theme_and_languages_cubit.dart'; 7 | 8 | class CustomUpgradeAlert extends StatelessWidget { 9 | const CustomUpgradeAlert({super.key, required this.child}); 10 | final Widget child; 11 | @override 12 | Widget build(BuildContext context) { 13 | final locale = context.read().locale; 14 | return UpgradeAlert( 15 | dialogStyle: context.isIOS ? UpgradeDialogStyle.cupertino : UpgradeDialogStyle.material, 16 | upgrader: Upgrader( 17 | languageCode: locale.languageCode, 18 | messages: UpgraderMessages(code: locale.languageCode), 19 | countryCode: locale.countryCode, 20 | // debugLogging: true, 21 | // debugDisplayAlways: true, 22 | // durationUntilAlertAgain: const Duration(days: 1),// Enable it in debug mode 23 | ), 24 | child: child, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/core/widgets/image/select_image_cubit/select_image_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:easy_localization/easy_localization.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:image_picker/image_picker.dart'; 7 | import 'package:permission_handler/permission_handler.dart'; 8 | 9 | part 'select_image_state.dart'; 10 | 11 | class SelectImageCubit extends Cubit { 12 | SelectImageCubit() : super(SelectImageInitial()); 13 | static SelectImageCubit get(BuildContext context) => BlocProvider.of(context); 14 | File? file; 15 | Future pickImagesFromGallery(BuildContext context) async { 16 | try { 17 | final pickedImages = await ImagePicker().pickImage( 18 | imageQuality: 80, 19 | source: ImageSource.gallery, 20 | ); 21 | 22 | if (pickedImages != null) { 23 | file = File(pickedImages.path); 24 | } 25 | emit(OnPickImage(file: file)); 26 | } catch (e) { 27 | if (context.mounted) { 28 | await onThrowErrorPermission(context); 29 | } 30 | } 31 | } 32 | 33 | Future pickImagesFromCamera(BuildContext context) async { 34 | try { 35 | final pickedImage = await ImagePicker().pickImage( 36 | source: ImageSource.camera, 37 | imageQuality: 80, 38 | ); 39 | if (pickedImage != null) { 40 | file = File(pickedImage.path); 41 | emit(OnPickImage(file: file)); 42 | } 43 | } catch (e) { 44 | if (context.mounted) { 45 | await onThrowErrorPermission(context); 46 | } 47 | } 48 | } 49 | 50 | Future onThrowErrorPermission(BuildContext context) async { 51 | var status = await Permission.photos.status; 52 | if (status.isDenied) { 53 | debugPrint('تم الرفض'); 54 | } else { 55 | if (context.mounted) { 56 | showCupertinoDialog( 57 | context: context, 58 | barrierDismissible: false, 59 | builder: (context) => CupertinoAlertDialog( 60 | title: Text('notAllowedPermissionPhotos'.tr()), 61 | actions: [ 62 | CupertinoDialogAction( 63 | onPressed: () => Navigator.of(context).pop(), 64 | child: Text('cancel'.tr()), 65 | ), 66 | CupertinoDialogAction( 67 | isDefaultAction: true, 68 | onPressed: () => openAppSettings(), 69 | child: Text('setting'.tr()), 70 | ) 71 | ], 72 | content: Text('allowedPermissionPhotos'.tr()), 73 | ), 74 | ); 75 | } 76 | } 77 | } 78 | 79 | void clearImage() => file = null; 80 | 81 | void setState() => emit(OnPickImage(file: file)); 82 | } 83 | -------------------------------------------------------------------------------- /lib/core/widgets/image/select_image_cubit/select_image_state.dart: -------------------------------------------------------------------------------- 1 | part of 'select_image_cubit.dart'; 2 | 3 | class SelectImageState { 4 | const SelectImageState(); 5 | } 6 | 7 | class SelectImageInitial extends SelectImageState {} 8 | 9 | class OnPickImage extends SelectImageState { 10 | OnPickImage({required this.file}); 11 | final File? file; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lib/core/widgets/image/upload_image.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:easy_localization/easy_localization.dart'; 4 | import 'package:firebase_storage/firebase_storage.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:issue/core/networking/type_response.dart'; 7 | import 'package:issue/core/services/services_locator.dart'; 8 | 9 | class UploadFiles { 10 | final FirebaseStorage _storage = getIt.get(); 11 | SocketException noInternet = SocketException('noInternet'.tr()); 12 | Duration durationTimeOut = const Duration(minutes: 3); 13 | Future> uploadImage(File imageFile) async { 14 | try { 15 | // Upload the image to Firebase Storage 16 | TaskSnapshot snapshot = await _storage 17 | .ref() 18 | .child('images/${DateTime.now().millisecondsSinceEpoch}') 19 | .putFile(imageFile) 20 | .timeout( 21 | durationTimeOut, 22 | onTimeout: () => throw noInternet, 23 | ); 24 | 25 | // Get the download URL of the uploaded image 26 | String downloadUrl = await snapshot.ref.getDownloadURL().timeout( 27 | durationTimeOut, 28 | onTimeout: () => throw noInternet, 29 | ); 30 | 31 | return Success(downloadUrl); 32 | } catch (error) { 33 | debugPrint(error.toString()); 34 | return Failure('Unknown error check image size or quality'); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/core/widgets/show_notification_details.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:issue/core/extensions/string_extension.dart'; 3 | import 'package:issue/core/router/routes_constants.dart'; 4 | import 'package:issue/data/data_base/db_helper.dart'; 5 | 6 | import '../services/notifications/payload_model.dart'; 7 | import '../services/services_locator.dart'; 8 | 9 | class ShowNotificationDetails extends StatefulWidget { 10 | const ShowNotificationDetails({super.key, required this.notificationModel}); 11 | final PayloadModel notificationModel; 12 | 13 | @override 14 | State createState() => _ShowNotificationDetailsState(); 15 | } 16 | 17 | class _ShowNotificationDetailsState extends State { 18 | @override 19 | void initState() { 20 | super.initState(); 21 | 22 | getIt 23 | .get() 24 | .getAccuseById(int.parse(widget.notificationModel.id.validate())) 25 | .then((onValue) { 26 | if (mounted) { 27 | Navigator.pushReplacementNamed( 28 | context, 29 | AppRoutesConstants.accusedDetailsView, 30 | arguments: onValue, 31 | ); 32 | } 33 | }); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return PopScope( 39 | canPop: false, 40 | onPopInvokedWithResult: (didPop, result) => false, 41 | child: const Scaffold( 42 | body: SizedBox.shrink(), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/data/models/notification_model.dart: -------------------------------------------------------------------------------- 1 | import '../../core/utils/alarms_days.dart'; 2 | import 'accuse_model.dart'; 3 | 4 | class NotificationModel { 5 | final int? notificationID; 6 | final int userID; 7 | final String notificationDate; 8 | final AlarmTypes alarmType; 9 | 10 | NotificationModel({ 11 | this.notificationID, 12 | required this.userID, 13 | required this.notificationDate, 14 | required this.alarmType, 15 | }); 16 | 17 | Map toMap() { 18 | return { 19 | 'userID': userID, 20 | 'notificationDate': notificationDate, 21 | 'alarmType': alarmType.index, 22 | }; 23 | } 24 | 25 | factory NotificationModel.fromMap(Map map) { 26 | return NotificationModel( 27 | notificationID: map['notificationID'], 28 | userID: map['userID'], 29 | notificationDate: map['notificationDate'], 30 | alarmType: AlarmTypes.values[map['alarmType']], 31 | ); 32 | } 33 | } 34 | 35 | class NotificationAdapter { 36 | final NotificationModel notificationModel; 37 | final AccusedModel accusedModel; 38 | 39 | NotificationAdapter({ 40 | required this.notificationModel, 41 | required this.accusedModel, 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /lib/data/models/profile_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:issue/core/extensions/string_extension.dart'; 3 | 4 | class ProfileModel { 5 | final String name; 6 | final String email; 7 | 8 | final String? profileImage; 9 | ProfileModel({ 10 | required this.name, 11 | required this.email, 12 | this.profileImage, 13 | }); 14 | 15 | factory ProfileModel.fromMap(Map map) { 16 | return ProfileModel( 17 | name: map['name'] as String, 18 | email: map['email'] as String, 19 | profileImage: map['profileImage'], 20 | ); 21 | } 22 | factory ProfileModel.adaptiveUser(User user) { 23 | return ProfileModel( 24 | name: user.displayName.validate(), 25 | email: user.email.validate(), 26 | profileImage: user.photoURL.validate(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/data/repositories/sync_users_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | 5 | import '../../core/constants/default_settings.dart'; 6 | import '../../core/helpers/helper_user.dart'; 7 | import '../../core/networking/network_info.dart'; 8 | import '../../core/networking/type_response.dart'; 9 | import '../../core/services/services_locator.dart'; 10 | import 'profile_repository.dart'; 11 | 12 | class SyncOldUsersRepository { 13 | void syncOldUserDataToFireStoreIfNotExists({required User? currentUser}) async { 14 | if (await getIt.get().isConnected) { 15 | if (_shouldSync(currentUser)) { 16 | final response = await getIt().isUserExisted(currentUser!.uid); 17 | if (response is Failure) { 18 | await _syncUser(currentUser); 19 | } 20 | } 21 | } 22 | } 23 | 24 | Future _syncUser(User currentUser) async { 25 | try { 26 | final getUserNameLocally = getIt.get().getUsername(); 27 | await getIt 28 | .get() 29 | .collection(DefaultSettings.nameCollectionUsersInFirebase) 30 | .doc(currentUser.uid) 31 | .set({'name': getUserNameLocally, 'email': currentUser.email}); 32 | getIt.get().saveIsUserSyncAccount(true); 33 | } catch (e) { 34 | debugPrint(e.toString()); 35 | } 36 | } 37 | 38 | bool _shouldSync(User? currentUser) { 39 | return isSignInUsingGoogle == false && 40 | currentUser != null && 41 | getIt.get().getIsUserSyncAccount() == false; 42 | } 43 | 44 | bool get isSignInUsingGoogle { 45 | final currentUser = getIt.get().currentUser; 46 | return currentUser!.providerData[0].providerId == 'google.com'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/features/accused/accused_cubit/accused_state.dart: -------------------------------------------------------------------------------- 1 | part of 'accused_cubit.dart'; 2 | 3 | sealed class AccusedState {} 4 | 5 | final class AccusedInitial extends AccusedState {} 6 | 7 | // Get Accused 8 | final class LoadingGetAccused extends AccusedState {} 9 | 10 | final class EmptyGetAccused extends AccusedState {} 11 | 12 | final class SuccessGetAccused extends AccusedState { 13 | final List accused; 14 | SuccessGetAccused(this.accused); 15 | } 16 | 17 | final class ErrorGetAccused extends AccusedState { 18 | final String error; 19 | ErrorGetAccused(this.error); 20 | } 21 | 22 | // Add Accused 23 | final class LoadingAddAccused extends AccusedState {} 24 | 25 | final class SuccessAddAccused extends AccusedState {} 26 | 27 | final class ErrorAddAccused extends AccusedState { 28 | final String error; 29 | ErrorAddAccused(this.error); 30 | } 31 | 32 | final class SetState extends AccusedState { 33 | final AccusedModel? accused; 34 | SetState({required this.accused}); 35 | } 36 | 37 | // Get Notifications 38 | final class LoadingGetNotifications extends AccusedState {} 39 | 40 | final class EmptyGetNotifications extends AccusedState {} 41 | 42 | final class SuccessGetNotifications extends AccusedState { 43 | final List notifications; 44 | SuccessGetNotifications(this.notifications); 45 | } 46 | 47 | final class ErrorGetNotifications extends AccusedState { 48 | final String error; 49 | ErrorGetNotifications(this.error); 50 | } 51 | 52 | 53 | final class OnRefreshProfileAppBar extends AccusedState {} 54 | -------------------------------------------------------------------------------- /lib/features/accused/widgets/accused_agent.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/constants/app_icons.dart'; 5 | import 'package:issue/core/extensions/context_extension.dart'; 6 | 7 | import '../../../core/services/launch_uri.dart'; 8 | import '../../../core/widgets/adaptive_them_container.dart'; 9 | import '../../../data/models/accuse_model.dart'; 10 | 11 | class AccusedAgent extends StatelessWidget { 12 | final AccusedModel accused; 13 | 14 | const AccusedAgent({super.key, required this.accused}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return _buildAccusedAgentSection(context); 19 | } 20 | 21 | Widget _buildAccusedAgentSection(BuildContext context) { 22 | return _containerWithBorder( 23 | context: context, 24 | child: Row( 25 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 26 | children: [ 27 | Text('accusedAgent'.tr(), style: context.textTheme.bodyLarge), 28 | const Spacer(), 29 | _buildAgentButton(AppIcons.messageSMS, () { 30 | LaunchUri().sendSMS(accused.phoneNu.toString()); 31 | }), 32 | _buildAgentButton(AppIcons.call, () { 33 | LaunchUri().makePhoneCall(accused.phoneNu.toString()); 34 | }), 35 | ], 36 | ), 37 | ); 38 | } 39 | 40 | Widget _containerWithBorder({required Widget child, required BuildContext context}) { 41 | return AdaptiveThemeContainer( 42 | enableBoxShadow: false, 43 | margin: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), 44 | padding: EdgeInsets.symmetric(horizontal: 15.w), 45 | border: Border.all( 46 | color: context.customColors!.blackAndWhite!.withValues(alpha: .5), 47 | width: 0.2, 48 | ), 49 | borderRadius: BorderRadius.circular(10.r), 50 | child: child, 51 | ); 52 | } 53 | 54 | RawMaterialButton _buildAgentButton(IconData icon, VoidCallback onTap) { 55 | return RawMaterialButton( 56 | onPressed: onTap, 57 | constraints: BoxConstraints.tightFor(height: 30.h, width: 30.w), 58 | shape: const CircleBorder(), 59 | padding: EdgeInsets.zero, 60 | child: Icon(icon, size: 25), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/features/accused/widgets/add_accused_bloc_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../../../core/router/routes_constants.dart'; 6 | import '../../../core/widgets/custom_toast.dart'; 7 | import '../accused_cubit/accused_cubit.dart'; 8 | 9 | class AddAccusedBlocListener extends StatelessWidget { 10 | const AddAccusedBlocListener({super.key, required this.child}); 11 | final Widget child; 12 | @override 13 | Widget build(BuildContext context) { 14 | return BlocListener( 15 | listenWhen: (previous, current) => 16 | current is LoadingAddAccused || 17 | current is SuccessAddAccused || 18 | current is ErrorAddAccused, 19 | listener: (context, state) { 20 | if (state is LoadingAddAccused) { 21 | context.showLoading(); 22 | } else if (state is SuccessAddAccused) { 23 | context.hideLoading(); 24 | Navigator.pushNamedAndRemoveUntil( 25 | context, 26 | AppRoutesConstants.navigationBar, 27 | (route) => false, 28 | ); 29 | } else if (state is ErrorAddAccused) { 30 | context.hideLoading(); 31 | CustomToast.showErrorToast(state.error); 32 | } 33 | }, 34 | child: child, 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/features/accused/widgets/alarm_control_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | import 'package:issue/core/extensions/date_time_extension.dart'; 6 | import 'package:issue/features/accused/widgets/individual_alarm_control.dart'; 7 | 8 | import '../../../core/utils/alarms_days.dart'; 9 | import '../../../data/models/accuse_model.dart'; 10 | import '../accused_details_view.dart'; 11 | 12 | class AlarmControlPanel extends StatelessWidget { 13 | final List alarmDates; 14 | final List totalAlarmDays; 15 | final List fadeInValues; 16 | final AccusedModel accused; 17 | 18 | const AlarmControlPanel({ 19 | super.key, 20 | required this.alarmDates, 21 | required this.accused, 22 | required this.totalAlarmDays, 23 | required this.fadeInValues, 24 | }); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return _buildAlarmControls(context); 29 | } 30 | 31 | Widget _buildAlarmControls(BuildContext context) { 32 | final List alarmTypes = [ 33 | AlarmTypes.firstAlarm, 34 | AlarmTypes.nextAlarm, 35 | AlarmTypes.thirdAlert, 36 | ]; 37 | 38 | final List alarmStates = [ 39 | accused.firstAlarm!, 40 | accused.nextAlarm!, 41 | accused.thirdAlert!, 42 | ]; 43 | 44 | return Padding( 45 | padding: EdgeInsets.symmetric(horizontal: 10.w), 46 | child: DefaultTextStyle( 47 | style: context.textTheme.bodyMedium!, 48 | child: Column( 49 | crossAxisAlignment: CrossAxisAlignment.start, 50 | children: [ 51 | const SizedBox(height: 7), 52 | FadeInAnimation( 53 | opacity: fadeInValues[5], 54 | child: Text( 55 | 'stopAlarms'.tr(), 56 | style: context.textTheme.bodyMedium, 57 | ), 58 | ), 59 | const SizedBox(height: 7), 60 | ...List.generate(totalAlarmDays.length, (index) { 61 | return FadeInAnimation( 62 | opacity: fadeInValues[6 + index], 63 | child: IndividualAlarmControl( 64 | alarmType: alarmTypes[index], 65 | isStopped: accused.isCompleted == 1, 66 | isCompleted: alarmStates[index] == 1, 67 | remainingDays: DateTime.now().getRemainingDays(time: alarmDates[index]), 68 | context: context, 69 | accused: accused, 70 | ), 71 | ); 72 | }), 73 | ], 74 | ), 75 | ), 76 | ); 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /lib/features/auth/auth_cubit/auth_state.dart: -------------------------------------------------------------------------------- 1 | part of 'auth_cubit.dart'; 2 | 3 | @immutable 4 | sealed class AuthState {} 5 | 6 | final class AuthInitial extends AuthState {} 7 | 8 | final class SignInLoading extends AuthState {} 9 | 10 | final class SignInSuccess extends AuthState {} 11 | 12 | final class SignInError extends AuthState { 13 | final String error; 14 | SignInError(this.error); 15 | } 16 | 17 | // SignUp 18 | final class SignUpLoading extends AuthState {} 19 | 20 | final class SignUpSuccess extends AuthState {} 21 | 22 | final class SignUpError extends AuthState { 23 | final String error; 24 | SignUpError(this.error); 25 | } 26 | 27 | // ResetPassword 28 | final class ResetPasswordLoading extends AuthState {} 29 | 30 | final class ResetPasswordSuccess extends AuthState {} 31 | 32 | final class ResetPasswordError extends AuthState { 33 | final String error; 34 | ResetPasswordError(this.error); 35 | } 36 | 37 | // SignOut 38 | final class LoadingSignOutState extends AuthState {} 39 | 40 | final class SuccessSignOutState extends AuthState {} 41 | 42 | final class ErrorSignOutState extends AuthState { 43 | final String error; 44 | 45 | ErrorSignOutState(this.error); 46 | } 47 | 48 | // Register With Google 49 | final class RegisterWithGoogleLoading extends AuthState {} 50 | 51 | final class RegisterWithGoogleSuccess extends AuthState {} 52 | 53 | final class RegisterWithGoogleError extends AuthState { 54 | final String error; 55 | RegisterWithGoogleError(this.error); 56 | } 57 | -------------------------------------------------------------------------------- /lib/features/auth/views/sign_up_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | import 'package:issue/core/widgets/custom_button.dart'; 5 | 6 | import '../../../core/theme/theme.dart'; 7 | import '../auth_cubit/auth_cubit.dart'; 8 | import '../widgets/auth_background.dart'; 9 | import '../widgets/auth_form.dart'; 10 | import '../widgets/is_have_account.dart'; 11 | import '../widgets/register_with_third_party.dart'; 12 | import '../widgets/sign_up_bloc_listener.dart'; 13 | import '../widgets/title_auth.dart'; 14 | 15 | class SignUpView extends StatefulWidget { 16 | const SignUpView({super.key}); 17 | 18 | @override 19 | State createState() => _SignUpViewState(); 20 | } 21 | 22 | class _SignUpViewState extends State { 23 | late AuthCubit _authCubit; 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _authCubit = AuthCubit.get(context); 28 | _authCubit.initController(); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | _authCubit.disposeController(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Scaffold( 40 | body: AuthBackgroundWithTitle( 41 | paddingButtonOnOpenKeyword: context.viewInsets.bottom, 42 | child: SignUpBlocListener( 43 | child: SizedBox( 44 | height: context.height, 45 | width: context.width, 46 | child: Padding( 47 | padding: EdgeInsets.symmetric(horizontal: 30.w), 48 | child: SingleChildScrollView( 49 | child: Column( 50 | crossAxisAlignment: CrossAxisAlignment.start, 51 | children: [ 52 | const TitleAuth(title: 'signUp'), 53 | const AuthForm(isSignUp: true), 54 | const SizedBox(height: defaultPadding * 1.2), 55 | CustomButton( 56 | onPressed: _authCubit.validateThenSignUp, 57 | label: 'signUp', 58 | ), 59 | SizedBox(height: context.height * 0.02), 60 | RegisterWithThirdParty( 61 | onTapGoogle: _authCubit.signInWithGoogle, 62 | onTapApple: _authCubit.signInWithApple, 63 | ), 64 | SizedBox(height: context.height * 0.04), 65 | const IsHaveAccount(isSignIn: false), 66 | SizedBox(height: context.height * 0.02), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ), 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/auth_background.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../core/theme/theme.dart'; 4 | 5 | class AuthBackgroundWithTitle extends StatelessWidget { 6 | const AuthBackgroundWithTitle({ 7 | super.key, 8 | required this.child, 9 | required this.paddingButtonOnOpenKeyword, 10 | }); 11 | final Widget child; 12 | 13 | final double paddingButtonOnOpenKeyword; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return LayoutBuilder(builder: (context, constraints) { 18 | double maxHeight = constraints.maxHeight + paddingButtonOnOpenKeyword; 19 | double maxWidth = constraints.maxWidth; 20 | return AnnotatedRegion( 21 | value: authSystemUiOverlayStyle(context), 22 | child: Stack( 23 | clipBehavior: Clip.none, 24 | fit: StackFit.expand, 25 | children: [ 26 | child, 27 | Positioned( 28 | top: -maxHeight * 0.3, 29 | right: -maxWidth * 0.1, 30 | child: Container( 31 | height: maxHeight * 0.7, 32 | width: maxWidth * 0.7, 33 | decoration: const BoxDecoration( 34 | shape: BoxShape.circle, 35 | gradient: LinearGradient( 36 | colors: [ 37 | Color(0xFF3D5AD1), 38 | Color(0xFF6291E2), 39 | ], 40 | begin: Alignment.topCenter, 41 | end: Alignment.bottomCenter, 42 | ), 43 | ), 44 | ), 45 | ), 46 | Positioned( 47 | top: -maxHeight * 0.1, 48 | left: -maxWidth * 0.2, 49 | child: Container( 50 | height: maxHeight * 0.43, 51 | width: maxWidth * 0.8, 52 | decoration: const BoxDecoration( 53 | shape: BoxShape.circle, 54 | gradient: LinearGradient( 55 | colors: [ 56 | Color(0xFF3D5AD1), 57 | Color(0xFF2048E6), 58 | Color(0xFF548AE7), 59 | ], 60 | begin: Alignment.topCenter, 61 | end: Alignment.bottomCenter, 62 | ), 63 | ), 64 | ), 65 | ), 66 | ], 67 | ), 68 | ); 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/auth_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../../../core/theme/app_colors.dart'; 6 | import '../../../core/theme/theme.dart'; 7 | import '../../../core/widgets/custom_text_field.dart'; 8 | import '../auth_cubit/auth_cubit.dart'; 9 | 10 | class AuthForm extends StatelessWidget { 11 | const AuthForm({super.key, required this.isSignUp}); 12 | final bool isSignUp; 13 | static bool obscureText = true; 14 | static bool rememberMe = false; 15 | @override 16 | Widget build(BuildContext context) { 17 | return Center( 18 | child: Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | if (isSignUp) 22 | Padding( 23 | padding: const EdgeInsets.only( 24 | bottom: defaultPadding * 1.2, 25 | ), 26 | child: CustomTextField( 27 | textInputAction: TextInputAction.next, 28 | keyboardType: TextInputType.name, 29 | hintStyle: context.textTheme.bodyMedium, 30 | autofillHints: const [AutofillHints.name], 31 | icon: Icons.person, 32 | placeholder: 'username'.tr(), 33 | maxLength: 32, 34 | controller: AuthCubit.get(context).usernameController, 35 | ), 36 | ), 37 | CustomTextField( 38 | textInputAction: TextInputAction.next, 39 | keyboardType: TextInputType.emailAddress, 40 | autofillHints: const [AutofillHints.email], 41 | icon: Icons.email, 42 | hintStyle: context.textTheme.bodyMedium, 43 | placeholder: 'email'.tr(), 44 | maxLength: 32, 45 | controller: AuthCubit.get(context).emailController, 46 | ), 47 | const SizedBox(height: defaultPadding * 1.2), 48 | StatefulBuilder( 49 | builder: (context, setState) { 50 | return CustomTextField( 51 | keyboardType: TextInputType.visiblePassword, 52 | textInputAction: TextInputAction.done, 53 | prefixIcon: Icon(Icons.lock, color: AppColors.kPrimaryColor), 54 | obscureText: obscureText, 55 | suffixIcon: GestureDetector( 56 | onTap: () => setState(() => obscureText = !obscureText), 57 | child: Icon( 58 | obscureText ? Icons.visibility : Icons.visibility_off, 59 | color: 60 | !obscureText ? AppColors.kPrimaryColor : Colors.grey.withValues(alpha: 0.4), 61 | ), 62 | ), 63 | hintStyle: context.textTheme.bodyMedium, 64 | placeholder: 'password'.tr(), 65 | maxLength: 32, 66 | controller: AuthCubit.get(context).passwordController, 67 | ); 68 | }, 69 | ), 70 | ], 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/forgot_password/forgot_passowrd_bloc_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | import 'package:issue/core/widgets/custom_toast.dart'; 6 | 7 | import '../../../../core/router/routes_constants.dart'; 8 | import '../../../../core/widgets/animations/animation_dialog.dart'; 9 | import '../../auth_cubit/auth_cubit.dart'; 10 | 11 | class ForgotPasswordBlocListener extends StatelessWidget { 12 | const ForgotPasswordBlocListener({super.key, required this.child}); 13 | final Widget child; 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocListener( 17 | listenWhen: (previous, current) => 18 | current is ResetPasswordLoading || 19 | current is ResetPasswordError || 20 | current is ResetPasswordSuccess, 21 | listener: (context, state) { 22 | if (state is ResetPasswordLoading) { 23 | context.showLoading(); 24 | } else if (state is ResetPasswordSuccess) { 25 | context.hideLoading(); 26 | CustomToast.showDefaultToast('weAreSendLinkToYourEmail'.tr()); 27 | Navigator.pushNamedAndRemoveUntil( 28 | context, 29 | AppRoutesConstants.signInView, 30 | (route) => false, 31 | ); 32 | } else if (state is ResetPasswordError) { 33 | context.hideLoading(); 34 | context.awesomeDialog( 35 | context: context, 36 | title: state.error, 37 | dialogType: CustomDialogType.error, 38 | ); 39 | } 40 | }, 41 | child: child, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/forgot_password/forgot_password_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart' as material; 3 | import 'package:flutter/material.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | 6 | import '../../../../core/widgets/custom_text_field.dart'; 7 | 8 | class ForgotPasswordForm extends StatelessWidget { 9 | const ForgotPasswordForm({super.key, required this.emailController}); 10 | final TextEditingController emailController; 11 | @override 12 | Widget build(BuildContext context) { 13 | return Directionality( 14 | textDirection: material.TextDirection.rtl, 15 | child: Center( 16 | child: Column( 17 | crossAxisAlignment: CrossAxisAlignment.start, 18 | children: [ 19 | CustomTextField( 20 | maxLength: 32, 21 | icon: Icons.email, 22 | controller: emailController, 23 | textInputAction: TextInputAction.done, 24 | keyboardType: TextInputType.emailAddress, 25 | hintStyle: context.textTheme.bodyMedium, 26 | autofillHints: const [AutofillHints.email], 27 | placeholder: 'enterEmail'.tr(), 28 | ), 29 | ], 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/forgot_password/switch_auth_views_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart' as easy_localization; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | import '../../../../core/router/routes_manager.dart'; 6 | 7 | class SwitchAuthViewsButton extends StatelessWidget { 8 | final String label; 9 | final String routeName; 10 | final String textButton; 11 | final VoidCallback? onTap; 12 | final bool comingFromSignInView; 13 | final TextDirection? textDirection; 14 | const SwitchAuthViewsButton({ 15 | super.key, 16 | this.onTap, 17 | this.textDirection, 18 | required this.label, 19 | this.routeName = '', 20 | required this.textButton, 21 | required this.comingFromSignInView, 22 | }); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Row( 27 | textDirection: textDirection ?? TextDirection.rtl, 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | SizedBox(width: 10.w), 31 | Text( 32 | label.tr(), 33 | style: context.textTheme.bodyMedium?.copyWith( 34 | color: Colors.grey, 35 | letterSpacing: -0.2, 36 | ), 37 | ), 38 | SizedBox(width: 5.w), 39 | InkWell( 40 | onTap: onTap ?? 41 | () { 42 | if (routeName.isEmpty) { 43 | AppRouter.unknownRouteScreen(); 44 | } else { 45 | comingFromSignInView 46 | ? Navigator.pushReplacementNamed(context, routeName) 47 | : Navigator.pop(context); 48 | } 49 | }, 50 | child: Text( 51 | textButton.tr(), 52 | style: context.textTheme.bodyMedium?.copyWith( 53 | color: context.customColors?.blackAndWhite, 54 | ), 55 | ), 56 | ), 57 | ], 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/is_have_account.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/gestures.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | import 'package:issue/core/extensions/context_extension.dart'; 7 | 8 | import '../../../core/router/routes_constants.dart'; 9 | import '../auth_cubit/auth_cubit.dart'; 10 | 11 | class IsHaveAccount extends StatelessWidget { 12 | const IsHaveAccount({super.key, required this.isSignIn}); 13 | final bool isSignIn; 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocBuilder( 17 | builder: (context, state) { 18 | return Center( 19 | child: RichText( 20 | textAlign: TextAlign.center, 21 | text: TextSpan( 22 | children: [ 23 | TextSpan( 24 | text: !isSignIn ? 'alreadyHaveAccount'.tr() : 'noHaveAccount'.tr(), 25 | style: context.textTheme.bodyMedium?.copyWith( 26 | color: Colors.grey, 27 | letterSpacing: -0.2, 28 | ), 29 | ), 30 | WidgetSpan(child: SizedBox(width: 5.w)), 31 | TextSpan( 32 | text: isSignIn ? 'signUp'.tr() : 'signIn'.tr(), 33 | style: context.textTheme.bodyMedium?.copyWith( 34 | color: context.customColors?.blackAndWhite, 35 | ), 36 | recognizer: TapGestureRecognizer() 37 | ..onTap = () { 38 | Navigator.pushReplacementNamed( 39 | context, 40 | isSignIn ? AppRoutesConstants.signUpView : AppRoutesConstants.signInView, 41 | ); 42 | }, 43 | ), 44 | ], 45 | ), 46 | ), 47 | ); 48 | }, 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/register_with_third_party.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:flutter_svg/svg.dart'; 5 | import 'package:issue/core/extensions/context_extension.dart'; 6 | 7 | import '../../../core/constants/assets_constants.dart'; 8 | 9 | class RegisterWithThirdParty extends StatelessWidget { 10 | const RegisterWithThirdParty({ 11 | super.key, 12 | required this.onTapApple, 13 | required this.onTapGoogle, 14 | }); 15 | final VoidCallback onTapApple; 16 | final VoidCallback onTapGoogle; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Column( 21 | children: [ 22 | Row( 23 | mainAxisAlignment: MainAxisAlignment.center, 24 | crossAxisAlignment: CrossAxisAlignment.center, 25 | children: [ 26 | _buildExpandedDivider(), 27 | Text( 28 | 'orSignInBy'.tr(), 29 | style: context.textTheme.bodyMedium?.copyWith( 30 | color: context.customColors?.blackAndWhite, 31 | ), 32 | ), 33 | _buildExpandedDivider(), 34 | ], 35 | ), 36 | SizedBox(height: 20.h), 37 | Row( 38 | children: [ 39 | Expanded(child: continueWithGoogle(context)), 40 | ], 41 | ), 42 | ], 43 | ); 44 | } 45 | 46 | Widget continueWithGoogle(BuildContext context) { 47 | return ElevatedButton( 48 | style: ElevatedButton.styleFrom( 49 | backgroundColor: 50 | context.isDark ? const Color(0xff1B2349) : Colors.grey.withValues(alpha: 0.1), 51 | shape: StadiumBorder(side: BorderSide(color: Colors.grey.withValues(alpha: 0.15))), 52 | padding: EdgeInsets.symmetric(vertical: 12.h), 53 | ), 54 | onPressed: onTapGoogle, 55 | child: Padding( 56 | padding: EdgeInsets.symmetric( 57 | horizontal: 20.w, 58 | ), 59 | child: Row( 60 | children: [ 61 | SvgPicture.asset( 62 | ImagesConstants.googleLogo, 63 | height: 30.h, 64 | width: 30.w, 65 | ), 66 | SizedBox(width: 10.w), 67 | Text( 68 | 'continue_with_google'.tr(), 69 | style: context.textTheme.bodyLarge, 70 | ), 71 | ], 72 | ), 73 | ), 74 | ); 75 | } 76 | 77 | Expanded _buildExpandedDivider() { 78 | return Expanded( 79 | child: Divider( 80 | color: Colors.grey, 81 | height: 1, 82 | thickness: 1, 83 | indent: 5.w, 84 | endIndent: 10.w, 85 | ), 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/sign_in_bloc_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../../../core/helpers/helper_user.dart'; 6 | import '../../../core/router/routes_constants.dart'; 7 | import '../../../core/services/services_locator.dart'; 8 | import '../../../core/widgets/animations/animation_dialog.dart'; 9 | import '../auth_cubit/auth_cubit.dart'; 10 | 11 | class SignInBlocListener extends StatelessWidget { 12 | const SignInBlocListener({super.key, required this.child}); 13 | final Widget child; 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocListener( 17 | listenWhen: (previous, current) => 18 | current is SignInLoading || 19 | current is SignInError || 20 | current is SignInSuccess || 21 | current is RegisterWithGoogleLoading || 22 | current is RegisterWithGoogleSuccess || 23 | current is RegisterWithGoogleError, 24 | listener: (context, state) { 25 | if (state is SignInLoading || state is RegisterWithGoogleLoading) { 26 | context.showLoading(); 27 | } else if (state is SignInSuccess || state is RegisterWithGoogleSuccess) { 28 | context.hideLoading(); 29 | getIt.get().saveUserLoggedIn(true); 30 | Navigator.pushNamedAndRemoveUntil( 31 | context, 32 | AppRoutesConstants.navigationBar, 33 | (route) => false, 34 | ); 35 | } else if (state is SignInError || state is RegisterWithGoogleError) { 36 | context.hideLoading(); 37 | String error = ''; 38 | if (state is RegisterWithGoogleError) { 39 | error = state.error; 40 | } else if (state is SignInError) { 41 | error = state.error; 42 | } 43 | context.awesomeDialog( 44 | context: context, 45 | title: error, 46 | dialogType: CustomDialogType.error, 47 | ); 48 | } 49 | }, 50 | child: child, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/sign_up_bloc_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | import 'package:issue/core/helpers/helper_user.dart'; 5 | import 'package:issue/core/services/services_locator.dart'; 6 | 7 | import '../../../core/router/routes_constants.dart'; 8 | import '../../../core/widgets/animations/animation_dialog.dart'; 9 | import '../auth_cubit/auth_cubit.dart'; 10 | 11 | class SignUpBlocListener extends StatelessWidget { 12 | const SignUpBlocListener({super.key, required this.child}); 13 | final Widget child; 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocListener( 17 | listenWhen: (previous, current) => 18 | current is SignUpLoading || 19 | current is SignUpError || 20 | current is SignUpSuccess || 21 | current is RegisterWithGoogleLoading || 22 | current is RegisterWithGoogleSuccess || 23 | current is RegisterWithGoogleError, 24 | listener: (context, state) { 25 | if (state is SignUpLoading || state is RegisterWithGoogleLoading) { 26 | context.showLoading(); 27 | } else if (state is SignUpSuccess || state is RegisterWithGoogleSuccess) { 28 | context.hideLoading(); 29 | getIt.get().saveUserLoggedIn(true); 30 | Navigator.pushNamedAndRemoveUntil( 31 | context, 32 | AppRoutesConstants.navigationBar, 33 | (route) => false, 34 | ); 35 | } else if (state is SignUpError || state is RegisterWithGoogleError) { 36 | context.hideLoading(); 37 | String error = ''; 38 | if (state is RegisterWithGoogleError) { 39 | error = state.error; 40 | } else if (state is SignUpError) { 41 | error = state.error; 42 | } 43 | context.awesomeDialog( 44 | context: context, 45 | title: error, 46 | dialogType: CustomDialogType.error, 47 | ); 48 | } 49 | }, 50 | child: child, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/features/auth/widgets/title_auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | 6 | class TitleAuth extends StatelessWidget { 7 | const TitleAuth({super.key, required this.title}); 8 | final String title; 9 | @override 10 | Widget build(BuildContext context) { 11 | return Center( 12 | child: Padding( 13 | padding: EdgeInsets.only( 14 | top: context.height * .33, 15 | bottom: 20.h, 16 | ), 17 | child: Text( 18 | title.tr(), 19 | style: context.textTheme.headlineSmall?.copyWith( 20 | color: context.textTheme.titleLarge!.color, 21 | fontSize: 23.sp, 22 | ), 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/features/backup/backup_cubit/backup_state.dart: -------------------------------------------------------------------------------- 1 | part of 'backup_cubit.dart'; 2 | 3 | sealed class BackupState {} 4 | 5 | final class BackupInitial extends BackupState {} 6 | 7 | // Get All Backups 8 | final class LoadingGetBackups extends BackupState {} 9 | 10 | final class LoadedGetBackups extends BackupState { 11 | final List backups; 12 | 13 | LoadedGetBackups({required this.backups}); 14 | } 15 | 16 | final class ErrorGetBackups extends BackupState { 17 | final String error; 18 | 19 | ErrorGetBackups(this.error); 20 | } 21 | 22 | // Download Backup 23 | final class LoadingDownloadBackup extends BackupState {} 24 | 25 | final class LoadedDownloadBackup extends BackupState { 26 | final List dataStore; 27 | 28 | LoadedDownloadBackup({required this.dataStore}); 29 | } 30 | 31 | final class ErrorDownloadBackup extends BackupState { 32 | final String error; 33 | 34 | ErrorDownloadBackup(this.error); 35 | } 36 | 37 | // Upload Backup 38 | final class LoadingUploadBackup extends BackupState {} 39 | 40 | final class LoadedUploadBackup extends BackupState {} 41 | 42 | final class ErrorUploadBackup extends BackupState { 43 | final String error; 44 | 45 | ErrorUploadBackup(this.error); 46 | } 47 | 48 | // Delete Backup 49 | final class LoadingDeleteBackup extends BackupState {} 50 | 51 | final class DoneDeleteBackup extends BackupState { 52 | final List backups; 53 | 54 | DoneDeleteBackup({required this.backups}); 55 | } 56 | 57 | final class ErrorDeleteBackup extends BackupState { 58 | final String error; 59 | 60 | ErrorDeleteBackup(this.error); 61 | } 62 | 63 | // Get Google Drive Api 64 | final class LoadingGetGoogleDriveApi extends BackupState {} 65 | 66 | final class LoadedGetGoogleDriveApi extends BackupState {} 67 | 68 | final class ErrorGetGoogleDriveApi extends BackupState { 69 | final String error; 70 | 71 | ErrorGetGoogleDriveApi(this.error); 72 | } 73 | 74 | /// Restore Backup 75 | final class LoadingRestoredBackup extends BackupState {} 76 | 77 | final class DoneRestoredBackup extends BackupState {} 78 | 79 | final class ErrorRestoredBackup extends BackupState { 80 | final String error; 81 | 82 | ErrorRestoredBackup(this.error); 83 | } 84 | -------------------------------------------------------------------------------- /lib/features/backup/logic/auto_upload_backup.dart: -------------------------------------------------------------------------------- 1 | import 'package:issue/core/extensions/string_extension.dart'; 2 | import 'package:issue/core/helpers/helper_user.dart'; 3 | 4 | import '../../../core/networking/network_info.dart'; 5 | import '../../../core/networking/type_response.dart'; 6 | import '../../../data/data_base/db_helper.dart'; 7 | import '../../../data/repositories/google_drive_repository.dart'; 8 | import '../backup_cubit/backup_cubit.dart'; 9 | 10 | class AutomaticUploadBackup { 11 | final BaseGoogleDriveRepository googleDriveRepository; 12 | final NetworkInfo networkInfo; 13 | final UserHelper userHelper; 14 | final DBHelper dbHelper; 15 | AutomaticUploadBackup({ 16 | required this.googleDriveRepository, 17 | required this.networkInfo, 18 | required this.userHelper, 19 | required this.dbHelper, 20 | }); 21 | Future generateBackup() async { 22 | if (await _shouldUploadBackup()) { 23 | final backup = await dbHelper.generateBackup(); 24 | if (backup is Failure) return; 25 | final googleDriveApiV3 = await googleDriveRepository.getDriveApi(); 26 | if (googleDriveApiV3.success == null) return; 27 | final response = await googleDriveRepository.uploadBackup( 28 | driveApiV3: googleDriveApiV3.success!, 29 | backup: backup.success!, 30 | ); 31 | if (response is Success) { 32 | userHelper.saveLastUploadBackupDate( 33 | DateTime.now().toIso8601String(), 34 | ); 35 | } 36 | } 37 | } 38 | 39 | Future _shouldUploadBackup() async { 40 | // Retrieve backup status and options 41 | final statusUploadBackup = userHelper.getBackupStatus(); 42 | final lastUploadBackupDate = userHelper.getLastUploadBackupDate(); 43 | final getBackupFrequencyOption = userHelper.getBackupOptions(); 44 | 45 | // If there's no last upload date, we should upload 46 | if (lastUploadBackupDate.isEmptyOrNull) return true; 47 | 48 | // Calculate the difference in days since the last upload 49 | final differenceDays = DateTime.now().difference(DateTime.parse(lastUploadBackupDate)).inDays; 50 | 51 | // Check if auto upload is enabled and internet is available 52 | final isAutoUploadEnabled = 53 | statusUploadBackup == BackupSyncTypes.auto && await networkInfo.isConnected; 54 | // Check if user in trial mode 55 | final isUserInTrialMode = userHelper.isUserInTrialMode(); 56 | 57 | // Determine if a backup should be uploaded based on frequency 58 | if (!isAutoUploadEnabled || isUserInTrialMode) return false; 59 | 60 | switch (getBackupFrequencyOption) { 61 | case BackupOptions.daily: 62 | return differenceDays >= 1; 63 | case BackupOptions.weekly: 64 | return differenceDays >= 7; 65 | case BackupOptions.monthly: 66 | return differenceDays >= 30; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/features/backup/widgets/backup_elevated_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | 6 | class BackUpElevatedButton extends StatelessWidget { 7 | const BackUpElevatedButton({ 8 | super.key, 9 | required this.borderSide, 10 | required this.onPressed, 11 | required this.imageUri, 12 | required this.title, 13 | this.radioWidget, 14 | this.backgroundColor, 15 | }); 16 | 17 | final BorderSide borderSide; 18 | final VoidCallback onPressed; 19 | final String imageUri; 20 | final String title; 21 | final Widget? radioWidget; 22 | final Color? backgroundColor; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return ElevatedButton( 27 | style: ButtonStyle( 28 | backgroundColor: WidgetStateProperty.all( 29 | backgroundColor ?? Colors.transparent, 30 | ), 31 | shape: WidgetStateProperty.all( 32 | ContinuousRectangleBorder( 33 | side: borderSide, 34 | borderRadius: BorderRadius.circular(10.r), 35 | ), 36 | ), 37 | padding: WidgetStateProperty.all( 38 | const EdgeInsets.all(5), 39 | ), 40 | ), 41 | onPressed: onPressed, 42 | child: Row( 43 | children: [ 44 | Padding( 45 | padding: EdgeInsets.symmetric( 46 | vertical: 5.h, 47 | horizontal: 12.w, 48 | ), 49 | child: Image.asset( 50 | imageUri, 51 | height: 50.h, 52 | width: 50.w, 53 | fit: BoxFit.fill, 54 | ), 55 | ), 56 | Text( 57 | title.tr(), 58 | style: context.textTheme.bodyMedium, 59 | ), 60 | const Spacer(), 61 | radioWidget ?? const SizedBox.shrink(), 62 | ], 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/backup/widgets/backup_header_title.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:easy_localization/easy_localization.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | 6 | class BackUpHeaderTitle extends StatelessWidget { 7 | const BackUpHeaderTitle({super.key, required this.title}); 8 | 9 | final String title; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return RichText( 14 | textAlign: TextAlign.center, 15 | text: TextSpan( 16 | text: title.tr(), 17 | style: context.textTheme.titleLarge?.copyWith( 18 | letterSpacing: 0.5, 19 | ), 20 | children: [ 21 | TextSpan( 22 | text: '\n?Google Drive', 23 | style: context.textTheme.titleLarge, 24 | ), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/features/backup/widgets/loading_backups.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import '../../../core/theme/theme.dart'; 5 | import '../../../core/widgets/adaptive_them_container.dart'; 6 | import '../../../core/widgets/shimmer.dart'; 7 | 8 | class LoadingBackups extends StatelessWidget { 9 | const LoadingBackups({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return ListView.builder( 14 | padding: EdgeInsets.symmetric(vertical: defaultPadding.h), 15 | itemCount: 10, 16 | itemBuilder: (context, index) { 17 | return AdaptiveThemeContainer( 18 | margin: EdgeInsets.symmetric(vertical: 8.h, horizontal: defaultPadding.w), 19 | child: ListTile( 20 | dense: true, 21 | leading: CustomShimmer(height: 50.h, width: 50.w, isCircle: true), 22 | visualDensity: const VisualDensity(horizontal: -4, vertical: -1), 23 | title: Row( 24 | children: [ 25 | Flexible(child: CustomShimmer(height: 7.h, width: 150.w)), 26 | SizedBox(width: 10.w), 27 | CustomShimmer(height: 15.h, width: 20.w, isCircle: true) 28 | ], 29 | ), 30 | subtitle: Row( 31 | children: [ 32 | CustomShimmer(height: 7.h, width: 120.w), 33 | SizedBox(width: 3.w), 34 | CustomShimmer(height: 15.h, width: 15.w, isCircle: true) 35 | ], 36 | ), 37 | trailing: CustomShimmer(height: 5.h, width: 27.w), 38 | ), 39 | ); 40 | }, 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/features/filter/filter_cubit/filter_state.dart: -------------------------------------------------------------------------------- 1 | part of 'filter_cubit.dart'; 2 | 3 | enum FilterStates { initial, loading, loaded, error } 4 | 5 | class FilterState {} 6 | 7 | final class FilterInitial extends FilterState {} 8 | 9 | final class LoadingFilteredAccusedState extends FilterState {} 10 | 11 | final class SuccessFilteredAccusedState extends FilterState { 12 | final List listAccused; 13 | 14 | SuccessFilteredAccusedState({required this.listAccused}); 15 | } 16 | 17 | final class ErrorFilteredAccusedState extends FilterState { 18 | final String error; 19 | ErrorFilteredAccusedState(this.error); 20 | } 21 | 22 | final class RefreshFilterView extends FilterState {} 23 | 24 | final class CompletedOrSoonCompletedSelected extends FilterState { 25 | final List listAccused; 26 | 27 | CompletedOrSoonCompletedSelected({required this.listAccused}); 28 | } 29 | 30 | final class FilterByIssueNumber extends FilterState { 31 | final List listAccused; 32 | 33 | FilterByIssueNumber({required this.listAccused}); 34 | } 35 | 36 | final class SearchAccusedByNameOrIssueNumber extends FilterState { 37 | final List listAccused; 38 | 39 | SearchAccusedByNameOrIssueNumber({required this.listAccused}); 40 | } 41 | 42 | final class EmptyGetAccusedState extends FilterState {} 43 | -------------------------------------------------------------------------------- /lib/features/filter/widget/list_issue_numbers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | import 'package:issue/data/models/accuse_model.dart'; 6 | 7 | import '../../../core/theme/app_colors.dart'; 8 | import '../filter_cubit/filter_cubit.dart'; 9 | 10 | class ListIssueNumbers extends StatefulWidget { 11 | const ListIssueNumbers({super.key}); 12 | 13 | @override 14 | State createState() => _ListIssueNumbersState(); 15 | } 16 | 17 | class _ListIssueNumbersState extends State { 18 | final ValueNotifier _selectedIndex = ValueNotifier(-1); 19 | 20 | @override 21 | void dispose() { 22 | _selectedIndex.dispose(); 23 | super.dispose(); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | final listAccused = AccusedModel.removeDuplicates( 29 | context.read().listAccused, 30 | ); 31 | 32 | return SingleChildScrollView( 33 | scrollDirection: Axis.horizontal, 34 | child: ValueListenableBuilder( 35 | valueListenable: _selectedIndex, 36 | builder: (context, selectedIndex, child) { 37 | return Row( 38 | spacing: 2.w, 39 | children: List.generate( 40 | listAccused.length, 41 | (index) { 42 | final issueNumber = listAccused[index].issueNumber.toString(); 43 | final isSelected = selectedIndex == index; 44 | 45 | return RawChip( 46 | showCheckmark: false, 47 | labelPadding: EdgeInsets.symmetric(horizontal: 10.w), 48 | selectedColor: context.themeData.primaryColor, 49 | side: BorderSide( 50 | color: AppColors.grey.withValues(alpha: 0.1), 51 | ), 52 | backgroundColor: context.customColors?.backgroundTextField, 53 | selected: isSelected, 54 | visualDensity: const VisualDensity(horizontal: 4), 55 | label: Text( 56 | issueNumber, 57 | style: context.textTheme.bodyMedium?.copyWith( 58 | color: isSelected 59 | ? AppColors.white 60 | : context.isDark 61 | ? null 62 | : AppColors.iconColor, 63 | ), 64 | ), 65 | onPressed: () { 66 | final isAlreadySelected = _selectedIndex.value == index; 67 | context.read().filterByIssueNumber( 68 | listAccused[index].issueNumber!, 69 | isAlreadySelected, 70 | ); 71 | 72 | _selectedIndex.value = isAlreadySelected ? -1 : index; 73 | }, 74 | ); 75 | }, 76 | ), 77 | ); 78 | }, 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/features/home/widgets/base_hero_flip_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class BaseHeroFlipAnimation extends StatelessWidget { 6 | const BaseHeroFlipAnimation({ 7 | super.key, 8 | required this.child, 9 | required this.heroTag, 10 | required this.addMaterial, 11 | }); 12 | final Widget child; 13 | final String heroTag; 14 | final bool addMaterial; 15 | @override 16 | Widget build(BuildContext context) { 17 | return Hero( 18 | tag: heroTag, 19 | flightShuttleBuilder: (_, Animation animation, __, ___, ____) { 20 | final rotationAnimation = Tween( 21 | begin: 0.0, // This value should match the first Hero 22 | end: 2.0, // This value should match the second Hero 23 | ).animate(animation); 24 | 25 | if (addMaterial) { 26 | return Material( 27 | child: _animatedBuilder(rotationAnimation), 28 | ); 29 | } else { 30 | return Material(child: _animatedBuilder(rotationAnimation)); 31 | } 32 | }, 33 | child: Transform( 34 | transform: Matrix4.identity() 35 | ..setEntry(3, 2, 0.003) 36 | ..rotateX(0), 37 | alignment: Alignment.center, 38 | child: child, 39 | ), 40 | ); 41 | } 42 | 43 | AnimatedBuilder _animatedBuilder(Animation rotationAnimation) { 44 | return AnimatedBuilder( 45 | animation: rotationAnimation, 46 | child: child, 47 | builder: (context, child) { 48 | return Transform( 49 | transform: Matrix4.identity() 50 | ..setEntry(3, 2, 0.003) 51 | ..rotateX(rotationAnimation.value * math.pi), 52 | alignment: Alignment.center, 53 | child: child, 54 | ); 55 | }, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/home/widgets/highlight_painter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HighlightPainter extends CustomPainter { 4 | final Size highlightSize; 5 | final Size screenSize; 6 | final Offset highlightPosition; 7 | final double highlightRadius; 8 | 9 | HighlightPainter({ 10 | required this.highlightSize, 11 | required this.highlightPosition, 12 | required this.highlightRadius, 13 | required this.screenSize, 14 | }); 15 | @override 16 | void paint(Canvas canvas, Size size) { 17 | final paint = Paint()..color = Colors.black54; 18 | 19 | canvas.drawPath( 20 | Path.combine( 21 | PathOperation.difference, 22 | Path() 23 | ..addRRect( 24 | RRect.fromLTRBR( 25 | 0, 26 | 0, 27 | screenSize.width, 28 | screenSize.height, 29 | Radius.zero, 30 | ), 31 | ), 32 | Path() 33 | ..addRRect( 34 | RRect.fromLTRBR( 35 | highlightPosition.dx, 36 | highlightPosition.dy, 37 | highlightPosition.dx + highlightSize.width, 38 | highlightPosition.dy + highlightSize.height, 39 | Radius.circular(highlightRadius), 40 | ), 41 | ), 42 | ), 43 | paint); 44 | } 45 | 46 | @override 47 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 48 | return false; 49 | } 50 | 51 | @override 52 | bool? hitTest(Offset position) { 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/features/notifications/notifications_cubit/notifications_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/iterable_extension.dart'; 4 | import 'package:issue/core/helpers/helper_user.dart'; 5 | 6 | import '../../../data/data_base/db_helper.dart'; 7 | import '../../../data/models/notification_model.dart'; 8 | 9 | part 'notifications_state.dart'; 10 | 11 | class NotificationsCubit extends Cubit { 12 | NotificationsCubit(this._dbHelper, this._userHelper) : super(NotificationsInitial()); 13 | final DBHelper _dbHelper; 14 | final UserHelper _userHelper; 15 | 16 | void getNotifications() async { 17 | try { 18 | emit(LoadingGetNotifications()); 19 | List notificationAdapter = []; 20 | 21 | final notifications = await _dbHelper.getAllNotifications(); 22 | if (notifications.isEmptyOrNull) { 23 | emit(SuccessGetNotifications([])); 24 | } else { 25 | for (var notificationModel in notifications) { 26 | final getAccuse = await _dbHelper.getAccuseById( 27 | notificationModel.userID, 28 | ); 29 | notificationAdapter.add( 30 | NotificationAdapter( 31 | notificationModel: notificationModel, 32 | accusedModel: getAccuse, 33 | ), 34 | ); 35 | } 36 | emit(SuccessGetNotifications(notificationAdapter)); 37 | } 38 | } catch (e) { 39 | debugPrint(e.toString()); 40 | emit(ErrorGetNotifications(e.toString())); 41 | } 42 | } 43 | 44 | void clearAllNotifications() async { 45 | try { 46 | emit(LoadingDeleteNotifications()); 47 | final updateAccused = await _dbHelper.clearAllNotifications( 48 | _userHelper.getNotificationCleanupOptions(), 49 | ); 50 | if (updateAccused is int) { 51 | emit(SuccessDeleteNotifications()); 52 | } 53 | } catch (e) { 54 | debugPrint(e.toString()); 55 | emit(ErrorDeleteNotifications(e.toString())); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/notifications/notifications_cubit/notifications_state.dart: -------------------------------------------------------------------------------- 1 | part of 'notifications_cubit.dart'; 2 | 3 | sealed class NotificationsState {} 4 | 5 | final class NotificationsInitial extends NotificationsState {} 6 | 7 | // Get Notifications 8 | final class LoadingGetNotifications extends NotificationsState {} 9 | 10 | final class SuccessGetNotifications extends NotificationsState { 11 | final List notificationAdapter; 12 | SuccessGetNotifications(this.notificationAdapter); 13 | } 14 | 15 | final class ErrorGetNotifications extends NotificationsState { 16 | final String error; 17 | ErrorGetNotifications(this.error); 18 | } 19 | 20 | 21 | 22 | // Delete Notifications 23 | final class LoadingDeleteNotifications extends NotificationsState {} 24 | 25 | final class SuccessDeleteNotifications extends NotificationsState {} 26 | 27 | final class ErrorDeleteNotifications extends NotificationsState { 28 | final String error; 29 | ErrorDeleteNotifications(this.error); 30 | } 31 | -------------------------------------------------------------------------------- /lib/features/onboarding/animated_pages.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | typedef ChildBuilder = Widget Function(int index, BuildContext context); 4 | typedef OnPageChangeCallback = void Function(int index); 5 | 6 | class AnimatedPages extends StatelessWidget { 7 | final PageController pageController; 8 | final ValueNotifier pageValueNotifier; 9 | final ChildBuilder child; 10 | final int pageCount; 11 | final OnPageChangeCallback onPageChangeCallback; 12 | 13 | const AnimatedPages({ 14 | super.key, 15 | required this.pageController, 16 | required this.pageValueNotifier, 17 | required this.child, 18 | required this.pageCount, 19 | required this.onPageChangeCallback, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return ValueListenableBuilder( 25 | valueListenable: pageValueNotifier, 26 | builder: (context, pageValue, _) { 27 | return PageView.builder( 28 | onPageChanged: onPageChangeCallback, 29 | physics: const ClampingScrollPhysics(), 30 | controller: pageController, 31 | itemCount: pageCount, 32 | itemBuilder: (context, index) { 33 | if (index == pageValue.floor() + 1 || index == pageValue.floor() + 2) { 34 | /// Right 35 | return Transform.translate( 36 | offset: Offset(0.0, 300 * (index - pageValue)), 37 | child: child(index, context), 38 | ); 39 | } else if (index == pageValue.floor() || index == pageValue.floor() - 1) { 40 | /// Left 41 | return Transform.translate( 42 | offset: Offset(0.0, 300 * (pageValue - index)), 43 | child: child(index, context), 44 | ); 45 | } else { 46 | /// Middle 47 | return child(index, context); 48 | } 49 | }, 50 | ); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/features/onboarding/animation_circular_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../../core/theme/app_colors.dart'; 6 | import '../../core/widgets/percent_indicator.dart'; 7 | import 'on_boarding_data.dart'; 8 | 9 | class AnimationCircularIndicator extends StatelessWidget { 10 | const AnimationCircularIndicator({ 11 | super.key, 12 | required this.pageIndex, 13 | required this.nextPage, 14 | required this.onAnimationEnd, 15 | }); 16 | 17 | final int pageIndex; 18 | final VoidCallback nextPage; 19 | final VoidCallback onAnimationEnd; 20 | static const double circleRadius = 360.0; 21 | static const int firstPageIndex = 1; 22 | static int totalPage = OnBoardingData.onBoardingList.length; 23 | static const int animationDuration = 150; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | final double percent = _getPercent(pageIndex + firstPageIndex); 28 | 29 | return CircularPercentIndicator( 30 | radius: 83.r, 31 | lineWidth: 3.5.w, 32 | animation: true, 33 | onAnimationEnd: onAnimationEnd, 34 | animateFromLastPercent: true, 35 | animationDuration: animationDuration, 36 | backgroundColor: AppColors.kPrimaryColor200, 37 | progressColor: context.themeData.primaryColor, 38 | percent: percent, 39 | center: RawMaterialButton( 40 | fillColor: context.themeData.primaryColor, 41 | shape: const CircleBorder(), 42 | onPressed: nextPage, 43 | child: Padding( 44 | padding: const EdgeInsets.all(15.0), 45 | child: Icon( 46 | Icons.arrow_forward_ios_sharp, 47 | size: 25.0.w, 48 | color: Colors.white, 49 | ), 50 | ), 51 | ), 52 | ); 53 | } 54 | 55 | double _getPercent(int remainingPages) { 56 | return ((remainingPages / totalPage) * circleRadius).abs(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/features/onboarding/on_boarding_data.dart: -------------------------------------------------------------------------------- 1 | import '../../core/constants/assets_constants.dart'; 2 | 3 | class OnBoardingData { 4 | static final List onBoardingList = [ 5 | const OnBoardingModel( 6 | image: ImagesConstants.onboardingManageCriminals, 7 | title: 'manageCriminals', 8 | description: 'manageCriminalsSubtitle', 9 | ), 10 | const OnBoardingModel( 11 | image: ImagesConstants.onboardingSaveYourTime, 12 | title: 'saveYourTime', 13 | description: 'saveYourTimeSubtitle', 14 | ), 15 | const OnBoardingModel( 16 | image: ImagesConstants.onboardingAutoBackups, 17 | title: 'autoBackups', 18 | description: 'autoBackupsSubtitle', 19 | ), 20 | const OnBoardingModel( 21 | image: ImagesConstants.onboardingNotifications, 22 | title: 'onboardingNotifications', 23 | description: 'onboardingNotificationsSubtitle', 24 | ), 25 | ]; 26 | } 27 | 28 | class OnBoardingModel { 29 | final String image; 30 | final String title; 31 | final String description; 32 | 33 | const OnBoardingModel({ 34 | required this.image, 35 | required this.title, 36 | required this.description, 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /lib/features/settings/profile_cubit/profile_state.dart: -------------------------------------------------------------------------------- 1 | part of 'profile_cubit.dart'; 2 | 3 | sealed class ProfileState { 4 | const ProfileState(); 5 | } 6 | 7 | final class ProfileInitial extends ProfileState {} 8 | 9 | // Get Profile States 10 | class LoadingGetProfileState extends ProfileState {} 11 | 12 | class SuccessGetProfileState extends ProfileState { 13 | const SuccessGetProfileState({required this.profileModel}); 14 | final ProfileModel profileModel; 15 | } 16 | 17 | class ErrorGetProfileState extends ProfileState { 18 | const ErrorGetProfileState({required this.error}); 19 | final String error; 20 | } 21 | 22 | // Update Profile States 23 | class LoadingUpdateProfileState extends ProfileState {} 24 | 25 | class SuccessUpdateProfileState extends ProfileState {} 26 | 27 | class ErrorUpdateProfileState extends ProfileState { 28 | const ErrorUpdateProfileState({required this.error}); 29 | 30 | final String error; 31 | } 32 | 33 | // Delete Account States 34 | class LoadingDeleteAccountState extends ProfileState {} 35 | 36 | class SuccessDeleteAccountState extends ProfileState {} 37 | 38 | class ErrorDeleteAccountState extends ProfileState { 39 | const ErrorDeleteAccountState({required this.error}); 40 | 41 | final String error; 42 | } 43 | 44 | // Update Image States 45 | class LoadingUpdateImageState extends ProfileState {} 46 | 47 | class SuccessUpdateImageState extends ProfileState { 48 | final String imageUri; 49 | 50 | SuccessUpdateImageState(this.imageUri); 51 | } 52 | 53 | class ErrorUpdateImageState extends ProfileState { 54 | const ErrorUpdateImageState({required this.error}); 55 | 56 | final String error; 57 | } 58 | 59 | // Refresh Profile 60 | class RefreshProfileState extends ProfileState {} 61 | -------------------------------------------------------------------------------- /lib/features/settings/widgets/logout_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:issue/core/extensions/context_extension.dart'; 5 | 6 | import '../../../core/constants/app_icons.dart'; 7 | import '../../../core/router/routes_constants.dart'; 8 | import '../../../core/theme/app_colors.dart'; 9 | import '../../../core/widgets/animations/animation_dialog.dart'; 10 | import '../../../core/widgets/custom_toast.dart'; 11 | import '../../auth/auth_cubit/auth_cubit.dart'; 12 | import 'list_tile_setting.dart'; 13 | 14 | class LogoutButton extends StatelessWidget { 15 | const LogoutButton({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return BlocListener( 20 | listenWhen: (previous, current) => 21 | current is SuccessSignOutState || 22 | current is LoadingSignOutState || 23 | current is ErrorSignOutState, 24 | listener: (context, state) { 25 | if (state is LoadingSignOutState) { 26 | context.showLoading(); 27 | } else if (state is ErrorSignOutState) { 28 | CustomToast.showErrorToast(state.error); 29 | } else if (state is SuccessSignOutState) { 30 | context.hideLoading(); 31 | Navigator.pushNamedAndRemoveUntil( 32 | context, 33 | AppRoutesConstants.signInView, 34 | (predicate) => false, 35 | ); 36 | } 37 | }, 38 | child: ListTileSetting( 39 | isLogoutButton: true, 40 | icon: AppIcons.logout, 41 | title: 'logout', 42 | onTap: () { 43 | context.awesomeDialog( 44 | title: 'doYouWantLogout'.tr(), 45 | context: context, 46 | btnOkText: 'logout'.tr(), 47 | dialogType: CustomDialogType.question, 48 | color: AppColors.errorDeepColor, 49 | btnOkOnPress: () => BlocProvider.of(context).signOut(), 50 | ); 51 | }, 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/features/settings/widgets/profile/update_profile_bloc_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:issue/core/extensions/context_extension.dart'; 4 | 5 | import '../../../../core/widgets/custom_toast.dart'; 6 | import '../../profile_cubit/profile_cubit.dart'; 7 | 8 | class UpdateProfileBlocListener extends StatelessWidget { 9 | const UpdateProfileBlocListener( 10 | {super.key, required this.contextUpdateProfileWidget, required this.child}); 11 | final BuildContext contextUpdateProfileWidget; 12 | 13 | final Function(ProfileCubit profileCubit) child; 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocListener( 17 | listenWhen: (previous, current) => 18 | current is LoadingUpdateProfileState || 19 | current is SuccessUpdateProfileState || 20 | current is ErrorUpdateProfileState, 21 | listener: (context, state) { 22 | if (state is LoadingUpdateProfileState) { 23 | context.showLoading(); 24 | } else if (state is SuccessUpdateProfileState) { 25 | context.hideLoading(); // hide loading 26 | bool updatedProfile = true; 27 | Navigator.pop(contextUpdateProfileWidget, updatedProfile); // hide bottom sheet 28 | } else if (state is ErrorUpdateProfileState) { 29 | context.hideLoading(); 30 | CustomToast.showErrorToast(state.error); 31 | } 32 | }, 33 | child: child(context.read()), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: type=lint 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | throw UnsupportedError( 21 | 'DefaultFirebaseOptions have not been configured for web - ' 22 | 'you can reconfigure this by running the FlutterFire CLI again.', 23 | ); 24 | } 25 | switch (defaultTargetPlatform) { 26 | case TargetPlatform.android: 27 | return android; 28 | case TargetPlatform.iOS: 29 | return ios; 30 | case TargetPlatform.macOS: 31 | throw UnsupportedError( 32 | 'DefaultFirebaseOptions have not been configured for macos - ' 33 | 'you can reconfigure this by running the FlutterFire CLI again.', 34 | ); 35 | case TargetPlatform.windows: 36 | throw UnsupportedError( 37 | 'DefaultFirebaseOptions have not been configured for windows - ' 38 | 'you can reconfigure this by running the FlutterFire CLI again.', 39 | ); 40 | case TargetPlatform.linux: 41 | throw UnsupportedError( 42 | 'DefaultFirebaseOptions have not been configured for linux - ' 43 | 'you can reconfigure this by running the FlutterFire CLI again.', 44 | ); 45 | default: 46 | throw UnsupportedError( 47 | 'DefaultFirebaseOptions are not supported for this platform.', 48 | ); 49 | } 50 | } 51 | // Add Your android and ios FirebaseOptions here 52 | 53 | // static const FirebaseOptions android = FirebaseOptions( 54 | // apiKey: '', 55 | // appId: '', 56 | // messagingSenderId: '', 57 | // projectId: '', 58 | // storageBucket: '', 59 | // ); 60 | 61 | // static const FirebaseOptions ios = FirebaseOptions( 62 | // apiKey: '', 63 | // appId: '', 64 | // messagingSenderId: '', 65 | // projectId: '', 66 | // storageBucket: '', 67 | // androidClientId: '', 68 | // iosClientId: '', 69 | // iosBundleId: '', 70 | // ); 71 | } 72 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_native_splash/flutter_native_splash.dart'; 6 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 7 | import 'package:issue/firebase_options.dart'; 8 | import 'package:workmanager/workmanager.dart'; 9 | 10 | import '../core/services/services_locator.dart' as di; 11 | import 'app.dart'; 12 | import 'core/helpers/shared_prefs_service.dart'; 13 | import 'core/helpers/task_scheduler.dart'; 14 | import 'core/services/notifications/local_notifications.dart'; 15 | import 'core/utils/bloc_observer.dart'; 16 | 17 | Future main() async { 18 | Bloc.observer = AppBlocObserver(); 19 | WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); 20 | FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); 21 | 22 | EasyLocalization.ensureInitialized(); 23 | await Firebase.initializeApp( 24 | options: DefaultFirebaseOptions.currentPlatform, 25 | name: "issues", 26 | ); 27 | 28 | await ScreenUtil.ensureScreenSize(); 29 | await di.initServiceLocator(); 30 | await HelperSharedPreferences.getInstance(); 31 | await LocalNotificationService().init(); 32 | Workmanager().initialize(callbackDispatcher, isInDebugMode: false); 33 | FlutterNativeSplash.remove(); 34 | 35 | runApp( 36 | EasyLocalization( 37 | saveLocale: true, 38 | fallbackLocale: const Locale('ar', 'SA'), 39 | supportedLocales: const [Locale('en', 'US'), Locale('ar', 'SA')], 40 | path: 'assets/translations', 41 | child: const Issue(), 42 | ), 43 | ); 44 | } 45 | 46 | // Mandatory if the App is obfuscated or using Flutter 3.1+ 47 | @pragma('vm:entry-point') 48 | void callbackDispatcher() { 49 | Workmanager().executeTask( 50 | (task, inputData) async { 51 | try { 52 | await di.initServiceLocator(); 53 | await HelperSharedPreferences.getInstance(); 54 | await di.getIt.get().onExecuteTask(); 55 | return Future.value(true); 56 | } catch (e) { 57 | return Future.value(false); 58 | } 59 | }, 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: issue 2 | description: "هذا التطبيق يتيح لك اضافة المتهم وإدارة جميع بياناتة من حيث التعديل والحذف وما الى ذالك ويقوم بإظهار تنبيهات عند انتهاء الوقت المتاح للمتهم والوقت المحدد لتنبية هو الاسبوع الاول من تاريخ الإظافة المتهم ثم بعد خمسة واربعون يوم ثم إيظا بعد خمسة واربعون يوم اخرى." 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | 7 | version: 2.1.1+3 8 | 9 | environment: 10 | sdk: ^3.5.3 11 | 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | 17 | 18 | 19 | cupertino_icons: ^1.0.8 20 | flutter_bloc: ^8.1.6 21 | flutter_screenutil: ^5.9.3 22 | fluttertoast: ^8.2.8 23 | flutter_local_notifications: ^17.2.3 24 | firebase_core: ^3.6.0 25 | firebase_auth: ^5.3.1 26 | cloud_firestore: ^5.2.1 27 | firebase_storage: ^12.3.2 28 | easy_localization: ^3.0.7 29 | internet_connection_checker: ^1.0.0+1 30 | get_it: ^8.0.0 31 | focused_menu: ^1.0.5 32 | workmanager: ^0.5.2 33 | url_launcher: ^6.3.0 34 | share_plus: ^10.0.2 35 | pdf: ^3.11.1 36 | googleapis: ^13.2.0 37 | http: ^1.2.2 38 | encrypt: ^5.0.3 39 | path: ^1.9.0 40 | local_auth: ^2.3.0 41 | shared_preferences: ^2.3.4 42 | animations: ^2.0.11 43 | google_sign_in: ^6.2.1 44 | flutter_svg: ^2.0.10+1 45 | upgrader: ^11.2.0 46 | cached_network_image: ^3.4.1 47 | intl: ^0.19.0 48 | fluentui_system_icons: ^1.1.259 49 | image_picker: ^1.1.2 50 | permission_handler: ^11.3.1 51 | sqflite: ^2.3.3+2 52 | path_provider: ^2.1.4 53 | envied: ^0.5.4+1 54 | flutter_native_splash: ^2.4.2 55 | lottie: ^3.3.1 56 | 57 | 58 | 59 | 60 | dev_dependencies: 61 | flutter_test: 62 | sdk: flutter 63 | flutter_lints: ^4.0.0 64 | build_runner: ^2.4.13 65 | envied_generator: ^0.5.4+1 66 | 67 | 68 | flutter: 69 | 70 | uses-material-design: true 71 | 72 | 73 | assets: 74 | - assets/ 75 | - assets/splash.png 76 | - assets/dark_splash.png 77 | - assets/images/logo.png 78 | - assets/images/arabic_language.png 79 | - assets/images/logo_without_background.png 80 | - assets/images/onboarding_add_criminals.png 81 | - assets/images/onboarding_save_your_time.png 82 | - assets/images/googleDrive.png 83 | - assets/images/folders.png 84 | - assets/images/download_backup.png 85 | - assets/images/backup_manually.png 86 | - assets/images/backup_automatically.png 87 | - assets/images/profile.jpg 88 | - assets/translations/ 89 | - assets/svg/ 90 | 91 | 92 | 93 | 94 | 95 | fonts: 96 | - family: IBM Plex Sans Arabic 97 | fonts: 98 | - asset: assets/fonts/IBMPlexSansArabicRegular.ttf 99 | weight: 400 100 | - asset: assets/fonts/IBMPlexSansArabicBold.ttf 101 | weight: 700 102 | - family: IBM Plex Sans Arabic Medium 103 | fonts: 104 | - asset: assets/fonts/IBMPlexSansArabicMedium.ttf 105 | weight: 500 106 | -------------------------------------------------------------------------------- /screen_shoots/abdout_app_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/abdout_app_dark.png -------------------------------------------------------------------------------- /screen_shoots/add_accused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/add_accused.png -------------------------------------------------------------------------------- /screen_shoots/biometrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/biometrics.png -------------------------------------------------------------------------------- /screen_shoots/change_language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/change_language.png -------------------------------------------------------------------------------- /screen_shoots/choose_backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/choose_backup.png -------------------------------------------------------------------------------- /screen_shoots/choose_type_backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/choose_type_backup.png -------------------------------------------------------------------------------- /screen_shoots/create_backup_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/create_backup_dark.png -------------------------------------------------------------------------------- /screen_shoots/delete_my_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/delete_my_account.png -------------------------------------------------------------------------------- /screen_shoots/details_accused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/details_accused.png -------------------------------------------------------------------------------- /screen_shoots/details_accused_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/details_accused_dark.png -------------------------------------------------------------------------------- /screen_shoots/empty_filter_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/empty_filter_dark.png -------------------------------------------------------------------------------- /screen_shoots/empty_home_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/empty_home_view.png -------------------------------------------------------------------------------- /screen_shoots/filter_bottom_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/filter_bottom_sheet.png -------------------------------------------------------------------------------- /screen_shoots/filter_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/filter_view.png -------------------------------------------------------------------------------- /screen_shoots/first_step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/first_step.png -------------------------------------------------------------------------------- /screen_shoots/for_got_password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/for_got_password.png -------------------------------------------------------------------------------- /screen_shoots/for_got_password_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/for_got_password_dark.png -------------------------------------------------------------------------------- /screen_shoots/get_backups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/get_backups.png -------------------------------------------------------------------------------- /screen_shoots/home_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/home_dark.png -------------------------------------------------------------------------------- /screen_shoots/home_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/home_view.png -------------------------------------------------------------------------------- /screen_shoots/loading_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/loading_dark.png -------------------------------------------------------------------------------- /screen_shoots/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/login.png -------------------------------------------------------------------------------- /screen_shoots/notification_view_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/notification_view_dark.png -------------------------------------------------------------------------------- /screen_shoots/notifications_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/notifications_view.png -------------------------------------------------------------------------------- /screen_shoots/onboarding1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding1.png -------------------------------------------------------------------------------- /screen_shoots/onboarding1_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding1_dark.png -------------------------------------------------------------------------------- /screen_shoots/onboarding2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding2.png -------------------------------------------------------------------------------- /screen_shoots/onboarding3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding3.png -------------------------------------------------------------------------------- /screen_shoots/onboarding4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding4.png -------------------------------------------------------------------------------- /screen_shoots/onboarding5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/onboarding5.png -------------------------------------------------------------------------------- /screen_shoots/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/profile.png -------------------------------------------------------------------------------- /screen_shoots/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/result.png -------------------------------------------------------------------------------- /screen_shoots/search_history_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/search_history_dark.png -------------------------------------------------------------------------------- /screen_shoots/second_step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/second_step.png -------------------------------------------------------------------------------- /screen_shoots/selected_accused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/selected_accused.png -------------------------------------------------------------------------------- /screen_shoots/selected_backup_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/selected_backup_dark.png -------------------------------------------------------------------------------- /screen_shoots/selected_local_auth_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/selected_local_auth_dark.png -------------------------------------------------------------------------------- /screen_shoots/setting_english.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/setting_english.png -------------------------------------------------------------------------------- /screen_shoots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/settings.png -------------------------------------------------------------------------------- /screen_shoots/setup_AppDelegate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/setup_AppDelegate.png -------------------------------------------------------------------------------- /screen_shoots/signup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/signup.png -------------------------------------------------------------------------------- /screen_shoots/signup_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/signup_dark.png -------------------------------------------------------------------------------- /screen_shoots/upload_image_profile_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najeebaslan/AppIssue/e5d0669522ab43b0b6e010359cba3b54f2589b58/screen_shoots/upload_image_profile_light.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | import 'package:issue/app.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const Issue()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | --------------------------------------------------------------------------------