├── .gitignore ├── .metadata ├── LICENCE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── devsadeq │ │ │ │ └── jobs_flutter_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── empty.json ├── empty_save.svg ├── header_bg.svg ├── screenshots │ ├── cover.png │ ├── screenshot (1).png │ ├── screenshot (10).png │ ├── screenshot (11).png │ ├── screenshot (12).png │ ├── screenshot (13).png │ ├── screenshot (2).png │ ├── screenshot (3).png │ ├── screenshot (4).png │ ├── screenshot (5).png │ ├── screenshot (6).png │ ├── screenshot (7).png │ ├── screenshot (8).png │ └── screenshot (9).png └── space.json ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── 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 │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ ├── LaunchBackground.imageset │ │ ├── Contents.json │ │ ├── background.png │ │ └── darkbackground.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── app │ ├── core │ │ ├── theme │ │ │ └── app_theme.dart │ │ └── values │ │ │ └── strings.dart │ ├── data │ │ ├── local │ │ │ ├── base │ │ │ │ ├── i_entity.dart │ │ │ │ └── iservice.dart │ │ │ ├── entities │ │ │ │ └── user_entity.dart │ │ │ └── services │ │ │ │ └── storage_service.dart │ │ └── remote │ │ │ ├── api │ │ │ ├── api_routes.dart │ │ │ ├── dio_Interceptor.dart │ │ │ └── dio_client.dart │ │ │ ├── base │ │ │ ├── idto.dart │ │ │ ├── status.dart │ │ │ └── status.freezed.dart │ │ │ ├── dto │ │ │ ├── application │ │ │ │ ├── application_in_dto.dart │ │ │ │ └── application_out_dto.dart │ │ │ ├── auth │ │ │ │ ├── login_in_dto.dart │ │ │ │ ├── login_out_dto.dart │ │ │ │ ├── register_company_dto.dart │ │ │ │ ├── register_company_out_dto.dart │ │ │ │ ├── register_customer_dto.dart │ │ │ │ └── register_customer_out_dto.dart │ │ │ ├── choices │ │ │ │ └── position_out_dto.dart │ │ │ ├── company │ │ │ │ ├── company_out_dto.dart │ │ │ │ └── company_search_out_dto.dart │ │ │ ├── customer │ │ │ │ ├── customer_profile_out_dto.dart │ │ │ │ ├── single_customer_out_dto.dart │ │ │ │ └── toggle_save_out_dto.dart │ │ │ ├── job │ │ │ │ ├── job_in_dto.dart │ │ │ │ └── job_out_dto.dart │ │ │ └── search │ │ │ │ └── search_out_dto.dart │ │ │ ├── exceptions │ │ │ └── dio_exceptions.dart │ │ │ ├── repositories │ │ │ ├── application │ │ │ │ ├── application_repository.dart │ │ │ │ └── i_application_repository.dart │ │ │ ├── auth │ │ │ │ ├── auth_repository.dart │ │ │ │ └── i_auth_repository.dart │ │ │ ├── company │ │ │ │ ├── company_repository.dart │ │ │ │ └── i_company_repository.dart │ │ │ ├── customer │ │ │ │ ├── customer_repository.dart │ │ │ │ └── i_customer_repository.dart │ │ │ ├── job │ │ │ │ ├── i_job_repository.dart │ │ │ │ └── job_repository.dart │ │ │ ├── position │ │ │ │ ├── i_choice_repository.dart │ │ │ │ └── position_repository.dart │ │ │ └── search │ │ │ │ ├── i_search_repository.dart │ │ │ │ └── search_repository.dart │ │ │ └── services │ │ │ ├── application │ │ │ ├── application_service.dart │ │ │ └── i_application_service.dart │ │ │ ├── auth │ │ │ ├── auth_service.dart │ │ │ └── i_auth_service.dart │ │ │ ├── company │ │ │ ├── comapny_service.dart │ │ │ └── i_company_service.dart │ │ │ ├── customer │ │ │ ├── customer_service.dart │ │ │ └── i_customer_service.dart │ │ │ ├── job │ │ │ ├── i_job_service.dart │ │ │ └── job_service.dart │ │ │ ├── position │ │ │ ├── i_choice_service.dart │ │ │ └── position_choice_service.dart │ │ │ └── search │ │ │ ├── i_search_service.dart │ │ │ └── search_service.dart │ ├── di │ │ └── locator.dart │ ├── domain │ │ └── enums │ │ │ └── user_type.dart │ ├── icons.dart │ ├── modules │ │ ├── JobDetails │ │ │ ├── bindings │ │ │ │ └── job_details_binding.dart │ │ │ ├── controllers │ │ │ │ └── job_details_controller.dart │ │ │ └── views │ │ │ │ ├── job_details_view.dart │ │ │ │ └── widgets │ │ │ │ ├── about_the_employer.dart │ │ │ │ ├── apply_bottom_sheet.dart │ │ │ │ ├── body.dart │ │ │ │ ├── description.dart │ │ │ │ ├── details_bottom_nav_bar.dart │ │ │ │ ├── details_sliver_app_bar.dart │ │ │ │ ├── header.dart │ │ │ │ ├── similar_jobs.dart │ │ │ │ ├── slimilar_job_card.dart │ │ │ │ └── submit_bottom_sheet.dart │ │ ├── auth │ │ │ ├── bindings │ │ │ │ └── auth_binding.dart │ │ │ ├── controllers │ │ │ │ └── auth_controller.dart │ │ │ └── views │ │ │ │ ├── login │ │ │ │ ├── login_view.dart │ │ │ │ └── widgets │ │ │ │ │ ├── body.dart │ │ │ │ │ ├── choose_bottom_sheet.dart │ │ │ │ │ ├── custom_choose_button.dart │ │ │ │ │ └── login_form.dart │ │ │ │ ├── register │ │ │ │ ├── register_view.dart │ │ │ │ └── widgets │ │ │ │ │ ├── body.dart │ │ │ │ │ ├── employee_form.dart │ │ │ │ │ └── employer_form.dart │ │ │ │ └── widgets │ │ │ │ ├── button_with_text.dart │ │ │ │ └── header.dart │ │ ├── companyProfile │ │ │ ├── bindings │ │ │ │ └── company_profile_binding.dart │ │ │ ├── controllers │ │ │ │ └── company_profile_controller.dart │ │ │ └── views │ │ │ │ ├── company_profile_view.dart │ │ │ │ └── widgets │ │ │ │ ├── about_us.dart │ │ │ │ ├── body.dart │ │ │ │ ├── company_profile_sliver_app_bar.dart │ │ │ │ ├── company_tab_view.dart │ │ │ │ ├── company_tap_bar.dart │ │ │ │ ├── header.dart │ │ │ │ ├── jobs_list.dart │ │ │ │ ├── profile_header.dart │ │ │ │ └── sliverPersistentHeaderDelegateImp.dart │ │ ├── customerProfile │ │ │ ├── bindings │ │ │ │ └── customer_profile_binding.dart │ │ │ ├── controllers │ │ │ │ └── customer_profile_controller.dart │ │ │ └── views │ │ │ │ ├── customer_profile_view.dart │ │ │ │ └── widgets │ │ │ │ ├── about_me.dart │ │ │ │ ├── body.dart │ │ │ │ ├── customer_profile_sliver_app_bar.dart │ │ │ │ ├── education.dart │ │ │ │ ├── experience.dart │ │ │ │ ├── header.dart │ │ │ │ ├── languages.dart │ │ │ │ ├── skills.dart │ │ │ │ └── wrapped_chips.dart │ │ ├── home │ │ │ ├── bindings │ │ │ │ └── home_binding.dart │ │ │ ├── controllers │ │ │ │ └── home_controller.dart │ │ │ └── views │ │ │ │ ├── home_view.dart │ │ │ │ └── widgets │ │ │ │ ├── body.dart │ │ │ │ ├── chips_list.dart │ │ │ │ ├── featured_jobs.dart │ │ │ │ └── recent_jobs.dart │ │ ├── root │ │ │ ├── bindings │ │ │ │ └── root_binding.dart │ │ │ ├── controllers │ │ │ │ └── root_controller.dart │ │ │ └── views │ │ │ │ ├── root_view.dart │ │ │ │ └── widgets │ │ │ │ └── menu_view.dart │ │ ├── saved │ │ │ ├── bindings │ │ │ │ └── saved_binding.dart │ │ │ ├── controllers │ │ │ │ └── saved_controller.dart │ │ │ └── views │ │ │ │ ├── saved_view.dart │ │ │ │ └── widgets │ │ │ │ ├── body.dart │ │ │ │ ├── no_saving.dart │ │ │ │ └── saved_jobs.dart │ │ ├── search │ │ │ ├── bindings │ │ │ │ └── search_binding.dart │ │ │ ├── controllers │ │ │ │ └── search_controller.dart │ │ │ └── views │ │ │ │ ├── search_view.dart │ │ │ │ └── widgets │ │ │ │ ├── body.dart │ │ │ │ ├── items_card.dart │ │ │ │ └── search_items.dart │ │ └── waiting │ │ │ ├── bindings │ │ │ └── waiting_binding.dart │ │ │ ├── controllers │ │ │ └── waiting_controller.dart │ │ │ └── views │ │ │ ├── waiting_view.dart │ │ │ └── widgets │ │ │ └── body.dart │ ├── routes │ │ ├── app_pages.dart │ │ └── app_routes.dart │ ├── utils │ │ ├── extensions.dart │ │ ├── functions.dart │ │ └── validators.dart │ └── widgets │ │ ├── custom_appbar.dart │ │ ├── custom_avatar.dart │ │ ├── custom_bottom_sheet.dart │ │ ├── custom_button.dart │ │ ├── custom_chip.dart │ │ ├── custom_info_card.dart │ │ ├── custom_job_card.dart │ │ ├── custom_lottie.dart │ │ ├── custom_save_button.dart │ │ ├── custom_tag.dart │ │ ├── custom_text_field.dart │ │ ├── dialogs.dart │ │ ├── section_header.dart │ │ ├── shimmer │ │ ├── chips_shimmer.dart │ │ ├── company_profile_shimmer.dart │ │ ├── customer_profile_shimmer.dart │ │ ├── featured_job_shimmer.dart │ │ ├── job_details_shimmer.dart │ │ ├── recent_jobs_shimmer.dart │ │ └── shimmer_widget.dart │ │ └── snackbars.dart └── main.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | 44 | # Android Studio will place build artifacts here 45 | /android/app/debug 46 | /android/app/profile 47 | /android/app/release -------------------------------------------------------------------------------- /.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. 5 | 6 | version: 7 | revision: f1875d570e39de09040c8f79aa13cc56baab8db1 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: f1875d570e39de09040c8f79aa13cc56baab8db1 17 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 18 | - platform: android 19 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 20 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 21 | - platform: ios 22 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 23 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 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 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sadeq Al-Mhana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /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/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | ndkVersion flutter.ndkVersion 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.devsadeq.jobs_flutter_app" 48 | // You can update the following values to match your application needs. 49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 50 | minSdkVersion flutter.minSdkVersion 51 | targetSdkVersion flutter.targetSdkVersion 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | } 55 | 56 | buildTypes { 57 | release { 58 | // TODO: Add your own signing config for the release build. 59 | // Signing with the debug keys for now, so `flutter run --release` works. 60 | signingConfig signingConfigs.debug 61 | } 62 | } 63 | } 64 | 65 | flutter { 66 | source '../..' 67 | } 68 | 69 | dependencies { 70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 31 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/devsadeq/jobs_flutter_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.jobs_flutter_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/screenshots/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/cover.png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (1).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (10).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (10).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (11).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (12).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (12).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (13).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (13).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (2).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (3).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (4).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (5).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (6).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (7).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (8).png -------------------------------------------------------------------------------- /assets/screenshots/screenshot (9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/assets/screenshots/screenshot (9).png -------------------------------------------------------------------------------- /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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /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 | 8 | -------------------------------------------------------------------------------- /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 UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "darkbackground.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "appearances" : [ 25 | { 26 | "appearance" : "luminosity", 27 | "value" : "dark" 28 | } 29 | ], 30 | "idiom" : "universal", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "universal", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "appearances" : [ 39 | { 40 | "appearance" : "luminosity", 41 | "value" : "dark" 42 | } 43 | ], 44 | "idiom" : "universal", 45 | "scale" : "3x" 46 | } 47 | ], 48 | "info" : { 49 | "author" : "xcode", 50 | "version" : 1 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/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 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/JobsFlutterApp/bae75ee511308191fa43526bacfbf1fcb6003e26/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@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/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Jobs Flutter App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | jobs_flutter_app 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIStatusBarHidden 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app/data/local/base/i_entity.dart: -------------------------------------------------------------------------------- 1 | abstract class IEntity { 2 | Map toMap(); 3 | 4 | IEntity.fromMap(dynamic map); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/local/base/iservice.dart: -------------------------------------------------------------------------------- 1 | import 'i_entity.dart'; 2 | 3 | abstract class IService { 4 | Future read({required String key}); 5 | 6 | Future write({required String key, required IEntity entity}); 7 | 8 | Future remove({required String key}); 9 | } 10 | -------------------------------------------------------------------------------- /lib/app/data/local/entities/user_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/local/base/i_entity.dart'; 2 | 3 | class UserEntity implements IEntity { 4 | String? id; 5 | String? name; 6 | String? email; 7 | String? phoneNumber; 8 | String? token; 9 | String? status; 10 | String? role; 11 | 12 | UserEntity({ 13 | required this.id, 14 | required this.name, 15 | required this.email, 16 | required this.phoneNumber, 17 | required this.token, 18 | required this.status, 19 | required this.role, 20 | }); 21 | 22 | @override 23 | UserEntity.fromMap(dynamic map) { 24 | id = map['id']; 25 | name = map['name']; 26 | email = map['email']; 27 | phoneNumber = map['phone']; 28 | token = map['token']; 29 | status = map['status']; 30 | role = map['role']; 31 | } 32 | 33 | @override 34 | Map toMap() { 35 | final map = {}; 36 | map['id'] = id; 37 | map['name'] = name; 38 | map['email'] = email; 39 | map['phone'] = phoneNumber; 40 | map['token'] = token; 41 | map['role'] = role; 42 | map['status'] = status; 43 | return map; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/app/data/local/services/storage_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_storage/get_storage.dart'; 2 | import 'package:jobs_flutter_app/app/data/local/base/iservice.dart'; 3 | 4 | import '../base/i_entity.dart'; 5 | 6 | class StorageService implements IService { 7 | final GetStorage _storage; 8 | 9 | StorageService(this._storage); 10 | 11 | @override 12 | Future read({required String key}) async { 13 | try { 14 | return await _storage.read(key); 15 | } catch (e) { 16 | rethrow; 17 | } 18 | } 19 | 20 | @override 21 | Future write({required String key, required IEntity entity}) async { 22 | try { 23 | await _storage.write(key, entity.toMap()); 24 | } catch (e) { 25 | rethrow; 26 | } 27 | } 28 | 29 | @override 30 | Future remove({required String key}) async { 31 | try { 32 | await _storage.remove(key); 33 | } catch (e) { 34 | rethrow; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/app/data/remote/api/api_routes.dart: -------------------------------------------------------------------------------- 1 | class ApiRoutes { 2 | static const BASE_URL = "https://kasmtj.pythonanywhere.com"; 3 | static const _API = "/api"; 4 | 5 | static const JOBS = "$_API/jobs/"; 6 | static const POSITIONS = "$_API/meta/get_all_job_titles"; 7 | static const SEARCH = "$_API/companies/search"; 8 | static const COMPANIES = "$_API/companies/"; 9 | static const COMPANY_REGISTER = "$_API/auth/company_signup"; 10 | static const CUSTOMER_REGISTER = "$_API/auth/customer_signup"; 11 | static const LOGIN = "$_API/auth/login"; 12 | static const CUSTOMERS = "$_API/customers"; 13 | static const SAVED_JOBS = "$CUSTOMERS/get_all_saved_jobs/"; 14 | static const TOGGLE_SAVE = "$CUSTOMERS/save/"; 15 | static const APPLICATIONS = "$_API/applications/"; 16 | static const JOB_APPLY = "$CUSTOMERS/Aplly/"; 17 | } 18 | -------------------------------------------------------------------------------- /lib/app/data/remote/api/dio_Interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:logger/logger.dart'; 3 | 4 | import '../../../modules/auth/controllers/auth_controller.dart'; 5 | 6 | class DioInterceptor extends Interceptor { 7 | final logger = Logger(printer: PrettyPrinter()); 8 | final loggerNoStack = Logger(printer: PrettyPrinter(methodCount: 0)); 9 | 10 | @override 11 | void onRequest(RequestOptions options, RequestInterceptorHandler handler) { 12 | options.headers['Authorization'] = 13 | "Bearer ${AuthController.to.currentUser?.token}"; 14 | 15 | logger.d("On new Request"); 16 | logger.i("Headers: \n${options.headers.toString()}"); 17 | super.onRequest(options, handler); 18 | } 19 | 20 | @override 21 | void onResponse(Response response, ResponseInterceptorHandler handler) { 22 | logger.d("On new Response."); 23 | loggerNoStack.i("Status Code: ${response.statusCode}"); 24 | loggerNoStack.i("Status Msg: ${response.statusMessage}"); 25 | loggerNoStack.i("Headers: \n${response.headers.toString()}"); 26 | loggerNoStack.v(response.data.toString()); 27 | super.onResponse(response, handler); 28 | } 29 | 30 | @override 31 | void onError(DioError err, ErrorInterceptorHandler handler) { 32 | logger.d("On Error."); 33 | loggerNoStack.e("Error Msg: ${err.message}"); 34 | loggerNoStack.e("Error Type: ${err.type}"); 35 | loggerNoStack.e("Error Response: ${err.response.toString()}"); 36 | super.onError(err, handler); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/app/data/remote/base/idto.dart: -------------------------------------------------------------------------------- 1 | abstract class IDto { 2 | Map toJson(); 3 | 4 | IDto.fromJson(dynamic json); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/base/status.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'status.freezed.dart'; 5 | 6 | @freezed 7 | abstract class Status with _$State { 8 | const factory Status.idle() = Idle; 9 | 10 | const factory Status.loading() = Loading; 11 | 12 | const factory Status.success({@required T? data}) = Success; 13 | 14 | const factory Status.failure({@required String? reason}) = Failure; 15 | } 16 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/application/application_in_dto.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | 3 | class ApplicationInDto implements IDto { 4 | ApplicationInDto({ 5 | this.customerId, 6 | this.jobId, 7 | this.whyApply, 8 | }); 9 | 10 | ApplicationInDto.fromJson(dynamic json) { 11 | customerId = json['customer_id']; 12 | jobId = json['job_id']; 13 | whyApply = json['why_apply']; 14 | } 15 | 16 | String? customerId; 17 | String? jobId; 18 | String? whyApply; 19 | 20 | ApplicationInDto copyWith({ 21 | String? customerId, 22 | String? jobId, 23 | String? whyApply, 24 | }) => 25 | ApplicationInDto( 26 | customerId: customerId ?? this.customerId, 27 | jobId: jobId ?? this.jobId, 28 | whyApply: whyApply ?? this.whyApply, 29 | ); 30 | 31 | Map toJson() { 32 | final map = {}; 33 | map['customer_id'] = customerId; 34 | map['job_id'] = jobId; 35 | map['why_apply'] = whyApply; 36 | return map; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/application/application_out_dto.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | 3 | class ApplicationOutDto implements IDto { 4 | ApplicationOutDto({ 5 | this.id, 6 | this.customer, 7 | this.job, 8 | this.whyApply, 9 | }); 10 | 11 | ApplicationOutDto.fromJson(dynamic json) { 12 | id = json['id']; 13 | customer = 14 | json['customer'] != null ? Customer.fromJson(json['customer']) : null; 15 | job = json['job'] != null ? Job.fromJson(json['job']) : null; 16 | whyApply = json['why_apply']; 17 | } 18 | 19 | String? id; 20 | Customer? customer; 21 | Job? job; 22 | String? whyApply; 23 | 24 | ApplicationOutDto copyWith({ 25 | String? id, 26 | Customer? customer, 27 | Job? job, 28 | String? whyApply, 29 | }) => 30 | ApplicationOutDto( 31 | id: id ?? this.id, 32 | customer: customer ?? this.customer, 33 | job: job ?? this.job, 34 | whyApply: whyApply ?? this.whyApply, 35 | ); 36 | 37 | Map toJson() { 38 | final map = {}; 39 | map['id'] = id; 40 | if (customer != null) { 41 | map['customer'] = customer?.toJson(); 42 | } 43 | if (job != null) { 44 | map['job'] = job?.toJson(); 45 | } 46 | map['why_apply'] = whyApply; 47 | return map; 48 | } 49 | } 50 | 51 | class Job { 52 | Job({ 53 | this.id, 54 | }); 55 | 56 | Job.fromJson(dynamic json) { 57 | id = json['id']; 58 | } 59 | 60 | String? id; 61 | 62 | Job copyWith({ 63 | String? id, 64 | }) => 65 | Job( 66 | id: id ?? this.id, 67 | ); 68 | 69 | Map toJson() { 70 | final map = {}; 71 | map['id'] = id; 72 | return map; 73 | } 74 | } 75 | 76 | class Customer { 77 | Customer({ 78 | this.id, 79 | this.name, 80 | this.jobTitle, 81 | this.image, 82 | }); 83 | 84 | Customer.fromJson(dynamic json) { 85 | id = json['id']; 86 | name = json['name']; 87 | jobTitle = json['job_title']; 88 | image = json['image']; 89 | } 90 | 91 | String? id; 92 | String? name; 93 | String? jobTitle; 94 | String? image; 95 | 96 | Customer copyWith({ 97 | String? id, 98 | String? name, 99 | String? jobTitle, 100 | String? image, 101 | }) => 102 | Customer( 103 | id: id ?? this.id, 104 | name: name ?? this.name, 105 | jobTitle: jobTitle ?? this.jobTitle, 106 | image: image ?? this.image, 107 | ); 108 | 109 | Map toJson() { 110 | final map = {}; 111 | map['id'] = id; 112 | map['name'] = name; 113 | map['job_title'] = jobTitle; 114 | map['image'] = image; 115 | return map; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/login_in_dto.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | 3 | class LoginInDto implements IDto { 4 | LoginInDto({ 5 | this.emailOrPhone, 6 | this.password, 7 | }); 8 | 9 | String? emailOrPhone; 10 | String? password; 11 | 12 | LoginInDto.fromJson(dynamic json) { 13 | emailOrPhone = json['email_or_phone']; 14 | password = json['password']; 15 | } 16 | 17 | LoginInDto copyWith({ 18 | String? email, 19 | String? password, 20 | }) => 21 | LoginInDto( 22 | emailOrPhone: email ?? this.emailOrPhone, 23 | password: password ?? this.password, 24 | ); 25 | 26 | Map toJson() { 27 | final map = {}; 28 | map['email_or_phone'] = emailOrPhone; 29 | map['password'] = password; 30 | return map; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/login_out_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class LoginOutDto implements IDto { 4 | LoginOutDto({ 5 | this.token, 6 | this.id, 7 | this.name, 8 | this.email, 9 | this.role, 10 | this.status, 11 | }); 12 | 13 | LoginOutDto.fromJson(dynamic json) { 14 | token = json['token'] != null ? Token.fromJson(json['token']) : null; 15 | id = json['id']; 16 | name = json['name']; 17 | email = json['email']; 18 | role = json['role']; 19 | status = json['status']; 20 | } 21 | 22 | Token? token; 23 | String? id; 24 | String? name; 25 | String? email; 26 | String? role; 27 | String? status; 28 | 29 | LoginOutDto copyWith({ 30 | Token? token, 31 | String? id, 32 | String? name, 33 | String? email, 34 | String? status, 35 | }) => 36 | LoginOutDto( 37 | token: token ?? this.token, 38 | id: id ?? this.id, 39 | name: name ?? this.name, 40 | email: email ?? this.email, 41 | status: status ?? this.status, 42 | ); 43 | 44 | Map toJson() { 45 | final map = {}; 46 | if (token != null) { 47 | map['token'] = token?.toJson(); 48 | } 49 | map['id'] = id; 50 | map['name'] = name; 51 | map['email'] = email; 52 | map['role'] = role; 53 | map['status'] = status; 54 | return map; 55 | } 56 | } 57 | 58 | class Token { 59 | Token({ 60 | this.access, 61 | }); 62 | 63 | Token.fromJson(dynamic json) { 64 | access = json['access']; 65 | } 66 | 67 | String? access; 68 | 69 | Token copyWith({ 70 | String? access, 71 | }) => 72 | Token( 73 | access: access ?? this.access, 74 | ); 75 | 76 | Map toJson() { 77 | final map = {}; 78 | map['access'] = access; 79 | return map; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/register_company_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class RegisterCompanyDto implements IDto { 4 | RegisterCompanyDto({ 5 | this.name, 6 | this.phone, 7 | this.email, 8 | this.address, 9 | this.country, 10 | this.password, 11 | }); 12 | 13 | RegisterCompanyDto.fromJson(dynamic json) { 14 | name = json['name']; 15 | phone = json['phone']; 16 | email = json['email']; 17 | address = json['address']; 18 | country = json['country']; 19 | password = json['password']; 20 | } 21 | 22 | String? name; 23 | String? phone; 24 | String? email; 25 | String? address; 26 | String? country; 27 | String? password; 28 | 29 | RegisterCompanyDto copyWith({ 30 | String? name, 31 | String? phone, 32 | String? email, 33 | String? address, 34 | String? country, 35 | String? password, 36 | }) => 37 | RegisterCompanyDto( 38 | name: name ?? this.name, 39 | phone: phone ?? this.phone, 40 | email: email ?? this.email, 41 | address: address ?? this.address, 42 | country: country ?? this.country, 43 | password: password ?? this.password, 44 | ); 45 | 46 | Map toJson() { 47 | final map = {}; 48 | map['name'] = name; 49 | map['phone'] = phone; 50 | map['email'] = email; 51 | map['address'] = address; 52 | map['country'] = country; 53 | map['password'] = password; 54 | return map; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/register_company_out_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class RegisterCompanyOutDto implements IDto { 4 | RegisterCompanyOutDto({ 5 | this.token, 6 | this.company, 7 | }); 8 | 9 | RegisterCompanyOutDto.fromJson(dynamic json) { 10 | token = json['token'] != null ? Token.fromJson(json['token']) : null; 11 | company = 12 | json['company'] != null ? Company.fromJson(json['company']) : null; 13 | } 14 | 15 | Token? token; 16 | Company? company; 17 | 18 | RegisterCompanyOutDto copyWith({ 19 | Token? token, 20 | Company? company, 21 | }) => 22 | RegisterCompanyOutDto( 23 | token: token ?? this.token, 24 | company: company ?? this.company, 25 | ); 26 | 27 | Map toJson() { 28 | final map = {}; 29 | if (token != null) { 30 | map['token'] = token?.toJson(); 31 | } 32 | if (company != null) { 33 | map['company'] = company?.toJson(); 34 | } 35 | return map; 36 | } 37 | } 38 | 39 | class Company { 40 | Company({ 41 | this.id, 42 | this.name, 43 | this.phone, 44 | this.email, 45 | }); 46 | 47 | Company.fromJson(dynamic json) { 48 | id = json['id']; 49 | name = json['name']; 50 | phone = json['phone']; 51 | email = json['email']; 52 | } 53 | 54 | String? id; 55 | String? name; 56 | String? phone; 57 | String? email; 58 | 59 | Company copyWith({ 60 | String? id, 61 | String? name, 62 | String? phone, 63 | String? email, 64 | }) => 65 | Company( 66 | id: id ?? this.id, 67 | name: name ?? this.name, 68 | phone: phone ?? this.phone, 69 | email: email ?? this.email, 70 | ); 71 | 72 | Map toJson() { 73 | final map = {}; 74 | map['id'] = id; 75 | map['name'] = name; 76 | map['phone'] = phone; 77 | map['email'] = email; 78 | return map; 79 | } 80 | } 81 | 82 | class Token { 83 | Token({ 84 | this.access, 85 | }); 86 | 87 | Token.fromJson(dynamic json) { 88 | access = json['access']; 89 | } 90 | 91 | String? access; 92 | 93 | Token copyWith({ 94 | String? access, 95 | }) => 96 | Token( 97 | access: access ?? this.access, 98 | ); 99 | 100 | Map toJson() { 101 | final map = {}; 102 | map['access'] = access; 103 | return map; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/register_customer_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class RegisterCustomerDto implements IDto { 4 | RegisterCustomerDto({ 5 | this.name, 6 | this.email, 7 | this.phone, 8 | this.password, 9 | }); 10 | 11 | RegisterCustomerDto.fromJson(dynamic json) { 12 | name = json['name']; 13 | email = json['email']; 14 | phone = json['phone']; 15 | password = json['password']; 16 | } 17 | 18 | String? name; 19 | String? email; 20 | String? phone; 21 | String? password; 22 | 23 | RegisterCustomerDto copyWith({ 24 | String? name, 25 | String? email, 26 | String? phone, 27 | String? password, 28 | }) => 29 | RegisterCustomerDto( 30 | name: name ?? this.name, 31 | email: email ?? this.email, 32 | phone: phone ?? this.phone, 33 | password: password ?? this.password, 34 | ); 35 | 36 | @override 37 | Map toJson() { 38 | final map = {}; 39 | map['name'] = name; 40 | map['email'] = email; 41 | map['phone'] = phone; 42 | map['password'] = password; 43 | return map; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/auth/register_customer_out_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class RegisterCustomerOutDto implements IDto { 4 | RegisterCustomerOutDto({ 5 | this.token, 6 | this.customer, 7 | }); 8 | 9 | RegisterCustomerOutDto.fromJson(dynamic json) { 10 | token = json['token'] != null ? Token.fromJson(json['token']) : null; 11 | customer = 12 | json['customer'] != null ? Customer.fromJson(json['customer']) : null; 13 | } 14 | 15 | Token? token; 16 | Customer? customer; 17 | 18 | RegisterCustomerOutDto copyWith({ 19 | Token? token, 20 | Customer? customer, 21 | }) => 22 | RegisterCustomerOutDto( 23 | token: token ?? this.token, 24 | customer: customer ?? this.customer, 25 | ); 26 | 27 | Map toJson() { 28 | final map = {}; 29 | if (token != null) { 30 | map['token'] = token?.toJson(); 31 | } 32 | if (customer != null) { 33 | map['customer'] = customer?.toJson(); 34 | } 35 | return map; 36 | } 37 | } 38 | 39 | class Customer { 40 | Customer({ 41 | this.id, 42 | this.name, 43 | this.email, 44 | this.phone, 45 | }); 46 | 47 | Customer.fromJson(dynamic json) { 48 | id = json['id']; 49 | name = json['name']; 50 | email = json['email']; 51 | phone = json['phone']; 52 | } 53 | 54 | String? id; 55 | String? name; 56 | String? email; 57 | String? phone; 58 | 59 | Customer copyWith({ 60 | String? id, 61 | String? name, 62 | String? email, 63 | String? phone, 64 | }) => 65 | Customer( 66 | id: id ?? this.id, 67 | name: name ?? this.name, 68 | email: email ?? this.email, 69 | phone: phone ?? this.phone, 70 | ); 71 | 72 | Map toJson() { 73 | final map = {}; 74 | map['id'] = id; 75 | map['name'] = name; 76 | map['email'] = email; 77 | map['phone'] = phone; 78 | return map; 79 | } 80 | } 81 | 82 | class Token { 83 | Token({ 84 | this.access, 85 | }); 86 | 87 | Token.fromJson(dynamic json) { 88 | access = json['access']; 89 | } 90 | 91 | String? access; 92 | 93 | Token copyWith({ 94 | String? access, 95 | }) => 96 | Token( 97 | access: access ?? this.access, 98 | ); 99 | 100 | Map toJson() { 101 | final map = {}; 102 | map['access'] = access; 103 | return map; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/choices/position_out_dto.dart: -------------------------------------------------------------------------------- 1 | class PositionOutDto { 2 | PositionOutDto({ 3 | this.jobTitle, 4 | }); 5 | 6 | PositionOutDto.fromJson(dynamic json) { 7 | jobTitle = json['job_title']; 8 | } 9 | 10 | String? jobTitle; 11 | 12 | PositionOutDto copyWith({ 13 | String? jobTitle, 14 | }) => 15 | PositionOutDto( 16 | jobTitle: jobTitle ?? this.jobTitle, 17 | ); 18 | 19 | Map toJson() { 20 | final map = {}; 21 | map['job_title'] = jobTitle; 22 | return map; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/company/company_out_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class CompanyOutDto implements IDto { 4 | CompanyOutDto({ 5 | this.id, 6 | this.name, 7 | this.email, 8 | this.phone, 9 | this.description, 10 | this.workType, 11 | this.city, 12 | this.address, 13 | this.image, 14 | }); 15 | 16 | CompanyOutDto.fromJson(dynamic json) { 17 | id = json['id']; 18 | name = json['name']; 19 | email = json['email']; 20 | phone = json['phone']; 21 | description = json['description']; 22 | workType = json['work_type']; 23 | city = json['city']; 24 | address = json['address']; 25 | image = json['image']; 26 | } 27 | 28 | String? id; 29 | String? name; 30 | String? email; 31 | String? phone; 32 | String? description; 33 | String? workType; 34 | String? city; 35 | String? address; 36 | String? image; 37 | 38 | CompanyOutDto copyWith({ 39 | String? id, 40 | String? name, 41 | String? email, 42 | String? phone, 43 | String? description, 44 | String? workType, 45 | String? city, 46 | String? address, 47 | String? image, 48 | }) => 49 | CompanyOutDto( 50 | id: id ?? this.id, 51 | name: name ?? this.name, 52 | email: email ?? this.email, 53 | phone: phone ?? this.phone, 54 | description: description ?? this.description, 55 | workType: workType ?? this.workType, 56 | city: city ?? this.city, 57 | address: address ?? this.address, 58 | image: image ?? this.image, 59 | ); 60 | 61 | Map toJson() { 62 | final map = {}; 63 | map['id'] = id; 64 | map['name'] = name; 65 | map['email'] = email; 66 | map['phone'] = phone; 67 | map['description'] = description; 68 | map['work_type'] = workType; 69 | map['city'] = city; 70 | map['address'] = address; 71 | map['image'] = image; 72 | return map; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/company/company_search_out_dto.dart: -------------------------------------------------------------------------------- 1 | class CompanySearchOutDto { 2 | CompanySearchOutDto({ 3 | this.id, 4 | this.name, 5 | this.image, 6 | this.workType, 7 | }); 8 | 9 | CompanySearchOutDto.fromJson(dynamic json) { 10 | id = json['id']; 11 | name = json['name']; 12 | image = json['image']; 13 | workType = json['work_type']; 14 | } 15 | 16 | String? id; 17 | String? name; 18 | String? image; 19 | String? workType; 20 | 21 | CompanySearchOutDto copyWith({ 22 | String? id, 23 | String? name, 24 | String? image, 25 | String? workType, 26 | }) => 27 | CompanySearchOutDto( 28 | id: id ?? this.id, 29 | name: name ?? this.name, 30 | image: image ?? this.image, 31 | workType: workType ?? this.workType, 32 | ); 33 | 34 | Map toJson() { 35 | final map = {}; 36 | map['id'] = id; 37 | map['name'] = name; 38 | map['image'] = image; 39 | map['work_type'] = workType; 40 | return map; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/customer/single_customer_out_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | class SingleCustomerOutDto implements IDto { 4 | SingleCustomerOutDto({ 5 | this.id, 6 | this.name, 7 | this.email, 8 | this.phone, 9 | }); 10 | 11 | SingleCustomerOutDto.fromJson(dynamic json) { 12 | id = json['id']; 13 | name = json['name']; 14 | email = json['email']; 15 | phone = json['phone']; 16 | } 17 | 18 | String? id; 19 | String? name; 20 | String? email; 21 | String? phone; 22 | 23 | SingleCustomerOutDto copyWith({ 24 | String? id, 25 | String? name, 26 | String? email, 27 | String? phone, 28 | }) => 29 | SingleCustomerOutDto( 30 | id: id ?? this.id, 31 | name: name ?? this.name, 32 | email: email ?? this.email, 33 | phone: phone ?? this.phone, 34 | ); 35 | 36 | Map toJson() { 37 | final map = {}; 38 | map['id'] = id; 39 | map['name'] = name; 40 | map['email'] = email; 41 | map['phone'] = phone; 42 | return map; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/customer/toggle_save_out_dto.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | 3 | class ToggleSaveOutDto implements IDto { 4 | ToggleSaveOutDto({ 5 | this.message, 6 | this.saved, 7 | }); 8 | 9 | ToggleSaveOutDto.fromJson(dynamic json) { 10 | message = json['message']; 11 | saved = json['saved']; 12 | } 13 | 14 | String? message; 15 | bool? saved; 16 | 17 | ToggleSaveOutDto copyWith({ 18 | String? message, 19 | bool? saved, 20 | }) => 21 | ToggleSaveOutDto( 22 | message: message ?? this.message, 23 | saved: saved ?? this.saved, 24 | ); 25 | 26 | Map toJson() { 27 | final map = {}; 28 | map['message'] = message; 29 | map['saved'] = saved; 30 | return map; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/job/job_in_dto.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | 3 | class JobInDto implements IDto { 4 | JobInDto({ 5 | this.companyId, 6 | this.position, 7 | this.employmentType, 8 | this.description, 9 | this.location, 10 | this.workplace, 11 | }); 12 | 13 | JobInDto.fromJson(dynamic json) { 14 | companyId = json['company_id']; 15 | position = json['position']; 16 | employmentType = json['employment_type']; 17 | description = json['description']; 18 | location = json['location']; 19 | workplace = json['workplace']; 20 | } 21 | 22 | String? companyId; 23 | String? position; 24 | String? employmentType; 25 | String? description; 26 | String? location; 27 | String? workplace; 28 | 29 | JobInDto copyWith({ 30 | String? companyId, 31 | String? position, 32 | String? employmentType, 33 | String? description, 34 | String? location, 35 | String? workplace, 36 | }) => 37 | JobInDto( 38 | companyId: companyId ?? this.companyId, 39 | position: position ?? this.position, 40 | employmentType: employmentType ?? this.employmentType, 41 | description: description ?? this.description, 42 | location: location ?? this.location, 43 | workplace: workplace ?? this.workplace, 44 | ); 45 | 46 | Map toJson() { 47 | final map = {}; 48 | map['company_id'] = companyId; 49 | map['position'] = position; 50 | map['employment_type'] = employmentType; 51 | map['description'] = description; 52 | map['location'] = location; 53 | map['workplace'] = workplace; 54 | return map; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/data/remote/dto/search/search_out_dto.dart: -------------------------------------------------------------------------------- 1 | class SearchOutDto { 2 | SearchOutDto({ 3 | this.id, 4 | this.name, 5 | this.image, 6 | }); 7 | 8 | SearchOutDto.fromJson(dynamic json) { 9 | id = json['id']; 10 | name = json['name']; 11 | image = json['image']; 12 | } 13 | 14 | String? id; 15 | String? name; 16 | String? image; 17 | 18 | SearchOutDto copyWith({ 19 | String? id, 20 | String? name, 21 | String? image, 22 | }) => 23 | SearchOutDto( 24 | id: id ?? this.id, 25 | name: name ?? this.name, 26 | image: image ?? this.image, 27 | ); 28 | 29 | Map toJson() { 30 | final map = {}; 31 | map['id'] = id; 32 | map['name'] = name; 33 | map['image'] = image; 34 | return map; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/app/data/remote/exceptions/dio_exceptions.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class DioExceptions implements Exception { 4 | late String message; 5 | 6 | static const Map _errorCodes = { 7 | '400': 'Bad Request', 8 | '401': 'Unauthorized', 9 | '403': 'Forbidden', 10 | '404': 'Not Found', 11 | '408': 'Request Timeout', 12 | '500': 'Internal Server Error', 13 | '502': 'Bad Gateway', 14 | '503': 'Service Unavailable', 15 | '504': 'Gateway Timeout', 16 | '505': 'HTTP Version Not Supported', 17 | '511': 'Network Authentication Required', 18 | '520': 'Unknown Error', 19 | '521': 'Web Server Is Down', 20 | '522': 'Connection Timed Out', 21 | '523': 'Origin Is Unreachable', 22 | '524': 'A Timeout Occurred', 23 | '525': 'SSL Handshake Failed', 24 | '526': 'Invalid SSL Certificate', 25 | '530': 'Origin DNS Error', 26 | '598': 'Network Read Timeout Error', 27 | '599': 'Network Connect Timeout Error', 28 | }; 29 | 30 | static const Map _errorTypes = { 31 | 'cancel': 'Request to API server was cancelled', 32 | 'connectTimeout': 'Connection timeout with API server', 33 | 'default': 'Connection to API server failed due to internet connection', 34 | 'receiveTimeout': 'Receive timeout in connection with API server', 35 | 'sendTimeout': 'Send timeout in connection with API server', 36 | 'other': 'Connection to API server failed due to internet connection', 37 | }; 38 | 39 | DioExceptions.fromDioError(DioError dioError) { 40 | message = _getErrorMessage(dioError); 41 | } 42 | 43 | String _getErrorMessage(DioError error) { 44 | String errorType = error.type.toString().split('.').last; 45 | String errorMessage = _errorTypes[errorType] ?? 46 | error.response?.data['error'] ?? 47 | _errorCodes[error.response?.statusCode.toString()] ?? 48 | 'Something went wrong'; 49 | return errorMessage; 50 | } 51 | 52 | @override 53 | String toString() => message; 54 | } 55 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/application/application_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../base/idto.dart'; 4 | import '../../base/status.dart'; 5 | import '../../dto/application/application_out_dto.dart'; 6 | import '../../exceptions/dio_exceptions.dart'; 7 | import '../../services/application/i_application_service.dart'; 8 | import 'i_application_repository.dart'; 9 | 10 | class ApplicationRepository implements IApplicationRepository { 11 | final IApplicationService service; 12 | 13 | ApplicationRepository({required this.service}); 14 | 15 | @override 16 | Future> create({required IDto dto}) async { 17 | try { 18 | await service.create(dto: dto); 19 | return const Status.success(data: true); 20 | } on DioError catch (e) { 21 | final errMsg = DioExceptions.fromDioError(e).toString(); 22 | return Status.failure(reason: errMsg); 23 | } 24 | } 25 | 26 | @override 27 | Future> delete({required String applicationId}) async { 28 | try { 29 | await service.delete(applicationId: applicationId); 30 | return const Status.success(data: true); 31 | } on DioError catch (e) { 32 | final errMsg = DioExceptions.fromDioError(e).toString(); 33 | return Status.failure(reason: errMsg); 34 | } 35 | } 36 | 37 | @override 38 | Future> get({required String applicationId}) async { 39 | try { 40 | final response = await service.get(applicationId: applicationId); 41 | return Status.success(data: ApplicationOutDto.fromJson(response.data)); 42 | } on DioError catch (e) { 43 | final errMsg = DioExceptions.fromDioError(e).toString(); 44 | return Status.failure(reason: errMsg); 45 | } 46 | } 47 | 48 | @override 49 | Future>> getAll({ 50 | int? limit, 51 | int? offset, 52 | }) async { 53 | try { 54 | final response = await service.getAll(limit: limit, offset: offset); 55 | final applications = (response.data['items'] as List) 56 | .map((e) => ApplicationOutDto.fromJson(e)) 57 | .toList(); 58 | return Status.success(data: applications); 59 | } on DioError catch (e) { 60 | final errMsg = DioExceptions.fromDioError(e).toString(); 61 | return Status.failure(reason: errMsg); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/application/i_application_repository.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | import '../../base/status.dart'; 3 | import '../../dto/application/application_out_dto.dart'; 4 | 5 | abstract class IApplicationRepository { 6 | Future> create({required IDto dto}); 7 | 8 | Future>> getAll({int? limit, int? offset}); 9 | 10 | Future> get({required String applicationId}); 11 | 12 | Future> delete({required String applicationId}); 13 | } 14 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/auth/i_auth_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | 3 | import '../../../local/base/i_entity.dart'; 4 | 5 | abstract class IAuthRepository { 6 | Future login({required IDto dto}); 7 | 8 | Future registerCompany({required IDto dto}); 9 | 10 | Future registerCustomer({required IDto dto}); 11 | 12 | Future readStorage({required String key}); 13 | 14 | Future writeStorage({required String key, required IEntity entity}); 15 | 16 | Future removeStorage({required String key}); 17 | } 18 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/company/company_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../base/status.dart'; 4 | import '../../dto/company/Company_out_dto.dart'; 5 | import '../../exceptions/dio_exceptions.dart'; 6 | import '../../services/company/i_company_service.dart'; 7 | import 'i_company_repository.dart'; 8 | 9 | class CompanyRepository implements ICompanyRepository { 10 | final ICompanyService service; 11 | 12 | CompanyRepository({required this.service}); 13 | 14 | @override 15 | Future> get({required String uuid}) async { 16 | try { 17 | final response = await service.get(uuid: uuid); 18 | if (response.statusCode == 200) { 19 | final CompanyOutDto job = CompanyOutDto.fromJson(response.data); 20 | return Status.success(data: job); 21 | } 22 | return const Status.failure(reason: "Something went wrong!"); 23 | } on DioError catch (e) { 24 | final errMsg = DioExceptions.fromDioError(e).toString(); 25 | return Status.failure(reason: errMsg); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/company/i_company_repository.dart: -------------------------------------------------------------------------------- 1 | import '../../base/status.dart'; 2 | 3 | abstract class ICompanyRepository { 4 | Future> get({required String uuid}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/customer/i_customer_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:jobs_flutter_app/app/data/remote/base/idto.dart'; 2 | import 'package:jobs_flutter_app/app/data/remote/base/status.dart'; 3 | 4 | abstract class ICustomerRepository { 5 | Future> toggleSave({ 6 | required String customerUuid, 7 | required String jobUuid, 8 | }); 9 | 10 | Future>> getAllSavedJobs({ 11 | int? limit, 12 | int? offset, 13 | required String customerUuid, 14 | }); 15 | 16 | Future> getProfile({required String customerUuid}); 17 | 18 | Future> getAvatar({required String customerUuid}); 19 | } 20 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/job/i_job_repository.dart: -------------------------------------------------------------------------------- 1 | import '../../base/idto.dart'; 2 | import '../../base/status.dart'; 3 | 4 | abstract class IJobRepository { 5 | Future create({required IDto dto}); 6 | 7 | Future delete({required String uuid}); 8 | 9 | Future> get({required String uuid}); 10 | 11 | Future>?> getAll({ 12 | int? limit, 13 | int? offset, 14 | bool? isFeatured, 15 | String? position, 16 | String? companyId, 17 | }); 18 | 19 | Future update({required String uuid, required IDto dto}); 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/position/i_choice_repository.dart: -------------------------------------------------------------------------------- 1 | import '../../base/status.dart'; 2 | 3 | abstract class IChoiceRepository { 4 | Future>> getAll({int? limit, int? offset}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/position/position_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../base/status.dart'; 4 | import '../../dto/choices/Position_out_dto.dart'; 5 | import '../../exceptions/dio_exceptions.dart'; 6 | import '../../services/position/i_choice_service.dart'; 7 | import 'i_choice_repository.dart'; 8 | 9 | class PositionRepository extends IChoiceRepository { 10 | final IChoiceService service; 11 | 12 | PositionRepository({required this.service}); 13 | 14 | @override 15 | Future>> getAll({int? limit, int? offset}) async { 16 | try { 17 | final response = await service.getAll(limit: limit, offset: offset); 18 | final positions = (response.data as List) 19 | .map((e) => PositionOutDto.fromJson(e)) 20 | .toList(); 21 | if (response.statusCode == 200) return Status.success(data: positions); 22 | return const Status.failure(reason: "Some thing wrong happen!"); 23 | } on DioError catch (e) { 24 | final errMsg = DioExceptions.fromDioError(e).toString(); 25 | return Status.failure(reason: errMsg); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/search/i_search_repository.dart: -------------------------------------------------------------------------------- 1 | import '../../base/status.dart'; 2 | 3 | abstract class ISearchRepository { 4 | Future>?> getAll({ 5 | int? limit, 6 | int? offset, 7 | String? q, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/app/data/remote/repositories/search/search_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../base/status.dart'; 4 | import '../../dto/search/search_out_dto.dart'; 5 | import '../../exceptions/dio_exceptions.dart'; 6 | import '../../services/search/i_search_service.dart'; 7 | import 'i_search_repository.dart'; 8 | 9 | class SearchRepository extends ISearchRepository { 10 | final ISearchService service; 11 | 12 | SearchRepository({required this.service}); 13 | 14 | @override 15 | Future>> getAll( 16 | {int? limit, int? offset, String? q}) async { 17 | try { 18 | final response = await service.getAll(limit: limit, offset: offset, q: q); 19 | final results = 20 | (response.data as List).map((e) => SearchOutDto.fromJson(e)).toList(); 21 | if (response.statusCode == 200) return Status.success(data: results); 22 | return const Status.failure(reason: "Some thing wrong happen!"); 23 | } on DioError catch (e) { 24 | final errMsg = DioExceptions.fromDioError(e).toString(); 25 | return Status.failure(reason: errMsg); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/application/application_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import '../../base/idto.dart'; 6 | import 'i_application_service.dart'; 7 | 8 | class ApplicationService implements IApplicationService { 9 | final DioClient dioClient; 10 | 11 | ApplicationService({required this.dioClient}); 12 | 13 | @override 14 | Future delete({required String applicationId}) async { 15 | try { 16 | return await dioClient.delete("${ApiRoutes.APPLICATIONS}$applicationId"); 17 | } catch (e) { 18 | rethrow; 19 | } 20 | } 21 | 22 | @override 23 | Future get({required String applicationId}) async { 24 | try { 25 | return await dioClient.get("${ApiRoutes.APPLICATIONS}$applicationId"); 26 | } catch (e) { 27 | rethrow; 28 | } 29 | } 30 | 31 | @override 32 | Future getAll({int? limit, int? offset}) async { 33 | try { 34 | return await dioClient.get(ApiRoutes.APPLICATIONS, queryParameters: { 35 | "limit": limit ?? 20, 36 | "offset": offset ?? 0, 37 | }); 38 | } catch (e) { 39 | rethrow; 40 | } 41 | } 42 | 43 | @override 44 | Future create({required IDto dto}) async { 45 | try { 46 | return await dioClient.post( 47 | ApiRoutes.JOB_APPLY, 48 | data: dto.toJson(), 49 | ); 50 | } catch (e) { 51 | rethrow; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/application/i_application_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../base/idto.dart'; 4 | 5 | abstract class IApplicationService { 6 | Future create({required IDto dto}); 7 | 8 | Future getAll({int? limit, int? offset}); 9 | 10 | Future get({required String applicationId}); 11 | 12 | Future delete({required String applicationId}); 13 | } 14 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/auth/auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import 'i_auth_service.dart'; 6 | import '../../base/idto.dart'; 7 | 8 | class AuthService implements IAuthService { 9 | final DioClient dioClient; 10 | 11 | AuthService({required this.dioClient}); 12 | 13 | @override 14 | Future login({required IDto dto}) async { 15 | try { 16 | return await dioClient.post(ApiRoutes.LOGIN, data: dto.toJson()); 17 | } catch (e) { 18 | rethrow; 19 | } 20 | } 21 | 22 | @override 23 | Future registerCompany({required IDto dto}) async { 24 | try { 25 | return await dioClient.post(ApiRoutes.COMPANY_REGISTER, 26 | data: dto.toJson()); 27 | } catch (e) { 28 | rethrow; 29 | } 30 | } 31 | 32 | @override 33 | Future registerCustomer({required IDto dto}) async { 34 | try { 35 | return await dioClient.post(ApiRoutes.CUSTOMER_REGISTER, 36 | data: dto.toJson()); 37 | } catch (e) { 38 | rethrow; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/auth/i_auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | abstract class IAuthService { 4 | Future registerCompany({required T dto}); 5 | 6 | Future registerCustomer({required T dto}); 7 | 8 | Future login({required T dto}); 9 | } 10 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/company/comapny_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import 'i_company_service.dart'; 6 | 7 | class CompanyService implements ICompanyService { 8 | final DioClient dioClient; 9 | 10 | CompanyService({required this.dioClient}); 11 | 12 | @override 13 | Future get({required String uuid}) async { 14 | try { 15 | return await dioClient.get('${ApiRoutes.COMPANIES}/$uuid'); 16 | } catch (e) { 17 | rethrow; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/company/i_company_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class ICompanyService { 4 | Future get({required String uuid}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/customer/customer_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import 'i_customer_service.dart'; 6 | 7 | class CustomerService implements ICustomerService { 8 | final DioClient dioClient; 9 | 10 | CustomerService({required this.dioClient}); 11 | 12 | @override 13 | Future getAllSavedJobs({ 14 | int? limit, 15 | int? offset, 16 | required String customerUuid, 17 | }) async { 18 | try { 19 | return await dioClient.get(ApiRoutes.SAVED_JOBS, queryParameters: { 20 | "limit": limit ?? 20, 21 | "offset": offset ?? 0, 22 | "customer_id": customerUuid, 23 | }); 24 | } catch (e) { 25 | rethrow; 26 | } 27 | } 28 | 29 | @override 30 | Future toggleSave({ 31 | required String customerUuid, 32 | required String jobUuid, 33 | }) async { 34 | try { 35 | return await dioClient.post( 36 | ApiRoutes.TOGGLE_SAVE, 37 | queryParameters: { 38 | 'customer_id': customerUuid, 39 | 'job_id': jobUuid, 40 | }, 41 | ); 42 | } catch (e) { 43 | rethrow; 44 | } 45 | } 46 | 47 | @override 48 | Future getProfile({required String customerUuid}) async { 49 | try { 50 | return await dioClient.get("${ApiRoutes.CUSTOMERS}/$customerUuid"); 51 | } catch (e) { 52 | rethrow; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/customer/i_customer_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | abstract class ICustomerService { 4 | Future toggleSave({ 5 | required String customerUuid, 6 | required String jobUuid, 7 | }); 8 | 9 | Future getAllSavedJobs({ 10 | int? limit, 11 | int? offset, 12 | required String customerUuid, 13 | }); 14 | 15 | Future getProfile({ 16 | required String customerUuid, 17 | }); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/job/i_job_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../base/idto.dart'; 4 | 5 | abstract class IJobService { 6 | Future create({required IDto dto}); 7 | 8 | Future delete({required String uuid}); 9 | 10 | Future get({required String uuid}); 11 | 12 | Future getAll({ 13 | int? limit, 14 | int? offset, 15 | bool? isFeatured, 16 | String? position, 17 | String? companyId, 18 | }); 19 | 20 | Future update({required String uuid, required IDto dto}); 21 | } 22 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/job/job_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import '../../base/idto.dart'; 6 | import 'i_job_service.dart'; 7 | 8 | class JobService implements IJobService { 9 | final DioClient dioClient; 10 | 11 | JobService({required this.dioClient}); 12 | 13 | @override 14 | Future get({required String uuid}) async { 15 | try { 16 | return await dioClient.get('${ApiRoutes.JOBS}/$uuid'); 17 | } catch (e) { 18 | rethrow; 19 | } 20 | } 21 | 22 | @override 23 | Future getAll({ 24 | int? limit, 25 | int? offset, 26 | bool? isFeatured, 27 | String? position, 28 | String? companyId, 29 | }) async { 30 | try { 31 | return await dioClient.get(ApiRoutes.JOBS, queryParameters: { 32 | "limit": limit ?? 20, 33 | "offset": offset ?? 0, 34 | if (isFeatured != null) "is_featured": isFeatured, 35 | if (position != null) "position": position, 36 | if (companyId != null) "company_id": companyId, 37 | }); 38 | } catch (e) { 39 | rethrow; 40 | } 41 | } 42 | 43 | @override 44 | Future delete({required String uuid}) async { 45 | try { 46 | return await dioClient.delete('${ApiRoutes.JOBS}/$uuid'); 47 | } catch (e) { 48 | rethrow; 49 | } 50 | } 51 | 52 | @override 53 | Future update({required String uuid, required IDto dto}) async { 54 | try { 55 | return await dioClient.put( 56 | '${ApiRoutes.JOBS}/$uuid', 57 | data: dto.toJson(), 58 | ); 59 | } catch (e) { 60 | rethrow; 61 | } 62 | } 63 | 64 | @override 65 | Future create({required IDto dto}) async { 66 | try { 67 | return await dioClient.post(ApiRoutes.JOBS, data: dto.toJson()); 68 | } catch (e) { 69 | rethrow; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/position/i_choice_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class IChoiceService { 4 | Future getAll({int? limit, int? offset, String? q}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/position/position_choice_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import 'i_choice_service.dart'; 6 | 7 | class PositionChoiceService extends IChoiceService { 8 | final DioClient dioClient; 9 | 10 | PositionChoiceService({required this.dioClient}); 11 | 12 | @override 13 | Future getAll({int? limit, int? offset, String? q}) async { 14 | try { 15 | return await dioClient.get(ApiRoutes.POSITIONS); 16 | } catch (e) { 17 | rethrow; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/search/i_search_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | abstract class ISearchService { 4 | Future getAll({int? limit, int? offset, String? q}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/data/remote/services/search/search_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/src/response.dart'; 2 | 3 | import '../../api/api_routes.dart'; 4 | import '../../api/dio_client.dart'; 5 | import 'i_search_service.dart'; 6 | 7 | class SearchService extends ISearchService { 8 | final DioClient dioClient; 9 | 10 | SearchService({required this.dioClient}); 11 | 12 | @override 13 | Future getAll({int? limit, int? offset, String? q}) async { 14 | try { 15 | return await dioClient.get( 16 | ApiRoutes.SEARCH, 17 | queryParameters: { 18 | "company_name": q, 19 | }, 20 | ); 21 | } catch (e) { 22 | rethrow; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/app/domain/enums/user_type.dart: -------------------------------------------------------------------------------- 1 | enum RegisterType { 2 | COMPANY, 3 | CUSTOMER, 4 | NOTSELECTED, 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class MyFlutterApp { 4 | MyFlutterApp._(); 5 | 6 | static const _kFontFam = 'MyFlutterApp'; 7 | static const String? _kFontPkg = null; 8 | 9 | static const IconData briefcase = IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg); 10 | } 11 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/bindings/job_details_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/job_details_controller.dart'; 4 | 5 | class JobDetailsBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => JobDetailsController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/job_details_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../controllers/job_details_controller.dart'; 6 | import 'widgets/body.dart'; 7 | import 'widgets/details_bottom_nav_bar.dart'; 8 | 9 | class JobDetailsView extends GetView { 10 | const JobDetailsView({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | resizeToAvoidBottomInset: false, 16 | body: AnnotatedRegion( 17 | value: SystemUiOverlayStyle( 18 | statusBarColor: Get.theme.primaryColor, 19 | statusBarIconBrightness: Brightness.light, 20 | systemNavigationBarColor: Colors.white, 21 | systemNavigationBarIconBrightness: Brightness.dark, 22 | ), 23 | child: const SafeArea( 24 | child: Body(), 25 | ), 26 | ), 27 | bottomNavigationBar: const DetailsBottomNavBar(), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/widgets/apply_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../widgets/custom_button.dart'; 8 | import '../../../../widgets/custom_text_field.dart'; 9 | import '../../controllers/job_details_controller.dart'; 10 | 11 | class ApplyBottomSheetBody extends GetView { 12 | const ApplyBottomSheetBody(this.jobId, {Key? key}) : super(key: key); 13 | final String jobId; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | children: [ 19 | Text( 20 | "Why Are You Applying for This Position?", 21 | style: GoogleFonts.poppins( 22 | fontSize: 20.sp, 23 | fontWeight: FontWeight.w700, 24 | color: Get.theme.colorScheme.onBackground, 25 | ), 26 | ), 27 | const CustomTextField( 28 | minLines: 3, 29 | maxLines: 5, 30 | hintText: "Describe what you're looking for in this job...", 31 | ), 32 | SizedBox(height: 15.h), 33 | Row( 34 | children: [ 35 | HeroIcon( 36 | HeroIcons.checkCircle, 37 | color: Get.theme.primaryColor, 38 | ), 39 | SizedBox(width: 5.w), 40 | Text( 41 | "Will take the resume from your profile.", 42 | style: GoogleFonts.poppins( 43 | fontSize: 13.sp, 44 | fontWeight: FontWeight.w400, 45 | color: Get.theme.colorScheme.primary, 46 | ), 47 | ) 48 | ], 49 | ), 50 | SizedBox(height: 30.h), 51 | CustomButton( 52 | title: "SUBMIT", 53 | onTap: () => controller.applyToJob(jobId, "whyApply"), 54 | ) 55 | ], 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../../../../widgets/shimmer/job_details_shimmer.dart'; 5 | import '../../controllers/job_details_controller.dart'; 6 | import 'about_the_employer.dart'; 7 | import 'description.dart'; 8 | import 'details_sliver_app_bar.dart'; 9 | import 'similar_jobs.dart'; 10 | 11 | class Body extends GetView { 12 | const Body({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Obx( 17 | () => controller.job.when( 18 | idle: () => Container(), 19 | loading: () => const JobDetailsShimmer(), 20 | failure: (err) => const JobDetailsShimmer(), 21 | success: (job) => CustomScrollView( 22 | slivers: [ 23 | DetailsSliverAppBar(job: job!), 24 | SliverToBoxAdapter( 25 | child: Column( 26 | children: [ 27 | Description(job: job), 28 | AboutTheEmployer(job: job), 29 | const SimilarJobs(), 30 | ], 31 | ), 32 | ) 33 | ], 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/widgets/description.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_markdown/flutter_markdown.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | import '../../../../data/remote/dto/job/job_out_dto.dart'; 8 | 9 | class Description extends StatelessWidget { 10 | const Description({Key? key, required this.job}) : super(key: key); 11 | final JobOutDto job; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | margin: EdgeInsets.all(16.w), 17 | decoration: BoxDecoration( 18 | color: Colors.white, 19 | borderRadius: BorderRadius.circular(14.r), 20 | ), 21 | child: Markdown( 22 | data: job.description!, 23 | selectable: true, 24 | shrinkWrap: true, 25 | physics: const NeverScrollableScrollPhysics(), 26 | styleSheet: MarkdownStyleSheet( 27 | p: GoogleFonts.poppins( 28 | fontSize: 13.sp, 29 | fontWeight: FontWeight.w400, 30 | color: Get.theme.colorScheme.onBackground, 31 | ), 32 | h2: GoogleFonts.poppins( 33 | fontSize: 14.sp, 34 | fontWeight: FontWeight.w700, 35 | color: Get.theme.colorScheme.onBackground, 36 | ), 37 | ), 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/widgets/details_bottom_nav_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../../data/remote/base/status.dart'; 6 | import '../../../../utils/functions.dart'; 7 | import '../../../../widgets/custom_button.dart'; 8 | import '../../../JobDetails/controllers/job_details_controller.dart'; 9 | import 'apply_bottom_sheet.dart'; 10 | 11 | class DetailsBottomNavBar extends GetView { 12 | const DetailsBottomNavBar({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Obx( 17 | () => (controller.job is! Success) 18 | ? const SizedBox.shrink() 19 | : Container( 20 | height: 85.h, 21 | padding: EdgeInsets.all(16.w), 22 | decoration: BoxDecoration( 23 | color: Colors.white, 24 | borderRadius: BorderRadius.only( 25 | topLeft: Radius.circular(14.r), 26 | topRight: Radius.circular(14.r), 27 | ), 28 | boxShadow: [ 29 | BoxShadow( 30 | color: Get.theme.colorScheme.secondary.withOpacity(.15), 31 | spreadRadius: 0, 32 | blurRadius: 159, 33 | offset: const Offset(0, 4), // changes position of shadow 34 | ), 35 | ], 36 | ), 37 | child: CustomButton( 38 | title: "APPLY NOW", 39 | onTap: () => popupBottomSheet( 40 | bottomSheetBody: ApplyBottomSheetBody( 41 | controller.job.whenOrNull(success: (job) => job!.id!)!, 42 | ), 43 | ), 44 | ), 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/app/modules/JobDetails/views/widgets/submit_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../widgets/custom_button.dart'; 8 | 9 | class SubmitBottomSheet extends StatelessWidget { 10 | const SubmitBottomSheet({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | HeroIcon( 17 | HeroIcons.checkBadge, 18 | size: 100.w, 19 | color: Get.theme.primaryColor, 20 | style: HeroIconStyle.solid, 21 | ), 22 | SizedBox(height: 25.h), 23 | Text( 24 | "Thank You For Applying!", 25 | textAlign: TextAlign.center, 26 | style: GoogleFonts.poppins( 27 | fontSize: 20, 28 | fontWeight: FontWeight.w700, 29 | color: Get.theme.colorScheme.onBackground, 30 | ), 31 | ), 32 | SizedBox(height: 10.h), 33 | Text( 34 | "Your Application was successfully submitted.\nwe’ll contact you when a decision is made.", 35 | textAlign: TextAlign.center, 36 | style: GoogleFonts.poppins( 37 | fontSize: 13, 38 | fontWeight: FontWeight.w400, 39 | color: Get.theme.colorScheme.secondary, 40 | ), 41 | ), 42 | SizedBox(height: 50.h), 43 | CustomButton( 44 | title: "Back To Home", 45 | onTap: () async => Get.close(2), 46 | ), 47 | ], 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/app/modules/auth/bindings/auth_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:jobs_flutter_app/app/modules/home/controllers/home_controller.dart'; 3 | import 'package:jobs_flutter_app/app/modules/root/controllers/root_controller.dart'; 4 | import '../../saved/controllers/saved_controller.dart'; 5 | import '../../search/controllers/search_controller.dart'; 6 | import '../controllers/auth_controller.dart'; 7 | 8 | class AuthBinding extends Bindings { 9 | @override 10 | void dependencies() { 11 | Get.lazyPut(() => AuthController()); 12 | Get.lazyPut(() => RootController()); 13 | Get.lazyPut(() => HomeController()); 14 | Get.lazyPut(() => SearchController()); 15 | Get.lazyPut(() => SavedController()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/login/login_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import 'package:get/get.dart'; 5 | 6 | import '../../controllers/auth_controller.dart'; 7 | import 'widgets/body.dart'; 8 | 9 | class LoginView extends GetView { 10 | const LoginView({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | backgroundColor: Get.theme.backgroundColor, 16 | resizeToAvoidBottomInset: false, 17 | body: AnnotatedRegion( 18 | value: SystemUiOverlayStyle( 19 | statusBarColor: Get.theme.backgroundColor, 20 | statusBarIconBrightness: Brightness.dark, 21 | systemNavigationBarColor: Colors.white, 22 | systemNavigationBarIconBrightness: Brightness.dark, 23 | ), 24 | child: const SafeArea(child: Body()), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/login/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../../../core/values/strings.dart'; 6 | import '../../../controllers/auth_controller.dart'; 7 | import '../../widgets/button_with_text.dart'; 8 | import '../../widgets/header.dart'; 9 | import 'login_form.dart'; 10 | 11 | class Body extends GetView { 12 | const Body({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Padding( 17 | padding: EdgeInsets.symmetric(horizontal: 29.w), 18 | child: CustomScrollView(slivers: [ 19 | SliverFillRemaining( 20 | child: Column( 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | children: [ 23 | const Header(title: AppStrings.welcomeBack), 24 | SizedBox(height: 30.h), 25 | const LoginForm(), 26 | SizedBox(height: 50.h), 27 | ButtonWithText( 28 | btnLabel: AppStrings.loginBtn, 29 | firstTextSpan: AppStrings.youDoNotHaveAnAccountYet, 30 | secondTextSpan: AppStrings.signup, 31 | onTap: controller.onLoginSubmit, 32 | onTextTap: controller.onSignUp, 33 | ), 34 | ], 35 | ), 36 | ) 37 | ]), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/login/widgets/choose_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../../core/values/strings.dart'; 8 | import '../../../../../domain/enums/user_type.dart'; 9 | import '../../../controllers/auth_controller.dart'; 10 | import 'custom_choose_button.dart'; 11 | 12 | class ChooseBottomSheetBody extends GetView { 13 | const ChooseBottomSheetBody({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | children: [ 19 | Text( 20 | AppStrings.whatAreYouLookingFor, 21 | style: GoogleFonts.poppins( 22 | fontSize: 20.sp, 23 | fontWeight: FontWeight.w700, 24 | color: Get.theme.colorScheme.onBackground, 25 | ), 26 | ), 27 | SizedBox(height: 42.h), 28 | FittedBox( 29 | child: Row( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | CustomChooseButton( 33 | title: "Employee", 34 | icon: HeroIcons.user, 35 | onTap: () => 36 | controller.onSelectRegisterType(RegisterType.COMPANY), 37 | ), 38 | SizedBox(width: 15.w), 39 | CustomChooseButton( 40 | title: "Job", 41 | icon: HeroIcons.briefcase, 42 | onTap: () => 43 | controller.onSelectRegisterType(RegisterType.CUSTOMER), 44 | ), 45 | ], 46 | ), 47 | ) 48 | ], 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/login/widgets/custom_choose_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | class CustomChooseButton extends StatelessWidget { 8 | const CustomChooseButton({ 9 | Key? key, 10 | required this.title, 11 | required this.icon, 12 | required this.onTap, 13 | }) : super(key: key); 14 | 15 | final String title; 16 | final HeroIcons icon; 17 | final void Function() onTap; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return SizedBox( 22 | width: 150.w, 23 | height: 95.h, 24 | child: ElevatedButton( 25 | onPressed: onTap, 26 | style: ElevatedButton.styleFrom( 27 | padding: EdgeInsets.all(10.w), 28 | backgroundColor: Colors.white, 29 | foregroundColor: Get.theme.colorScheme.primary, 30 | elevation: 0.0, 31 | side: BorderSide( 32 | color: Get.theme.colorScheme.primary, 33 | width: 1.5, 34 | ), 35 | ), 36 | child: Column( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | Container( 40 | width: 40.w, 41 | height: 40.h, 42 | padding: EdgeInsets.all(8.w), 43 | decoration: BoxDecoration( 44 | borderRadius: BorderRadius.circular(50.r), 45 | color: Get.theme.colorScheme.primary.withOpacity(0.1), 46 | ), 47 | child: Center( 48 | child: HeroIcon( 49 | style: HeroIconStyle.solid, 50 | icon, 51 | ), 52 | ), 53 | ), 54 | SizedBox(height: 5.h), 55 | Text( 56 | title, 57 | style: GoogleFonts.poppins( 58 | fontSize: 13.sp, 59 | fontWeight: FontWeight.w700, 60 | color: Get.theme.colorScheme.primary), 61 | ), 62 | ], 63 | ), 64 | ), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/login/widgets/login_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:heroicons/heroicons.dart'; 5 | import 'package:jobs_flutter_app/app/core/values/strings.dart'; 6 | import 'package:jobs_flutter_app/app/modules/auth/controllers/auth_controller.dart'; 7 | 8 | import '../../../../../widgets/custom_text_field.dart'; 9 | 10 | class LoginForm extends GetView { 11 | const LoginForm({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Form( 16 | key: controller.loginFormKey, 17 | child: Column( 18 | children: [ 19 | CustomTextField( 20 | controller: controller.loginEmailController, 21 | hintText: AppStrings.emailHint, 22 | title: AppStrings.emailLabel, 23 | autofocus: false, 24 | maxLines: 1, 25 | ), 26 | SizedBox(height: 15.h), 27 | Obx( 28 | () => CustomTextField( 29 | controller: controller.loginPasswordController, 30 | hintText: AppStrings.password, 31 | title: AppStrings.password, 32 | autofocus: false, 33 | isPassword: true, 34 | suffixIcon: 35 | controller.isObscure ? HeroIcons.eyeSlash : HeroIcons.eye, 36 | obscureText: controller.isObscure, 37 | onSuffixTap: controller.toggleObscurePassword, 38 | ), 39 | ), 40 | ], 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/register/register_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import 'package:get/get.dart'; 5 | 6 | import 'widgets/body.dart'; 7 | 8 | class RegisterView extends GetView { 9 | const RegisterView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | backgroundColor: Get.theme.backgroundColor, 15 | resizeToAvoidBottomInset: true, 16 | body: AnnotatedRegion( 17 | value: SystemUiOverlayStyle( 18 | statusBarColor: Get.theme.backgroundColor, 19 | statusBarIconBrightness: Brightness.dark, 20 | systemNavigationBarColor: Colors.white, 21 | systemNavigationBarIconBrightness: Brightness.dark, 22 | ), 23 | child: const SafeArea(child: Body()), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/register/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../../../core/values/strings.dart'; 6 | import '../../../../../domain/enums/user_type.dart'; 7 | import '../../../../../routes/app_pages.dart'; 8 | import '../../../controllers/auth_controller.dart'; 9 | import '../../widgets/button_with_text.dart'; 10 | import '../../widgets/header.dart'; 11 | import 'employee_form.dart'; 12 | import 'employer_form.dart'; 13 | 14 | class Body extends GetView { 15 | const Body({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Padding( 20 | padding: EdgeInsets.symmetric(horizontal: 29.w), 21 | child: SingleChildScrollView( 22 | child: Column( 23 | children: [ 24 | SizedBox(height: 50.h), 25 | const Header(title: AppStrings.createAnAccount), 26 | SizedBox(height: 30.h), 27 | if (controller.registerType == RegisterType.CUSTOMER) 28 | const EmployeeForm(), 29 | if (controller.registerType == RegisterType.COMPANY) 30 | const EmployerForm(), 31 | SizedBox(height: 50.h), 32 | ButtonWithText( 33 | btnLabel: AppStrings.signup.toUpperCase(), 34 | firstTextSpan: AppStrings.alreadyHaveAnAccount, 35 | secondTextSpan: AppStrings.signIn, 36 | onTap: controller.onRegisterSubmit, 37 | onTextTap: () => Get.offNamed(Routes.LOGIN), 38 | ), 39 | SizedBox(height: 50.h), 40 | ], 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/register/widgets/employee_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:heroicons/heroicons.dart'; 5 | 6 | import '../../../../../core/values/strings.dart'; 7 | import '../../../../../utils/validators.dart'; 8 | import '../../../../../widgets/custom_text_field.dart'; 9 | import '../../../controllers/auth_controller.dart'; 10 | 11 | class EmployeeForm extends GetView { 12 | const EmployeeForm({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Form( 17 | key: controller.customerFormKey, 18 | child: Column( 19 | children: [ 20 | CustomTextField( 21 | controller: controller.customerFullNameController, 22 | title: AppStrings.fullName, 23 | hintText: AppStrings.fullNameHint, 24 | autofocus: false, 25 | maxLines: 1, 26 | isRequired: true, 27 | validator: Validators.name, 28 | ), 29 | SizedBox(height: 15.h), 30 | CustomTextField( 31 | controller: controller.customerPhoneNumController, 32 | title: AppStrings.phoneNumber, 33 | hintText: AppStrings.phoneNumberHint, 34 | autofocus: false, 35 | maxLines: 1, 36 | isRequired: true, 37 | textInputType: TextInputType.phone, 38 | validator: Validators.phoneNumber, 39 | isPhoneNumber: true, 40 | onCountryChanged: controller.onCountryChanged, 41 | ), 42 | SizedBox(height: 15.h), 43 | CustomTextField( 44 | controller: controller.customerEmailController, 45 | title: AppStrings.email, 46 | hintText: AppStrings.emailHint, 47 | autofocus: false, 48 | maxLines: 1, 49 | isRequired: true, 50 | textInputType: TextInputType.emailAddress, 51 | validator: Validators.email, 52 | ), 53 | SizedBox(height: 15.h), 54 | Obx( 55 | () => CustomTextField( 56 | controller: controller.customerPasswordController, 57 | hintText: AppStrings.password, 58 | title: AppStrings.password, 59 | autofocus: false, 60 | isPassword: true, 61 | obscureText: controller.isObscure, 62 | onSuffixTap: controller.toggleObscurePassword, 63 | isRequired: true, 64 | validator: Validators.password, 65 | suffixIcon: 66 | controller.isObscure ? HeroIcons.eyeSlash : HeroIcons.eye, 67 | ), 68 | ), 69 | ], 70 | ), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/widgets/button_with_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | import '../../../../widgets/custom_button.dart'; 7 | 8 | class ButtonWithText extends StatelessWidget { 9 | const ButtonWithText({ 10 | Key? key, 11 | required this.btnLabel, 12 | required this.firstTextSpan, 13 | required this.secondTextSpan, 14 | required this.onTap, 15 | required this.onTextTap, 16 | }) : super(key: key); 17 | final String btnLabel; 18 | final String firstTextSpan; 19 | final String secondTextSpan; 20 | final Future Function() onTap; 21 | final void Function() onTextTap; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Column( 26 | children: [ 27 | CustomButton( 28 | title: btnLabel, 29 | onTap: onTap, 30 | ), 31 | SizedBox(height: 20.h), 32 | RichText( 33 | text: TextSpan( 34 | children: [ 35 | TextSpan( 36 | text: firstTextSpan, 37 | style: GoogleFonts.poppins( 38 | fontSize: 12.sp, 39 | fontWeight: FontWeight.w400, 40 | color: const Color(0xff14171A), 41 | ), 42 | ), 43 | TextSpan( 44 | text: secondTextSpan, 45 | recognizer: TapGestureRecognizer()..onTap = onTextTap, 46 | style: GoogleFonts.poppins( 47 | fontSize: 12.sp, 48 | fontWeight: FontWeight.w400, 49 | color: const Color(0xff1DA1F2), 50 | decoration: TextDecoration.underline), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ], 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/app/modules/auth/views/widgets/header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | class Header extends StatelessWidget { 6 | const Header({ 7 | Key? key, 8 | required this.title, 9 | }) : super(key: key); 10 | 11 | final String title; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | children: [ 17 | Text( 18 | title, 19 | style: GoogleFonts.poppins( 20 | fontSize: 30.sp, 21 | fontWeight: FontWeight.w700, 22 | color: const Color(0xff14171A), 23 | ), 24 | ), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/bindings/company_profile_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/company_profile_controller.dart'; 4 | 5 | class CompanyProfileBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => CompanyProfileController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/company_profile_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../controllers/company_profile_controller.dart'; 6 | import 'widgets/body.dart'; 7 | 8 | class CompanyProfileView extends GetView { 9 | const CompanyProfileView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | backgroundColor: Get.theme.backgroundColor, 15 | body: AnnotatedRegion( 16 | value: SystemUiOverlayStyle( 17 | statusBarColor: Get.theme.primaryColor, 18 | statusBarIconBrightness: Brightness.light, 19 | systemNavigationBarColor: Colors.white, 20 | systemNavigationBarIconBrightness: Brightness.dark, 21 | ), 22 | child: const SafeArea(child: Body())), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | import '../../../../widgets/shimmer/company_profile_shimmer.dart'; 7 | import '../../controllers/company_profile_controller.dart'; 8 | import 'company_profile_sliver_app_bar.dart'; 9 | import 'company_tab_view.dart'; 10 | import 'sliverPersistentHeaderDelegateImp.dart'; 11 | 12 | class Body extends GetView { 13 | const Body({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Obx( 18 | () => controller.rxCompany.when( 19 | idle: () => Container(), 20 | loading: () => const CompanyProfileShimmer(), 21 | failure: (err) => const CompanyProfileShimmer(), 22 | success: (company) => NestedScrollView( 23 | headerSliverBuilder: ( 24 | BuildContext context, 25 | bool innerBoxIsScrolled, 26 | ) { 27 | return [ 28 | CompanyProfileSliverAppBar(company: company!), 29 | SliverPersistentHeader( 30 | delegate: SliverPersistentHeaderDelegateImp( 31 | tabBar: TabBar( 32 | controller: controller.tabController, 33 | labelStyle: GoogleFonts.poppins( 34 | fontSize: 14.sp, 35 | fontWeight: FontWeight.w700, 36 | ), 37 | unselectedLabelStyle: GoogleFonts.poppins( 38 | fontSize: 14.sp, 39 | fontWeight: FontWeight.w700, 40 | ), 41 | labelColor: Get.theme.colorScheme.onPrimary, 42 | unselectedLabelColor: Get.theme.colorScheme.secondary, 43 | indicator: BoxDecoration( 44 | borderRadius: BorderRadius.circular(12.r), 45 | color: Get.theme.colorScheme.primary, 46 | ), 47 | tabs: const [ 48 | Tab(text: "About us"), 49 | Tab(text: "Jobs"), 50 | ], 51 | ), 52 | ), 53 | pinned: true, 54 | ), 55 | ]; 56 | }, 57 | body: const CompanyTabView(), 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/company_profile_sliver_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../data/remote/dto/company/Company_out_dto.dart'; 8 | import '../../controllers/company_profile_controller.dart'; 9 | import 'header.dart'; 10 | 11 | class CompanyProfileSliverAppBar extends GetView { 12 | const CompanyProfileSliverAppBar({Key? key, required this.company}) 13 | : super(key: key); 14 | final CompanyOutDto company; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return SliverAppBar( 19 | automaticallyImplyLeading: false, 20 | expandedHeight: 210.h, 21 | backgroundColor: Get.theme.primaryColor, 22 | pinned: true, 23 | leadingWidth: kToolbarHeight, 24 | toolbarHeight: kToolbarHeight, 25 | leading: Padding( 26 | padding: EdgeInsets.all(6.w), 27 | child: IconButton( 28 | onPressed: () => Get.back(), 29 | style: IconButton.styleFrom( 30 | backgroundColor: Colors.white.withOpacity(0.2), 31 | padding: EdgeInsets.zero, 32 | minimumSize: Size.zero, 33 | shape: RoundedRectangleBorder( 34 | borderRadius: BorderRadius.circular(8.r), 35 | ), 36 | ), 37 | icon: const HeroIcon( 38 | HeroIcons.chevronLeft, 39 | color: Colors.white, 40 | ), 41 | ), 42 | ), 43 | centerTitle: true, 44 | titleTextStyle: GoogleFonts.poppins( 45 | fontSize: 18.sp, 46 | fontWeight: FontWeight.w600, 47 | color: Get.theme.backgroundColor, 48 | ), 49 | title: Padding( 50 | padding: EdgeInsets.symmetric(vertical: 6.h), 51 | child: const Text( 52 | "Company Profile", 53 | ), 54 | ), 55 | flexibleSpace: FlexibleSpaceBar( 56 | background: Header(company: company), 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/company_tab_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../../controllers/company_profile_controller.dart'; 5 | import 'about_us.dart'; 6 | import 'jobs_list.dart'; 7 | 8 | class CompanyTabView extends GetView { 9 | const CompanyTabView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return TabBarView( 14 | controller: controller.tabController, 15 | children: const [ 16 | AboutUs(), 17 | JobsList(), 18 | ], 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/company_tap_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | import '../../controllers/company_profile_controller.dart'; 7 | 8 | class CompanyTabBar extends GetView { 9 | const CompanyTabBar({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return TabBar( 14 | controller: controller.tabController, 15 | labelStyle: GoogleFonts.poppins( 16 | fontSize: 14.sp, 17 | fontWeight: FontWeight.w700, 18 | ), 19 | unselectedLabelStyle: GoogleFonts.poppins( 20 | fontSize: 14.sp, 21 | fontWeight: FontWeight.w700, 22 | ), 23 | labelColor: Get.theme.colorScheme.onBackground, 24 | unselectedLabelColor: Get.theme.colorScheme.tertiary, 25 | tabs: const [ 26 | Tab(text: "About us"), 27 | Tab(text: "Jobs"), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:flutter_svg_provider/flutter_svg_provider.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | import '../../../../data/remote/api/api_routes.dart'; 8 | import '../../../../data/remote/dto/company/Company_out_dto.dart'; 9 | import '../../../../widgets/custom_avatar.dart'; 10 | import '../../../JobDetails/controllers/job_details_controller.dart'; 11 | 12 | class Header extends GetView { 13 | const Header({ 14 | Key? key, 15 | required this.company, 16 | }) : super(key: key); 17 | final CompanyOutDto company; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Container( 22 | padding: EdgeInsets.only(right: 16.w, left: 16.w, top: 50.h), 23 | decoration: const BoxDecoration( 24 | image: DecorationImage( 25 | image: Svg('assets/header_bg.svg', color: Colors.white), 26 | fit: BoxFit.cover, 27 | ), 28 | ), 29 | child: Column( 30 | crossAxisAlignment: CrossAxisAlignment.center, 31 | children: [ 32 | CustomAvatar( 33 | imageUrl: "${ApiRoutes.BASE_URL}${company.image}", 34 | ), 35 | Text( 36 | company.name!, 37 | style: GoogleFonts.poppins( 38 | fontWeight: FontWeight.w600, 39 | fontSize: 20.sp, 40 | color: Get.theme.colorScheme.onPrimary, 41 | ), 42 | ), 43 | if (company.workType != null) 44 | Text( 45 | company.workType!, 46 | style: GoogleFonts.poppins( 47 | fontWeight: FontWeight.w400, 48 | fontSize: 13.sp, 49 | color: Get.theme.colorScheme.onPrimary, 50 | ), 51 | ), 52 | ], 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/jobs_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:heroicons/heroicons.dart'; 4 | 5 | import '../../../../data/remote/api/api_routes.dart'; 6 | import '../../../../routes/app_pages.dart'; 7 | import '../../../../widgets/custom_job_card.dart'; 8 | import '../../../../widgets/custom_lottie.dart'; 9 | import '../../../saved/controllers/saved_controller.dart'; 10 | import '../../controllers/company_profile_controller.dart'; 11 | 12 | class JobsList extends GetView { 13 | const JobsList({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Obx(() => controller.rxJobs.when( 18 | idle: () => Container(), 19 | loading: () => const CircularProgressIndicator(), 20 | success: (jobs) => jobs == null || jobs.isEmpty 21 | ? const CustomLottie( 22 | title: "This company has not jobs yet.", 23 | asset: "assets/empty.json", 24 | ) 25 | : ListView.builder( 26 | itemCount: jobs.length, 27 | shrinkWrap: true, 28 | scrollDirection: Axis.vertical, 29 | itemBuilder: (context, index) => CustomJobCard( 30 | jobPosition: jobs[index].position, 31 | publishTime: jobs[index].createdAt!, 32 | companyName: jobs[index].company!.name!, 33 | employmentType: jobs[index].employmentType!, 34 | location: jobs[index].location!, 35 | workplace: jobs[index].workplace!, 36 | actionIcon: HeroIcons.bookmark, 37 | avatar: 38 | "${ApiRoutes.BASE_URL}${jobs[index].company!.image!}", 39 | description: jobs[index].description!, 40 | onTap: () => Get.toNamed( 41 | Routes.JOB_DETAILS, 42 | arguments: jobs[index].id, 43 | ), 44 | isSaved: SavedController.to.isJobSaved(jobs[index].id!), 45 | onActionTap: (isSaved) => 46 | controller.onSaveButtonTapped(isSaved, jobs[index].id!), 47 | ), 48 | ), 49 | failure: (e) => Text(e!), 50 | )); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/profile_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | import '../../../../data/remote/api/api_routes.dart'; 7 | import '../../../../widgets/custom_avatar.dart'; 8 | import '../../controllers/company_profile_controller.dart'; 9 | 10 | class ProfileHeader extends GetView { 11 | const ProfileHeader({ 12 | Key? key, 13 | required this.imagePath, 14 | required this.name, 15 | }) : super(key: key); 16 | final String imagePath; 17 | final String name; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Container( 22 | padding: EdgeInsets.symmetric(horizontal: 16.w), 23 | width: double.infinity, 24 | child: Column( 25 | children: [ 26 | CustomAvatar( 27 | imageUrl: "${ApiRoutes.BASE_URL}$imagePath", 28 | ), 29 | SizedBox(height: 5.h), 30 | Text( 31 | name, 32 | style: GoogleFonts.poppins( 33 | fontWeight: FontWeight.w700, 34 | fontSize: 16.sp, 35 | color: Get.theme.colorScheme.onBackground, 36 | ), 37 | ), 38 | ], 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/app/modules/companyProfile/views/widgets/sliverPersistentHeaderDelegateImp.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class SliverPersistentHeaderDelegateImp extends SliverPersistentHeaderDelegate { 6 | final TabBar tabBar; 7 | 8 | const SliverPersistentHeaderDelegateImp({ 9 | required this.tabBar, 10 | }); 11 | 12 | @override 13 | Widget build( 14 | BuildContext context, 15 | double shrinkOffset, 16 | bool overlapsContent, 17 | ) { 18 | return Align( 19 | child: Container( 20 | padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), 21 | decoration: BoxDecoration( 22 | color: Get.theme.backgroundColor, 23 | ), 24 | child: Container( 25 | padding: EdgeInsets.all(5.w), 26 | decoration: BoxDecoration( 27 | color: Colors.white, 28 | borderRadius: BorderRadius.circular(14.r), 29 | boxShadow: [ 30 | BoxShadow( 31 | color: Colors.grey.withOpacity(0.05), 32 | blurRadius: 20, 33 | offset: const Offset(0, 10), 34 | ), 35 | ], 36 | ), 37 | child: tabBar, 38 | ), 39 | ), 40 | ); 41 | } 42 | 43 | @override 44 | double get maxExtent => tabBar.preferredSize.height * 1.5; 45 | 46 | @override 47 | double get minExtent => tabBar.preferredSize.height * 1.5; 48 | 49 | @override 50 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { 51 | return false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/bindings/customer_profile_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/customer_profile_controller.dart'; 4 | 5 | class CustomerProfileBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => CustomerProfileController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/controllers/customer_profile_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../../../data/remote/base/status.dart'; 4 | import '../../../data/remote/dto/customer/customer_profile_out_dto.dart'; 5 | import '../../../data/remote/repositories/customer/customer_repository.dart'; 6 | import '../../../di/locator.dart'; 7 | import '../../../widgets/dialogs.dart'; 8 | import '../../auth/controllers/auth_controller.dart'; 9 | 10 | class CustomerProfileController extends GetxController { 11 | final customerRepository = getIt.get(); 12 | 13 | final Rx> _rxProfile = 14 | Rx(const Status.loading()); 15 | 16 | Status get profile => _rxProfile.value; 17 | 18 | @override 19 | void onInit() { 20 | super.onInit(); 21 | loadPage(); 22 | } 23 | 24 | @override 25 | void onReady() { 26 | super.onReady(); 27 | } 28 | 29 | @override 30 | void onClose() { 31 | super.onClose(); 32 | } 33 | 34 | getProfile() async { 35 | final state = await customerRepository.getProfile( 36 | customerUuid: AuthController.to.currentUser!.id!); 37 | _rxProfile.value = state; 38 | } 39 | 40 | void loadPage() async { 41 | await getProfile(); 42 | showDialogOnFailure(); 43 | } 44 | 45 | void onRetry() async { 46 | _rxProfile.value = const Status.loading(); 47 | await getProfile(); 48 | showDialogOnFailure(); 49 | } 50 | 51 | void showDialogOnFailure() { 52 | if (profile is Failure) { 53 | Dialogs.spaceDialog( 54 | description: (profile as Failure).reason.toString(), 55 | btnOkOnPress: onRetry, 56 | dismissOnBackKeyPress: false, 57 | dismissOnTouchOutside: false, 58 | ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/customer_profile_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../controllers/customer_profile_controller.dart'; 6 | import 'widgets/body.dart'; 7 | 8 | class CustomerProfileView extends GetView { 9 | const CustomerProfileView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | backgroundColor: Get.theme.backgroundColor, 15 | body: AnnotatedRegion( 16 | value: SystemUiOverlayStyle( 17 | statusBarColor: Get.theme.primaryColor, 18 | statusBarIconBrightness: Brightness.light, 19 | systemNavigationBarColor: Get.theme.backgroundColor, 20 | systemNavigationBarIconBrightness: Brightness.dark, 21 | ), 22 | child: const SafeArea(child: Body()), 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/about_me.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../widgets/custom_info_card.dart'; 8 | 9 | class AboutMe extends StatelessWidget { 10 | const AboutMe({ 11 | Key? key, 12 | required this.description, 13 | }) : super(key: key); 14 | final String? description; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return description == null 19 | ? const SizedBox() 20 | : CustomInfoCard( 21 | icon: HeroIcons.userCircle, 22 | title: "About me", 23 | child: Text( 24 | description!, 25 | style: GoogleFonts.poppins( 26 | fontSize: 13.sp, 27 | fontWeight: FontWeight.w400, 28 | color: Get.theme.colorScheme.secondary, 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../../widgets/shimmer/customer_profile_shimmer.dart'; 6 | import '../../controllers/customer_profile_controller.dart'; 7 | import 'about_me.dart'; 8 | import 'customer_profile_sliver_app_bar.dart'; 9 | import 'education.dart'; 10 | import 'experience.dart'; 11 | import 'languages.dart'; 12 | import 'skills.dart'; 13 | 14 | class Body extends GetView { 15 | const Body({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Obx( 20 | () => controller.profile.when( 21 | idle: () => const SizedBox(), 22 | loading: () => const CustomerProfileShimmer(), 23 | failure: (err) => const CustomerProfileShimmer(), 24 | success: (profile) => CustomScrollView( 25 | slivers: [ 26 | CustomerProfileSliverAppBar(profile: profile!), 27 | SliverToBoxAdapter( 28 | child: SingleChildScrollView( 29 | child: Padding( 30 | padding: EdgeInsets.only(top: 16.h), 31 | child: Column( 32 | children: [ 33 | AboutMe(description: profile.description), 34 | Experience(experience: profile.workExperience), 35 | EducationCard(education: profile.education), 36 | Skills(skills: profile.skills), 37 | Languages(languages: profile.language), 38 | ], 39 | ), 40 | ), 41 | ), 42 | ), 43 | ], 44 | ), 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/education.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | import '../../../../data/remote/dto/customer/customer_profile_out_dto.dart'; 8 | import '../../../../utils/extensions.dart'; 9 | import '../../../../widgets/custom_info_card.dart'; 10 | 11 | class EducationCard extends StatelessWidget { 12 | const EducationCard({ 13 | Key? key, 14 | required this.education, 15 | }) : super(key: key); 16 | final List? education; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return education == null || education!.isEmpty 21 | ? const SizedBox() 22 | : CustomInfoCard( 23 | icon: HeroIcons.academicCap, 24 | title: "Education", 25 | child: Column( 26 | crossAxisAlignment: CrossAxisAlignment.start, 27 | children: List.generate( 28 | education!.length, 29 | (index) => Column( 30 | crossAxisAlignment: CrossAxisAlignment.start, 31 | children: [ 32 | Text( 33 | education![index].degree!, 34 | style: GoogleFonts.poppins( 35 | fontSize: 13.sp, 36 | fontWeight: FontWeight.w600, 37 | color: Get.theme.colorScheme.onBackground, 38 | ), 39 | ), 40 | SizedBox(height: 5.h), 41 | Text( 42 | education![index].school!, 43 | style: GoogleFonts.poppins( 44 | fontSize: 13.sp, 45 | fontWeight: FontWeight.w400, 46 | color: Get.theme.colorScheme.secondary, 47 | ), 48 | ), 49 | SizedBox(height: 2.h), 50 | Text( 51 | "${education![index].startDate!.toShortDate()} - ${education![index].endDate!.toShortDate()}", 52 | style: GoogleFonts.poppins( 53 | fontSize: 13.sp, 54 | fontWeight: FontWeight.w400, 55 | color: Get.theme.colorScheme.secondary, 56 | ), 57 | ), 58 | if (index != education!.length - 1) 59 | Divider( 60 | color: Get.theme.colorScheme.background, 61 | thickness: 1.5, 62 | ), 63 | ], 64 | ), 65 | ), 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:flutter_svg_provider/flutter_svg_provider.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | import '../../../../data/remote/api/api_routes.dart'; 8 | import '../../../../data/remote/dto/customer/customer_profile_out_dto.dart'; 9 | import '../../../../widgets/custom_avatar.dart'; 10 | import '../../../JobDetails/controllers/job_details_controller.dart'; 11 | 12 | class Header extends GetView { 13 | const Header({ 14 | Key? key, 15 | required this.profile, 16 | }) : super(key: key); 17 | final CustomerProfileOutDto profile; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Container( 22 | padding: EdgeInsets.only(right: 16.w, left: 16.w, top: 30.h), 23 | decoration: const BoxDecoration( 24 | image: DecorationImage( 25 | image: Svg('assets/header_bg.svg', color: Colors.white), 26 | fit: BoxFit.cover, 27 | ), 28 | ), 29 | child: Padding( 30 | padding: EdgeInsets.only(top: 30.h), 31 | child: Column( 32 | children: [ 33 | CustomAvatar(imageUrl: "${ApiRoutes.BASE_URL}${profile.image}"), 34 | SizedBox(height: 5.h), 35 | Text( 36 | profile.name!, 37 | style: GoogleFonts.poppins( 38 | fontSize: 16.sp, 39 | fontWeight: FontWeight.w600, 40 | color: Get.theme.colorScheme.onPrimary, 41 | ), 42 | ), 43 | Text( 44 | profile.jobTitle!, 45 | style: GoogleFonts.poppins( 46 | fontSize: 13.sp, 47 | fontWeight: FontWeight.w400, 48 | color: Get.theme.colorScheme.onPrimary, 49 | ), 50 | ), 51 | ], 52 | ), 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/languages.dart: -------------------------------------------------------------------------------- 1 | import 'package:expandable/expandable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:heroicons/heroicons.dart'; 4 | 5 | import '../../../../widgets/custom_info_card.dart'; 6 | import 'wrapped_chips.dart'; 7 | 8 | class Languages extends StatelessWidget { 9 | const Languages({ 10 | Key? key, 11 | required this.languages, 12 | }) : super(key: key); 13 | final List? languages; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final List shortLanguages = List.from(languages!); 18 | if (languages!.length > 6) { 19 | shortLanguages.removeRange(6, languages!.length); 20 | shortLanguages.add('+${languages!.length - shortLanguages.length} more'); 21 | } 22 | return languages == null || languages!.isEmpty 23 | ? const SizedBox() 24 | : CustomInfoCard( 25 | icon: HeroIcons.language, 26 | title: "Languages", 27 | child: ExpandableNotifier( 28 | child: Expandable( 29 | collapsed: ExpandableButton( 30 | child: WrappedChips(list: shortLanguages), 31 | ), 32 | expanded: ExpandableButton( 33 | child: WrappedChips(list: languages!), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/skills.dart: -------------------------------------------------------------------------------- 1 | import 'package:expandable/expandable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:heroicons/heroicons.dart'; 4 | 5 | import '../../../../widgets/custom_info_card.dart'; 6 | import 'wrapped_chips.dart'; 7 | 8 | class Skills extends StatelessWidget { 9 | const Skills({ 10 | Key? key, 11 | required this.skills, 12 | }) : super(key: key); 13 | final List? skills; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final List shortSkills = List.from(skills!); 18 | if (skills!.length > 4) { 19 | shortSkills.removeRange(4, skills!.length); 20 | shortSkills.add('+${skills!.length - shortSkills.length} more'); 21 | } 22 | return skills == null || skills!.isEmpty 23 | ? const SizedBox() 24 | : CustomInfoCard( 25 | icon: HeroIcons.sparkles, 26 | title: "Skills", 27 | child: ExpandableNotifier( 28 | child: Expandable( 29 | collapsed: ExpandableButton( 30 | child: WrappedChips(list: shortSkills), 31 | ), 32 | expanded: ExpandableButton( 33 | child: WrappedChips(list: skills!), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/app/modules/customerProfile/views/widgets/wrapped_chips.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | class WrappedChips extends StatelessWidget { 7 | const WrappedChips({ 8 | Key? key, 9 | required this.list, 10 | }) : super(key: key); 11 | final List list; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Wrap( 16 | children: List.generate( 17 | list.length, 18 | (index) => Padding( 19 | padding: EdgeInsets.all(4.w), 20 | child: Chip( 21 | shape: RoundedRectangleBorder( 22 | borderRadius: BorderRadius.circular(10.r), 23 | ), 24 | backgroundColor: Get.theme.colorScheme.background, 25 | labelPadding: EdgeInsets.all(8.w), 26 | label: Text( 27 | list[index], 28 | style: GoogleFonts.poppins( 29 | fontSize: 13.sp, 30 | fontWeight: FontWeight.w400, 31 | color: Get.theme.colorScheme.secondary, 32 | ), 33 | ), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/app/modules/home/bindings/home_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/home_controller.dart'; 4 | 5 | class HomeBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => HomeController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/home/views/home_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../data/remote/api/api_routes.dart'; 6 | import '../../../widgets/custom_appbar.dart'; 7 | import '../../../widgets/custom_avatar.dart'; 8 | import '../controllers/home_controller.dart'; 9 | import 'widgets/body.dart'; 10 | 11 | class HomeView extends GetView { 12 | const HomeView({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return SafeArea( 17 | child: Scaffold( 18 | backgroundColor: Get.theme.backgroundColor, 19 | appBar: CustomAppBar( 20 | leading: Padding( 21 | padding: EdgeInsets.only(left: 16.w, bottom: 8.w, top: 8.w), 22 | child: GestureDetector( 23 | onTap: () => Scaffold.of(context).openDrawer(), 24 | child: Obx( 25 | () => controller.customerAvatar.when( 26 | idle: () => const SizedBox(), 27 | loading: () => const SizedBox(), 28 | success: (data) => CustomAvatar( 29 | imageUrl: "${ApiRoutes.BASE_URL}$data", 30 | height: 46.h, 31 | ), 32 | failure: (error) => const SizedBox(), 33 | ), 34 | ), 35 | ), 36 | ), 37 | title: "Job Finder", 38 | ), 39 | body: const Body(), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/app/modules/home/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../controllers/home_controller.dart'; 6 | import 'chips_list.dart'; 7 | import 'featured_jobs.dart'; 8 | import 'recent_jobs.dart'; 9 | 10 | class Body extends GetView { 11 | const Body({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SingleChildScrollView( 16 | controller: controller.homeScrollController, 17 | child: Column( 18 | children: [ 19 | SizedBox(height: 16.h), 20 | const ChipsList(), 21 | SizedBox(height: 16.h), 22 | const FeaturedJobs(), 23 | SizedBox(height: 16.h), 24 | const RecentJobs(), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/app/modules/home/views/widgets/chips_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | import '../../../../widgets/custom_chip.dart'; 6 | import '../../../../widgets/shimmer/chips_shimmer.dart'; 7 | import '../../controllers/home_controller.dart'; 8 | 9 | class ChipsList extends GetView { 10 | const ChipsList({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return SizedBox( 15 | height: 0.1.sw, 16 | child: Obx( 17 | () => controller.positions.when( 18 | idle: () => Container(), 19 | loading: () => const ChipsShimmer(), 20 | success: (positions) => ListView.builder( 21 | itemCount: positions!.length, 22 | padding: EdgeInsets.only(left: 16.w), 23 | scrollDirection: Axis.horizontal, 24 | shrinkWrap: true, 25 | itemBuilder: (context, index) => Obx( 26 | () => CustomChip( 27 | title: positions[index].jobTitle!, 28 | isActive: positions[index].jobTitle == controller.chipTitle, 29 | onPressed: () => 30 | controller.updateChipTitle(positions[index].jobTitle!), 31 | ), 32 | ), 33 | ), 34 | failure: (e) => const ChipsShimmer(), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/app/modules/root/bindings/root_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../../home/controllers/home_controller.dart'; 4 | import '../../saved/controllers/saved_controller.dart'; 5 | import '../../search/controllers/search_controller.dart'; 6 | import '../controllers/root_controller.dart'; 7 | 8 | class RootBinding extends Bindings { 9 | @override 10 | void dependencies() { 11 | Get.lazyPut(() => RootController()); 12 | Get.put(HomeController()); 13 | Get.put(SearchController()); 14 | Get.put(SavedController()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/app/modules/root/controllers/root_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:persistent_bottom_nav_bar_v2/persistent-tab-view.dart'; 3 | 4 | import '../../../widgets/dialogs.dart'; 5 | import '../../auth/controllers/auth_controller.dart'; 6 | import '../../home/controllers/home_controller.dart'; 7 | import '../../saved/controllers/saved_controller.dart'; 8 | import '../../search/controllers/search_controller.dart'; 9 | 10 | class RootController extends GetxController { 11 | static RootController get to => Get.find(); 12 | final persistentTabController = PersistentTabController(initialIndex: 0); 13 | 14 | @override 15 | void onInit() { 16 | super.onInit(); 17 | } 18 | 19 | @override 20 | void onReady() { 21 | super.onReady(); 22 | } 23 | 24 | @override 25 | void onClose() { 26 | super.onClose(); 27 | persistentTabController.dispose(); 28 | } 29 | 30 | void onHomeDoubleClick() { 31 | HomeController.to.animateToStart(); 32 | } 33 | 34 | void onSearchDoubleClick() { 35 | SearchController.to.clearSearch(); 36 | } 37 | 38 | void onSavedDoubleClick() { 39 | SavedController.to.animateToStart(); 40 | } 41 | 42 | void logout() { 43 | Dialogs.warningDialog( 44 | title: "You are about to logout", 45 | btnOkText: "Logout", 46 | btnOkOnPress: AuthController.to.logout, 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/app/modules/saved/bindings/saved_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/saved_controller.dart'; 4 | 5 | class SavedBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => SavedController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/saved/views/saved_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:jobs_flutter_app/app/modules/saved/views/widgets/body.dart'; 4 | import 'package:jobs_flutter_app/app/widgets/custom_appbar.dart'; 5 | import '../controllers/saved_controller.dart'; 6 | 7 | class SavedView extends GetView { 8 | const SavedView({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return SafeArea( 13 | child: Scaffold( 14 | backgroundColor: Get.theme.backgroundColor, 15 | appBar: const CustomAppBar(title: "Saved Jobs"), 16 | body: const Body(), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/modules/saved/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../../../../widgets/custom_lottie.dart'; 5 | import '../../../../widgets/shimmer/recent_jobs_shimmer.dart'; 6 | import '../../controllers/saved_controller.dart'; 7 | import 'no_saving.dart'; 8 | import 'saved_jobs.dart'; 9 | 10 | class Body extends GetView { 11 | const Body({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Obx( 16 | () => Center( 17 | child: controller.savedJobs.when( 18 | idle: () => const RecentJobsShimmer(), 19 | loading: () => const RecentJobsShimmer(), 20 | success: (data) { 21 | if (data!.isEmpty) return const NoSaving(); 22 | return SavedJobs(jobs: data); 23 | }, 24 | failure: (e) => CustomLottie( 25 | asset: "assets/space.json", 26 | repeat: true, 27 | title: e!, 28 | onTryAgain: controller.onRetry, 29 | ), 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/app/modules/saved/views/widgets/no_saving.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.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:get/get.dart'; 6 | import 'package:google_fonts/google_fonts.dart'; 7 | 8 | import '../../../../core/values/strings.dart'; 9 | import '../../../../widgets/custom_button.dart'; 10 | import '../../controllers/saved_controller.dart'; 11 | 12 | class NoSaving extends GetView { 13 | const NoSaving({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Text( 21 | 'No Saving', 22 | style: GoogleFonts.poppins( 23 | fontSize: 24.sp, 24 | fontWeight: FontWeight.w700, 25 | color: Get.theme.colorScheme.onBackground, 26 | ), 27 | ), 28 | SizedBox(height: 6.h), 29 | Text( 30 | AppStrings.noSaved, 31 | style: GoogleFonts.poppins( 32 | fontWeight: FontWeight.w400, 33 | fontSize: 14.sp, 34 | color: Get.theme.colorScheme.secondary), 35 | textAlign: TextAlign.center, 36 | ), 37 | SizedBox(height: 23.h), 38 | SvgPicture.asset( 39 | 'assets/empty_save.svg', 40 | height: 208.h, 41 | ), 42 | SizedBox(height: 80.h), 43 | Padding( 44 | padding: EdgeInsets.symmetric(horizontal: 80.w), 45 | child: CustomButton( 46 | title: "FIND A JOB", 47 | onTap: controller.jumpToHome, 48 | ), 49 | ) 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/app/modules/saved/views/widgets/saved_jobs.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:heroicons/heroicons.dart'; 4 | 5 | import '../../../../data/remote/api/api_routes.dart'; 6 | import '../../../../data/remote/dto/job/job_out_dto.dart'; 7 | import '../../../../routes/app_pages.dart'; 8 | import '../../../../widgets/custom_job_card.dart'; 9 | import '../../controllers/saved_controller.dart'; 10 | 11 | class SavedJobs extends GetView { 12 | const SavedJobs({ 13 | Key? key, 14 | required this.jobs, 15 | }) : super(key: key); 16 | final List jobs; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return AnimatedList( 21 | key: controller.animatedListKey, 22 | initialItemCount: jobs.length, 23 | controller: controller.savedScrollController, 24 | scrollDirection: Axis.vertical, 25 | physics: const BouncingScrollPhysics(), 26 | itemBuilder: (context, index, animation) => CustomJobCard( 27 | avatar: "${ApiRoutes.BASE_URL}${jobs[index].company!.image}", 28 | companyName: jobs[index].company!.name!, 29 | employmentType: jobs[index].employmentType, 30 | jobPosition: jobs[index].position, 31 | location: jobs[index].location, 32 | actionIcon: HeroIcons.bookmark, 33 | isSaved: true, 34 | publishTime: jobs[index].createdAt!, 35 | workplace: jobs[index].workplace, 36 | description: jobs[index].description, 37 | onActionTap: (isSaved) => 38 | controller.onSaveButtonTapped(isSaved, jobs[index].id!), 39 | onAvatarTap: () => Get.toNamed( 40 | Routes.COMPANY_PROFILE, 41 | arguments: jobs[index].company!.id, 42 | ), 43 | onTap: () => Get.toNamed( 44 | Routes.JOB_DETAILS, 45 | arguments: jobs[index].id, 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/app/modules/search/bindings/search_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/search_controller.dart'; 4 | 5 | class SearchBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => SearchController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/search/controllers/search_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../../../data/remote/base/status.dart'; 5 | import '../../../data/remote/dto/search/search_out_dto.dart'; 6 | import '../../../data/remote/repositories/search/search_repository.dart'; 7 | import '../../../di/locator.dart'; 8 | 9 | class SearchController extends GetxController { 10 | static SearchController get to => Get.find(); 11 | final _searchRepository = getIt.get(); 12 | final searchController = TextEditingController(); 13 | 14 | final Rx>> _rxResults = 15 | Rx>>(const Status.idle()); 16 | 17 | Status> get rxResults => _rxResults.value; 18 | 19 | @override 20 | void onInit() { 21 | super.onInit(); 22 | } 23 | 24 | @override 25 | void onReady() { 26 | super.onReady(); 27 | } 28 | 29 | @override 30 | void onClose() { 31 | super.onClose(); 32 | searchController.dispose(); 33 | } 34 | 35 | getSearchResult() async { 36 | if (!searchController.text.isBlank!) { 37 | final Status> results = 38 | await _searchRepository.getAll(q: searchController.text.trim()); 39 | _rxResults.value = results; 40 | } 41 | } 42 | 43 | clearSearch() { 44 | searchController.clear(); 45 | _rxResults.value = const Status.idle(); 46 | } 47 | 48 | void onRetry() { 49 | getSearchResult(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lib/app/modules/search/views/search_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../controllers/search_controller.dart'; 5 | import 'widgets/body.dart'; 6 | 7 | class SearchView extends GetView { 8 | const SearchView({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return SafeArea( 13 | child: Scaffold( 14 | backgroundColor: Get.theme.backgroundColor, 15 | resizeToAvoidBottomInset: true, 16 | body: const Body(), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/modules/search/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:heroicons/heroicons.dart'; 5 | import 'package:jobs_flutter_app/app/core/values/strings.dart'; 6 | 7 | import '../../../../widgets/custom_text_field.dart'; 8 | import '../../controllers/search_controller.dart'; 9 | import 'search_items.dart'; 10 | 11 | class Body extends GetView { 12 | const Body({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Padding( 17 | padding: EdgeInsets.symmetric(horizontal: 16.w), 18 | child: Column( 19 | children: [ 20 | SizedBox(height: 30.h), 21 | CustomTextField( 22 | controller: controller.searchController, 23 | autofocus: false, 24 | hintText: AppStrings.SEARCH_HINT, 25 | isSearchBar: true, 26 | maxLines: 1, 27 | prefixIcon: HeroIcons.magnifyingGlass, 28 | suffixIcon: HeroIcons.xMark, 29 | onChanged: (_) => controller.getSearchResult(), 30 | onSuffixTap: () => controller.clearSearch(), 31 | ), 32 | SizedBox(height: 20.h), 33 | const Expanded(child: SearchResults()), 34 | ], 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/app/modules/search/views/widgets/items_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:jobs_flutter_app/app/widgets/custom_avatar.dart'; 6 | 7 | class SearchItem extends StatelessWidget { 8 | const SearchItem({ 9 | Key? key, 10 | required this.avatar, 11 | required this.title, 12 | required this.subtitle, 13 | required this.onTap, 14 | }) : super(key: key); 15 | final String avatar; 16 | final String title; 17 | final String subtitle; 18 | final void Function() onTap; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return GestureDetector( 23 | onTap: onTap, 24 | child: Container( 25 | padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.h), 26 | decoration: BoxDecoration( 27 | color: Colors.white, 28 | borderRadius: BorderRadius.circular(14.r), 29 | ), 30 | child: Row( 31 | children: [ 32 | CustomAvatar( 33 | imageUrl: avatar, 34 | height: 35.h, 35 | ), 36 | Padding( 37 | padding: EdgeInsets.only(left: 10.w), 38 | child: Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Text( 42 | title, 43 | style: GoogleFonts.poppins( 44 | fontSize: 14.sp, 45 | fontWeight: FontWeight.w400, 46 | ), 47 | ), 48 | Text( 49 | subtitle, 50 | style: GoogleFonts.poppins( 51 | fontSize: 13.sp, 52 | color: Get.theme.colorScheme.secondary.withOpacity(0.75), 53 | ), 54 | ) 55 | ], 56 | ), 57 | ) 58 | ], 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/app/modules/search/views/widgets/search_items.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:jobs_flutter_app/app/core/values/strings.dart'; 5 | 6 | import '../../../../routes/app_pages.dart'; 7 | import '../../../../data/remote/api/api_routes.dart'; 8 | import '../../../../widgets/custom_lottie.dart'; 9 | import '../../controllers/search_controller.dart'; 10 | import 'items_card.dart'; 11 | 12 | class SearchResults extends GetView { 13 | const SearchResults({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Obx( 18 | () => controller.rxResults.when( 19 | idle: () => Container(), 20 | loading: () => const Center(child: CircularProgressIndicator()), 21 | success: (results) => results!.isEmpty 22 | ? CustomLottie( 23 | title: AppStrings.NO_RESULT, 24 | asset: "assets/empty.json", 25 | description:AppStrings.NO_RESULT_DES, 26 | assetHeight: 200.h, 27 | ) 28 | : ListView.builder( 29 | itemCount: results.length, 30 | shrinkWrap: true, 31 | scrollDirection: Axis.vertical, 32 | physics: const BouncingScrollPhysics(), 33 | itemBuilder: (context, index) => Padding( 34 | padding: EdgeInsets.only(bottom: 16.h), 35 | child: SearchItem( 36 | avatar: "${ApiRoutes.BASE_URL}${results[index].image}", 37 | title: results[index].name!, 38 | subtitle: "Internet company", 39 | onTap: () => Get.toNamed(Routes.COMPANY_PROFILE, 40 | arguments: results[index].id!), 41 | ), 42 | ), 43 | ), 44 | failure: (e) => CustomLottie( 45 | asset: "assets/space.json", 46 | title: e!, 47 | onTryAgain: controller.onRetry, 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/app/modules/waiting/bindings/waiting_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../controllers/waiting_controller.dart'; 4 | 5 | class WaitingBinding extends Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut( 9 | () => WaitingController(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/app/modules/waiting/controllers/waiting_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class WaitingController extends GetxController { 4 | //TODO: Implement WaitingController 5 | 6 | final count = 0.obs; 7 | @override 8 | void onInit() { 9 | super.onInit(); 10 | } 11 | 12 | @override 13 | void onReady() { 14 | super.onReady(); 15 | } 16 | 17 | @override 18 | void onClose() { 19 | super.onClose(); 20 | } 21 | 22 | void increment() => count.value++; 23 | } 24 | -------------------------------------------------------------------------------- /lib/app/modules/waiting/views/waiting_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:get/get.dart'; 4 | 5 | import '../controllers/waiting_controller.dart'; 6 | import 'widgets/body.dart'; 7 | 8 | class WaitingView extends GetView { 9 | const WaitingView({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return SafeArea( 14 | child: Scaffold( 15 | backgroundColor: Get.theme.backgroundColor, 16 | body: const Body(), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/app/modules/waiting/views/widgets/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | import '../../../../core/values/strings.dart'; 8 | import '../../../../widgets/custom_button.dart'; 9 | 10 | class Body extends StatelessWidget { 11 | const Body({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Padding( 16 | padding: EdgeInsets.all(29.w), 17 | child: Column( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Text( 21 | AppStrings.thankYou, 22 | style: GoogleFonts.poppins( 23 | fontSize: 40.sp, 24 | fontWeight: FontWeight.w700, 25 | color: Get.theme.colorScheme.onBackground, 26 | ), 27 | ), 28 | Text( 29 | AppStrings.forYourSubmission, 30 | style: GoogleFonts.poppins( 31 | fontWeight: FontWeight.w400, 32 | fontSize: 20.sp, 33 | color: Get.theme.colorScheme.secondary, 34 | ), 35 | ), 36 | SizedBox(height: 5.h), 37 | Text( 38 | AppStrings.willGetInTouch, 39 | style: GoogleFonts.poppins( 40 | fontSize: 14.sp, 41 | fontWeight: FontWeight.w400, 42 | color: Get.theme.colorScheme.secondary, 43 | ), 44 | ), 45 | SizedBox(height: 100.h), 46 | CustomButton( 47 | title: AppStrings.exit, 48 | onTap: () => SystemNavigator.pop(), 49 | ), 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/app/routes/app_pages.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import '../modules/JobDetails/bindings/job_details_binding.dart'; 4 | import '../modules/JobDetails/views/job_details_view.dart'; 5 | import '../modules/auth/bindings/auth_binding.dart'; 6 | import '../modules/auth/views/login/login_view.dart'; 7 | import '../modules/auth/views/register/register_view.dart'; 8 | import '../modules/companyProfile/bindings/company_profile_binding.dart'; 9 | import '../modules/companyProfile/views/company_profile_view.dart'; 10 | import '../modules/customerProfile/bindings/customer_profile_binding.dart'; 11 | import '../modules/customerProfile/views/customer_profile_view.dart'; 12 | import '../modules/home/bindings/home_binding.dart'; 13 | import '../modules/home/views/home_view.dart'; 14 | import '../modules/root/bindings/root_binding.dart'; 15 | import '../modules/root/views/root_view.dart'; 16 | import '../modules/saved/bindings/saved_binding.dart'; 17 | import '../modules/saved/views/saved_view.dart'; 18 | import '../modules/search/bindings/search_binding.dart'; 19 | import '../modules/search/views/search_view.dart'; 20 | import '../modules/waiting/bindings/waiting_binding.dart'; 21 | import '../modules/waiting/views/waiting_view.dart'; 22 | 23 | part 'app_routes.dart'; 24 | 25 | class AppPages { 26 | AppPages._(); 27 | 28 | static const INITIAL = Routes.LOGIN; 29 | 30 | static final routes = [ 31 | GetPage( 32 | name: _Paths.HOME, 33 | page: () => const HomeView(), 34 | binding: HomeBinding(), 35 | ), 36 | GetPage( 37 | name: _Paths.LOGIN, 38 | page: () => const LoginView(), 39 | binding: AuthBinding(), 40 | ), 41 | GetPage( 42 | name: _Paths.REGISTER, 43 | page: () => const RegisterView(), 44 | binding: AuthBinding(), 45 | ), 46 | GetPage( 47 | name: _Paths.WAITTING, 48 | page: () => const WaitingView(), 49 | binding: WaitingBinding(), 50 | ), 51 | GetPage( 52 | name: _Paths.SEARCH, 53 | page: () => const SearchView(), 54 | binding: SearchBinding(), 55 | ), 56 | GetPage( 57 | name: _Paths.SAVED, 58 | page: () => const SavedView(), 59 | binding: SavedBinding(), 60 | ), 61 | GetPage( 62 | name: _Paths.COMPANY_PROFILE, 63 | page: () => const CompanyProfileView(), 64 | binding: CompanyProfileBinding(), 65 | ), 66 | GetPage( 67 | name: _Paths.JOB_DETAILS, 68 | page: () => const JobDetailsView(), 69 | binding: JobDetailsBinding(), 70 | ), 71 | GetPage( 72 | name: _Paths.ROOT, 73 | page: () => const RootView(), 74 | binding: RootBinding(), 75 | ), 76 | GetPage( 77 | name: _Paths.CUSTOMER_PROFILE, 78 | page: () => const CustomerProfileView(), 79 | binding: CustomerProfileBinding(), 80 | ), 81 | ]; 82 | } 83 | -------------------------------------------------------------------------------- /lib/app/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | part of 'app_pages.dart'; 2 | 3 | abstract class Routes { 4 | Routes._(); 5 | 6 | static const HOME = _Paths.HOME; 7 | static const LOGIN = _Paths.LOGIN; 8 | static const REGISTER = _Paths.REGISTER; 9 | static const WAITTING = _Paths.WAITTING; 10 | static const SEARCH = _Paths.SEARCH; 11 | static const SAVED = _Paths.SAVED; 12 | static const COMPANY_PROFILE = _Paths.COMPANY_PROFILE; 13 | static const JOB_DETAILS = _Paths.JOB_DETAILS; 14 | static const ROOT = _Paths.ROOT; 15 | static const CUSTOMER_PROFILE = _Paths.CUSTOMER_PROFILE; 16 | } 17 | 18 | abstract class _Paths { 19 | _Paths._(); 20 | 21 | static const HOME = '/home'; 22 | static const LOGIN = '/login'; 23 | static const REGISTER = '/register'; 24 | static const WAITTING = '/waiting'; 25 | static const SEARCH = '/search'; 26 | static const SAVED = '/saved'; 27 | static const COMPANY_PROFILE = '/company-profile'; 28 | static const JOB_DETAILS = '/job_details'; 29 | static const ROOT = '/root'; 30 | static const CUSTOMER_PROFILE = '/customer-profile'; 31 | } 32 | -------------------------------------------------------------------------------- /lib/app/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | extension ToDataTime on String { 4 | DateTime toDateTime() { 5 | return DateTime.parse(this); 6 | } 7 | } 8 | 9 | extension ToShortDate on String { 10 | String toShortDate() { 11 | final format = DateFormat.yMMM().format(toDateTime()); 12 | return format.toString(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/app/utils/functions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | import '../widgets/custom_bottom_sheet.dart'; 7 | 8 | popupBottomSheet({ 9 | required Widget bottomSheetBody, 10 | bool? isDismissible, 11 | bool? enableDrag, 12 | }) { 13 | return Get.bottomSheet( 14 | CustomBottomSheet(body: bottomSheetBody), 15 | enableDrag: enableDrag ?? true, 16 | isDismissible: isDismissible ?? true, 17 | shape: RoundedRectangleBorder( 18 | borderRadius: BorderRadius.only( 19 | topRight: Radius.circular(22.r), 20 | topLeft: Radius.circular(22.r), 21 | ), 22 | ), 23 | isScrollControlled: true, 24 | backgroundColor: Colors.white, 25 | ); 26 | } 27 | 28 | void setDefaultStatusBarColor() { 29 | SystemChrome.setSystemUIOverlayStyle( 30 | const SystemUiOverlayStyle( 31 | statusBarColor: Colors.transparent, 32 | statusBarIconBrightness: Brightness.dark, 33 | systemNavigationBarColor: Colors.white, 34 | systemNavigationBarIconBrightness: Brightness.dark, 35 | ), 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /lib/app/utils/validators.dart: -------------------------------------------------------------------------------- 1 | import 'package:form_field_validator/form_field_validator.dart'; 2 | 3 | class Validators { 4 | static final name = MultiValidator([ 5 | RequiredValidator(errorText: "This field is required!"), 6 | MaxLengthValidator(150, errorText: "Max length is 150"), 7 | MinLengthValidator(2, errorText: "Name is too short!") 8 | ]); 9 | 10 | static final phoneNumber = MultiValidator([ 11 | RequiredValidator(errorText: "This field is required!"), 12 | ]); 13 | 14 | static final email = MultiValidator([ 15 | RequiredValidator(errorText: "This field is required!"), 16 | EmailValidator(errorText: "Provide valid email.") 17 | ]); 18 | 19 | static final password = MultiValidator([ 20 | RequiredValidator(errorText: "This field is required!"), 21 | MinLengthValidator(8, errorText: "Required 6 digitalis at least.") 22 | ]); 23 | 24 | static final address = MultiValidator([ 25 | RequiredValidator(errorText: "This field is required!"), 26 | MinLengthValidator(3, errorText: "Address is too short!") 27 | ]); 28 | } 29 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | class CustomAppBar extends StatelessWidget with PreferredSizeWidget { 8 | const CustomAppBar({ 9 | Key? key, 10 | this.leading, 11 | this.title, 12 | this.actions, 13 | this.backgroundColor, 14 | }) : super(key: key); 15 | final Widget? leading; 16 | final List? actions; 17 | final String? title; 18 | final Color? backgroundColor; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return AppBar( 23 | backgroundColor: backgroundColor ?? Colors.transparent, 24 | scrolledUnderElevation: 0, 25 | elevation: 0.0, 26 | leading: leading, 27 | title: title != null 28 | ? Text( 29 | title!, 30 | style: GoogleFonts.poppins( 31 | fontSize: 18.sp, 32 | fontWeight: FontWeight.w700, 33 | color: Get.theme.colorScheme.onBackground, 34 | ), 35 | ) 36 | : null, 37 | centerTitle: true, 38 | actions: actions, 39 | ); 40 | } 41 | 42 | @override 43 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 44 | } 45 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_avatar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:heroicons/heroicons.dart'; 5 | 6 | class CustomAvatar extends StatelessWidget { 7 | const CustomAvatar({ 8 | Key? key, 9 | required this.imageUrl, 10 | this.height, 11 | this.radius, 12 | }) : super(key: key); 13 | final String imageUrl; 14 | final double? height; 15 | final double? radius; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return CachedNetworkImage( 20 | imageUrl: imageUrl, 21 | height: height ?? 80.h, 22 | fit: BoxFit.contain, 23 | imageBuilder: (context, imageProvider) => ClipRRect( 24 | borderRadius: BorderRadius.circular(radius ?? 10000), 25 | child: Container( 26 | height: height ?? 80.h, 27 | width: height ?? 80.h, 28 | decoration: BoxDecoration( 29 | image: DecorationImage( 30 | image: imageProvider, 31 | fit: BoxFit.contain, 32 | ), 33 | ), 34 | ), 35 | ), 36 | placeholder: (context, url) => Container( 37 | height: height ?? 80.h, 38 | width: height ?? 80.h, 39 | decoration: BoxDecoration( 40 | shape: BoxShape.circle, 41 | color: Colors.grey[300], 42 | ), 43 | child: const Center( 44 | child: CircularProgressIndicator(), 45 | ), 46 | ), 47 | errorWidget: (context, url, error) => Container( 48 | height: height ?? 80.h, 49 | width: height ?? 80.h, 50 | decoration: BoxDecoration( 51 | shape: BoxShape.circle, 52 | color: Colors.grey[300], 53 | ), 54 | child: Center( 55 | child: HeroIcon( 56 | HeroIcons.exclamationCircle, 57 | color: Colors.grey[500], 58 | size: 0.5 * (height ?? 80.h), 59 | ), 60 | ), 61 | ), 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class CustomBottomSheet extends StatelessWidget { 6 | const CustomBottomSheet({Key? key, required this.body}) : super(key: key); 7 | final Widget body; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Padding( 12 | padding: EdgeInsets.only( 13 | bottom: Get.mediaQuery.viewInsets.bottom, 14 | left: 29.w, 15 | right: 29.w, 16 | ), 17 | child: Column( 18 | mainAxisSize: MainAxisSize.min, 19 | children: [ 20 | Container( 21 | margin: EdgeInsets.only(top: 28.h, bottom: 50.h), 22 | width: 30.w, 23 | height: 4.h, 24 | decoration: BoxDecoration( 25 | borderRadius: BorderRadius.circular(22.r), 26 | color: Get.theme.colorScheme.onBackground, 27 | ), 28 | ), 29 | body, 30 | SizedBox(height: 50.h) 31 | ], 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class CustomButton extends StatelessWidget { 6 | CustomButton({ 7 | Key? key, 8 | required this.title, 9 | required this.onTap, 10 | }) : super(key: key); 11 | final String title; 12 | final Future Function() onTap; 13 | 14 | final RxBool _isLoading = false.obs; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Obx( 19 | () => SizedBox( 20 | width: double.infinity, 21 | child: ElevatedButton( 22 | onPressed: _isLoading.value 23 | ? null 24 | : () async { 25 | _isLoading.value = true; 26 | await onTap(); 27 | _isLoading.value = false; 28 | }, 29 | style: Get.theme.elevatedButtonTheme.style, 30 | child: FittedBox( 31 | child: _isLoading.value 32 | ? SizedBox( 33 | height: 20.h, 34 | width: 20.h, 35 | child: const CircularProgressIndicator(color: Colors.white), 36 | ) 37 | : Text(title), 38 | ), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_chip.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | class CustomChip extends StatelessWidget { 7 | const CustomChip({ 8 | Key? key, 9 | required this.title, 10 | this.isActive = false, 11 | required this.onPressed, 12 | }) : super(key: key); 13 | final String title; 14 | final bool isActive; 15 | final void Function() onPressed; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | margin: EdgeInsets.only(right: 6.w), 21 | child: ActionChip( 22 | padding: EdgeInsets.all(6.w), 23 | label: Text(title), 24 | labelStyle: GoogleFonts.poppins( 25 | fontSize: 13.sp, 26 | fontWeight: FontWeight.w500, 27 | color: isActive ? Colors.white : Get.theme.colorScheme.secondary, 28 | ), 29 | elevation: 0.0, 30 | side: BorderSide( 31 | color: isActive 32 | ? Get.theme.colorScheme.primary 33 | : Get.theme.colorScheme.secondary, 34 | width: 1.w), 35 | labelPadding: EdgeInsets.symmetric(vertical: 1.w, horizontal: 15.w), 36 | shape: RoundedRectangleBorder( 37 | borderRadius: BorderRadius.circular(14.r), 38 | ), 39 | onPressed: onPressed, 40 | backgroundColor: isActive 41 | ? Get.theme.colorScheme.primary 42 | : Get.theme.backgroundColor, 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_info_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | class CustomInfoCard extends StatelessWidget { 8 | const CustomInfoCard({ 9 | Key? key, 10 | required this.child, 11 | required this.icon, 12 | required this.title, 13 | this.action, 14 | this.onActionTap, 15 | }) : super(key: key); 16 | final Widget child; 17 | final HeroIcons icon; 18 | final HeroIcons? action; 19 | final String title; 20 | final void Function()? onActionTap; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Container( 25 | padding: EdgeInsets.all(20.w), 26 | margin: EdgeInsets.only(bottom: 16.h, right: 16.w, left: 16.w), 27 | decoration: BoxDecoration( 28 | borderRadius: BorderRadius.circular(14.r), 29 | color: Colors.white, 30 | boxShadow: [ 31 | BoxShadow( 32 | color: Colors.grey.withOpacity(0.05), 33 | blurRadius: 20, 34 | offset: const Offset(0, 10), 35 | ), 36 | ], 37 | ), 38 | child: Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Row( 42 | children: [ 43 | HeroIcon( 44 | icon, 45 | color: Get.theme.colorScheme.primary, 46 | ), 47 | SizedBox(width: 10.w), 48 | Text( 49 | title, 50 | style: GoogleFonts.poppins( 51 | fontSize: 13.sp, 52 | fontWeight: FontWeight.w700, 53 | color: Get.theme.colorScheme.onBackground, 54 | ), 55 | ), 56 | const Spacer(), 57 | if (action != null) 58 | GestureDetector( 59 | onTap: onActionTap, 60 | child: HeroIcon(action!), 61 | ) 62 | ], 63 | ), 64 | SizedBox(height: 10.h), 65 | Divider( 66 | color: Get.theme.colorScheme.background, 67 | thickness: 1.5, 68 | ), 69 | SizedBox(height: 10.h), 70 | child, 71 | ], 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_lottie.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:lottie/lottie.dart'; 6 | 7 | class CustomLottie extends StatelessWidget { 8 | const CustomLottie({ 9 | Key? key, 10 | required this.title, 11 | this.onTryAgain, 12 | required this.asset, 13 | this.repeat = false, 14 | this.description, 15 | this.assetHeight, 16 | this.padding, 17 | this.titleStyle, 18 | this.descriptionStyle, 19 | }) : super(key: key); 20 | final String title; 21 | final String? description; 22 | final String asset; 23 | final bool repeat; 24 | final void Function()? onTryAgain; 25 | final double? assetHeight; 26 | final EdgeInsetsGeometry? padding; 27 | final TextStyle? titleStyle; 28 | final TextStyle? descriptionStyle; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Center( 33 | child: Padding( 34 | padding: padding ?? const EdgeInsets.only(bottom: kToolbarHeight), 35 | child: Column( 36 | mainAxisAlignment: MainAxisAlignment.center, 37 | mainAxisSize: MainAxisSize.min, 38 | children: [ 39 | LottieBuilder.asset( 40 | asset, 41 | height: assetHeight ?? 250.h, 42 | fit: BoxFit.contain, 43 | repeat: repeat, 44 | ), 45 | Text( 46 | title, 47 | style: titleStyle ?? 48 | GoogleFonts.poppins( 49 | fontSize: 13.sp, 50 | color: Get.theme.colorScheme.secondary, 51 | fontWeight: FontWeight.w500, 52 | ), 53 | ), 54 | if (description != null) SizedBox(height: 5.w), 55 | if (description != null) 56 | Text( 57 | description!, 58 | textAlign: TextAlign.center, 59 | style: descriptionStyle ?? 60 | GoogleFonts.poppins( 61 | color: Get.theme.colorScheme.tertiary, 62 | fontSize: 13.sp, 63 | fontWeight: FontWeight.w400, 64 | ), 65 | ), 66 | if (onTryAgain != null) 67 | TextButton( 68 | onPressed: onTryAgain, 69 | child: Text( 70 | "Try again", 71 | style: GoogleFonts.poppins( 72 | fontSize: 13.sp, 73 | fontWeight: FontWeight.w700, 74 | color: Get.theme.colorScheme.primary, 75 | ), 76 | ), 77 | ), 78 | ], 79 | ), 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/app/widgets/custom_tag.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:heroicons/heroicons.dart'; 6 | 7 | class CustomTag extends StatelessWidget { 8 | const CustomTag({ 9 | Key? key, 10 | required this.icon, 11 | required this.title, 12 | this.isFeatured = false, 13 | required this.titleColor, 14 | required this.backgroundColor, 15 | }) : super(key: key); 16 | final HeroIcons icon; 17 | final String title; 18 | final bool isFeatured; 19 | final Color titleColor; 20 | final Color backgroundColor; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Container( 25 | margin: EdgeInsets.only(right: 6.w , top: 6.h), 26 | padding: EdgeInsets.all(8.w), 27 | decoration: BoxDecoration( 28 | color: backgroundColor, 29 | borderRadius: BorderRadius.circular(10.r), 30 | ), 31 | child: Row( 32 | crossAxisAlignment: CrossAxisAlignment.center, 33 | mainAxisSize: MainAxisSize.min, 34 | children: [ 35 | HeroIcon( 36 | icon, 37 | color: titleColor, 38 | size: 15.w, 39 | ), 40 | SizedBox(width: 5.w), 41 | Text( 42 | title.capitalize!, 43 | style: GoogleFonts.poppins( 44 | fontSize: 12.sp, 45 | fontWeight: FontWeight.w500, 46 | color: titleColor, 47 | ), 48 | ) 49 | ], 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/app/widgets/section_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | 6 | class SectionHeader extends StatelessWidget { 7 | const SectionHeader({ 8 | Key? key, 9 | required this.title, 10 | this.actionTitle, 11 | }) : super(key: key); 12 | final String title; 13 | final String? actionTitle; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Padding( 18 | padding: EdgeInsets.symmetric(horizontal: 16.w), 19 | child: Row( 20 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 21 | children: [ 22 | Text( 23 | title, 24 | style: GoogleFonts.poppins( 25 | fontSize: 16.sp, 26 | fontWeight: FontWeight.w600, 27 | color: Get.theme.colorScheme.onBackground, 28 | ), 29 | ), 30 | if (actionTitle != null) 31 | Text( 32 | actionTitle!, 33 | style: GoogleFonts.poppins( 34 | fontSize: 13.sp, 35 | fontWeight: FontWeight.w400, 36 | color: Get.theme.colorScheme.secondary, 37 | ), 38 | ), 39 | ], 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/chips_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'shimmer_widget.dart'; 5 | 6 | class ChipsShimmer extends StatelessWidget { 7 | const ChipsShimmer({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return ListView.builder( 12 | itemCount: 8, 13 | padding: EdgeInsets.only(left: 16.w), 14 | scrollDirection: Axis.horizontal, 15 | shrinkWrap: true, 16 | itemBuilder: (context, index) => Container( 17 | margin: EdgeInsets.only(right: 6.w), 18 | child: ShimmerWidget( 19 | width: 100.w, 20 | height: 0.0, 21 | ), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/company_profile_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'shimmer_widget.dart'; 5 | 6 | class CompanyProfileShimmer extends StatelessWidget { 7 | const CompanyProfileShimmer({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return SingleChildScrollView( 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.start, 14 | children: [ 15 | ShimmerWidget( 16 | width: double.infinity, 17 | height: 210.h, 18 | radius: 0, 19 | ), 20 | Container( 21 | padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), 22 | child: ShimmerWidget( 23 | width: double.infinity, 24 | height: 60.h, 25 | ), 26 | ), 27 | Column( 28 | children: List.generate( 29 | 4, 30 | (index) => ShimmerWidget( 31 | width: double.infinity, 32 | height: 150.h, 33 | margin: EdgeInsets.only(right: 16.w, left: 16.w, bottom: 16.h), 34 | ), 35 | ), 36 | ) 37 | ], 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/customer_profile_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'shimmer_widget.dart'; 5 | 6 | class CustomerProfileShimmer extends StatelessWidget { 7 | const CustomerProfileShimmer({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return SingleChildScrollView( 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.start, 14 | children: [ 15 | ShimmerWidget( 16 | width: double.infinity, 17 | height: 210.h, 18 | radius: 0, 19 | margin: EdgeInsets.only(bottom: 8.h), 20 | ), 21 | Column( 22 | children: List.generate( 23 | 4, 24 | (index) => ShimmerWidget( 25 | width: double.infinity, 26 | height: 200.h, 27 | margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), 28 | ), 29 | ), 30 | ) 31 | ], 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/featured_job_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'shimmer_widget.dart'; 5 | 6 | class FeaturedJobShimmer extends StatelessWidget { 7 | const FeaturedJobShimmer({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | padding: EdgeInsets.all(16.w), 13 | child: ShimmerWidget( 14 | width: double.infinity, 15 | height: 170.h, 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/job_details_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | import 'shimmer_widget.dart'; 5 | 6 | class JobDetailsShimmer extends StatelessWidget { 7 | const JobDetailsShimmer({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return SingleChildScrollView( 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.start, 14 | children: [ 15 | ShimmerWidget( 16 | width: double.infinity, 17 | height: 225.h, 18 | radius: 0, 19 | ), 20 | SizedBox(height: 20.h), 21 | ShimmerWidget( 22 | width: double.infinity, 23 | height: 300.h, 24 | margin: EdgeInsets.symmetric(horizontal: 16.w), 25 | ), 26 | ShimmerWidget( 27 | width: double.infinity, 28 | height: 100.h, 29 | margin: EdgeInsets.all(16.w), 30 | ), 31 | ShimmerWidget( 32 | width: 200.w, 33 | height: 20.h, 34 | margin: EdgeInsets.symmetric(horizontal: 16.w), 35 | ), 36 | SizedBox(height: 16.h), 37 | Padding( 38 | padding: EdgeInsets.symmetric(horizontal: 16.w), 39 | child: Column( 40 | crossAxisAlignment: CrossAxisAlignment.start, 41 | children: [ 42 | SingleChildScrollView( 43 | scrollDirection: Axis.horizontal, 44 | physics: const BouncingScrollPhysics(), 45 | child: Row( 46 | children: List.generate( 47 | 2, 48 | (index) => ShimmerWidget( 49 | width: 0.65.sw, 50 | height: 200.h, 51 | margin: EdgeInsets.only(right: 16.w), 52 | ), 53 | ), 54 | ), 55 | ), 56 | ], 57 | ), 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/recent_jobs_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:jobs_flutter_app/app/widgets/shimmer/shimmer_widget.dart'; 4 | 5 | class RecentJobsShimmer extends StatelessWidget { 6 | const RecentJobsShimmer({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return ListView.builder( 11 | itemCount: 3, 12 | shrinkWrap: true, 13 | physics: const NeverScrollableScrollPhysics(), 14 | itemBuilder: (context, index) => Container( 15 | padding: EdgeInsets.symmetric(horizontal: 16.w), 16 | margin: EdgeInsets.only(bottom: 16.w), 17 | child: ShimmerWidget( 18 | width: double.infinity, 19 | height: 200.h, 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/app/widgets/shimmer/shimmer_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:shimmer/shimmer.dart'; 5 | 6 | class ShimmerWidget extends StatelessWidget { 7 | const ShimmerWidget({ 8 | Key? key, 9 | required this.width, 10 | required this.height, 11 | this.radius, 12 | this.child, 13 | this.margin, 14 | }) : super(key: key); 15 | final double width; 16 | final double height; 17 | final double? radius; 18 | final Widget? child; 19 | final EdgeInsetsGeometry? margin; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Shimmer.fromColors( 24 | baseColor: Get.theme.primaryColor.withOpacity(0.38), 25 | highlightColor: Get.theme.colorScheme.background, 26 | child: Container( 27 | width: width, 28 | height: height, 29 | margin: margin ?? EdgeInsets.zero, 30 | decoration: BoxDecoration( 31 | shape: BoxShape.rectangle, 32 | borderRadius: BorderRadius.circular(radius ?? 14.r), 33 | color: Get.theme.primaryColor.withOpacity(0.38), 34 | ), 35 | child: child, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:country_codes/country_codes.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:get_storage/get_storage.dart'; 7 | 8 | import 'app/core/theme/app_theme.dart'; 9 | import 'app/di/locator.dart'; 10 | import 'app/modules/auth/bindings/auth_binding.dart'; 11 | import 'app/modules/auth/controllers/auth_controller.dart'; 12 | import 'app/modules/auth/views/login/login_view.dart'; 13 | import 'app/modules/root/views/root_view.dart'; 14 | import 'app/routes/app_pages.dart'; 15 | 16 | void main() async { 17 | setupLocator(); 18 | await GetStorage.init(); 19 | await CountryCodes.init(); 20 | SystemChrome.setPreferredOrientations([ 21 | DeviceOrientation.portraitUp, 22 | DeviceOrientation.portraitDown, 23 | ]); 24 | runApp( 25 | ScreenUtilInit( 26 | designSize: const Size(375, 812), 27 | minTextAdapt: true, 28 | splitScreenMode: true, 29 | builder: (_, child) => GetMaterialApp( 30 | locale: Get.locale, 31 | debugShowCheckedModeBanner: false, 32 | initialBinding: AuthBinding(), 33 | home: Obx( 34 | () => AuthController.to.currentUser != null 35 | ? const RootView() 36 | : const LoginView(), 37 | ), 38 | getPages: AppPages.routes, 39 | theme: AppTheme.lightTheme, 40 | defaultTransition: Transition.cupertino, 41 | ), 42 | ), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: jobs_flutter_app 2 | version: 1.0.0+1 3 | publish_to: none 4 | description: A new Flutter project. 5 | environment: 6 | sdk: '>=2.17.6 <3.0.0' 7 | 8 | dependencies: 9 | cupertino_icons: ^1.0.2 10 | get: 4.6.5 11 | google_fonts: 3.0.1 12 | flutter_screenutil: 5.5.4 13 | cached_network_image: ^3.2.2 14 | flutter_svg: 1.1.5 15 | lottie: 1.4.2 16 | carousel_slider: 4.1.1 17 | flutter_markdown: 0.6.12 18 | persistent_bottom_nav_bar_v2: 4.2.5 19 | dio: 4.0.6 20 | get_it: 7.2.0 21 | freezed_annotation: ^2.1.0 22 | json_annotation: ^4.7.0 23 | intl: 0.17.0 24 | get_storage: 2.0.3 25 | heroicons: 0.7.0 26 | form_field_validator: 1.1.0 27 | intl_phone_field: 3.1.0 28 | shimmer: 2.0.0 29 | awesome_dialog: 3.0.2 30 | awesome_snackbar_content: 0.0.8 31 | country_codes: 2.2.0 32 | smooth_page_indicator: 1.0.0+2 33 | url_launcher: 6.1.6 34 | flutter_svg_provider: 1.0.3 35 | expandable: 5.0.1 36 | flutter: 37 | sdk: flutter 38 | 39 | dev_dependencies: 40 | build_runner: ^2.2.1 41 | flutter_lints: ^2.0.0 42 | freezed: ^2.1.1 43 | json_serializable: ^6.4.0 44 | flutter_native_splash: ^2.2.13 45 | logger: 1.1.0 46 | device_preview: ^1.1.0 47 | flutter_test: 48 | sdk: flutter 49 | 50 | flutter_native_splash: 51 | color: '#03A9F4' 52 | color_dark: '#23A9F4' 53 | android_12: 54 | color_dark: '#23A9F4' 55 | web: false 56 | 57 | flutter: 58 | assets: 59 | - assets/ 60 | uses-material-design: true 61 | 62 | --------------------------------------------------------------------------------