├── .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 |
--------------------------------------------------------------------------------
/assets/svg/apple_logo_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/svg/face_id.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/svg/failure.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/svg/google_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/svg/question.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/svg/success.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/svg/warning.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------