├── devtools_options.yaml ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── assets ├── avatars │ └── rdj.png └── images │ └── empty.png ├── lib ├── l10n.yaml ├── repositories │ ├── applicant.dart │ ├── user.dart │ ├── contact.dart │ ├── personal.dart │ ├── language.dart │ ├── certificate.dart │ ├── skill.dart │ ├── reference.dart │ ├── award.dart │ ├── interest.dart │ ├── experience.dart │ ├── education.dart │ ├── auth.dart │ └── resume.dart ├── providers │ ├── user_profile_provider.dart │ ├── resume_list_provider.dart │ ├── current_resume_provider.dart │ ├── contact_data_provider.dart │ ├── personal_data_provider.dart │ ├── experience_list_provider.dart │ ├── user_data_provider.dart │ └── settings_provider.dart ├── widgets │ ├── dialog_helper.dart │ ├── empty_view.dart │ ├── custom_button.dart │ └── custom_text_form_field.dart ├── utils │ ├── colors.dart │ ├── local_storage.dart │ ├── urls.dart │ ├── constants.dart │ ├── routes_handler.dart │ ├── theme.dart │ └── helper.dart ├── models │ ├── applicant.dart │ ├── skill.dart │ ├── language.dart │ ├── certificate.dart │ ├── reference.dart │ ├── award.dart │ ├── interest.dart │ ├── education.dart │ ├── experience.dart │ ├── contact.dart │ ├── user.dart │ ├── resume.dart │ ├── personal.dart │ └── resume_preview.dart ├── screens │ ├── utility_screens │ │ ├── not_found_screen.dart │ │ ├── account_settings_screen.dart │ │ ├── splash_screen.dart │ │ ├── email_update_screen.dart │ │ └── settings_screen.dart │ ├── auth_screens │ │ └── login_screen.dart │ ├── profile_screens │ │ └── profile_screen.dart │ └── main_screens │ │ └── resume_details │ │ ├── interest │ │ └── add_edit_interest_screen.dart │ │ └── language │ │ └── language_screen.dart ├── main.dart ├── l10n │ ├── app_en.arb │ └── app_bn.arb └── apis │ └── api.dart ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── meheraj │ │ │ │ │ └── gocv │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── test └── widget_test.dart ├── .gitignore ├── LICENSE ├── analysis_options.yaml ├── .metadata ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── pubspec.yaml └── README.md /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | extensions: 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /assets/avatars/rdj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/assets/avatars/rdj.png -------------------------------------------------------------------------------- /assets/images/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/assets/images/empty.png -------------------------------------------------------------------------------- /lib/l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | template-arb-file: app_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/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/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeherajUlMahmmud/GoCV/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/meheraj/gocv/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.meheraj.gocv 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /lib/repositories/applicant.dart: -------------------------------------------------------------------------------- 1 | import 'package:gocv/providers/user_data_provider.dart'; 2 | 3 | class ApplicantRepository { 4 | String getAccessToken() { 5 | return UserProvider().tokens['access']; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.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.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /lib/providers/user_profile_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../models/user.dart'; 4 | 5 | class UserProfileProvider with ChangeNotifier { 6 | UserProfile _userProfile = UserProfile(); 7 | 8 | UserProfile get userProfile => _userProfile; 9 | 10 | void setUserProfile(UserProfile userProfile) { 11 | _userProfile = userProfile; 12 | notifyListeners(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/widgets/dialog_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DialogHelper { 4 | static void showCustomDialog({ 5 | required BuildContext context, 6 | required String title, 7 | required Widget content, 8 | required List actions, 9 | }) { 10 | AlertDialog alert = AlertDialog( 11 | title: Text(title), 12 | content: content, 13 | actions: actions, 14 | ); 15 | 16 | showDialog( 17 | context: context, 18 | builder: (BuildContext context) { 19 | return alert; 20 | }, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/providers/resume_list_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/resume.dart'; 3 | 4 | class ResumeListProvider with ChangeNotifier { 5 | final List _resumeList = []; 6 | 7 | List get resumeList => _resumeList; 8 | 9 | void addResume(Resume resume) { 10 | _resumeList.add(resume); 11 | notifyListeners(); 12 | } 13 | 14 | void setResumeList(List resumeList) { 15 | _resumeList.clear(); 16 | _resumeList.addAll(resumeList); 17 | notifyListeners(); 18 | } 19 | 20 | void removeResume(int index) { 21 | _resumeList.removeAt(index); 22 | notifyListeners(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/utils/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | MaterialColor createMaterialColor(Color color) { 4 | List strengths = [.05]; 5 | Map swatch = {}; 6 | final int r = color.red, g = color.green, b = color.blue; 7 | 8 | for (int i = 1; i < 10; i++) { 9 | strengths.add(0.1 * i); 10 | } 11 | for (var strength in strengths) { 12 | final double ds = 0.5 - strength; 13 | swatch[(strength * 1000).round()] = Color.fromRGBO( 14 | r + ((ds < 0 ? r : (255 - r)) * ds).round(), 15 | g + ((ds < 0 ? g : (255 - g)) * ds).round(), 16 | b + ((ds < 0 ? b : (255 - b)) * ds).round(), 17 | 1, 18 | ); 19 | } 20 | 21 | return MaterialColor(color.value, swatch); 22 | } 23 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.0' 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 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:gocv/main.dart'; 5 | 6 | void main() { 7 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 8 | // Build our app and trigger a frame. 9 | await tester.pumpWidget(const App()); 10 | 11 | // Verify that our counter starts at 0. 12 | expect(find.text('0'), findsOneWidget); 13 | expect(find.text('1'), findsNothing); 14 | 15 | // Tap the '+' icon and trigger a frame. 16 | await tester.tap(find.byIcon(Icons.add)); 17 | await tester.pump(); 18 | 19 | // Verify that our counter has incremented. 20 | expect(find.text('0'), findsNothing); 21 | expect(find.text('1'), findsOneWidget); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /lib/widgets/empty_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EmptyView extends StatelessWidget { 4 | const EmptyView({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: Column( 10 | mainAxisSize: MainAxisSize.min, 11 | children: [ 12 | Image.asset( 13 | 'assets/images/empty.png', 14 | height: 300.0, 15 | width: 300.0, 16 | ), 17 | const Text( 18 | 'Nothing is here yet', 19 | style: TextStyle( 20 | fontSize: 24.0, 21 | color: Colors.greenAccent, 22 | fontWeight: FontWeight.bold, 23 | ), 24 | ), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/providers/current_resume_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/resume.dart'; 3 | 4 | class CurrentResumeProvider with ChangeNotifier { 5 | late Resume _currentResume; 6 | late String _personalId; 7 | late String _contactId; 8 | 9 | Resume get currentResume => _currentResume; 10 | String get personalId => _personalId; 11 | String get contactId => _contactId; 12 | 13 | void setCurrentResume(Resume resume) { 14 | _currentResume = resume; 15 | notifyListeners(); 16 | } 17 | 18 | void updateCurrentResume(Resume resume) { 19 | _currentResume = resume; 20 | notifyListeners(); 21 | } 22 | 23 | void setPersonalId(String id) { 24 | _personalId = id; 25 | notifyListeners(); 26 | } 27 | 28 | void setContactId(String id) { 29 | _contactId = id; 30 | notifyListeners(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.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 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/providers/contact_data_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/contact.dart'; 3 | 4 | class ContactDataProvider extends ChangeNotifier { 5 | final Contact _contactData = Contact( 6 | id: 0, 7 | email: '', 8 | ); 9 | 10 | Contact get contactData => _contactData; 11 | 12 | void setContactData(Contact contactData) { 13 | _contactData.id = contactData.id; 14 | _contactData.resume = contactData.resume; 15 | _contactData.phoneNumber = contactData.phoneNumber; 16 | _contactData.email = contactData.email; 17 | _contactData.address = contactData.address; 18 | _contactData.zipCode = contactData.zipCode; 19 | _contactData.facebook = contactData.facebook; 20 | _contactData.linkedin = contactData.linkedin; 21 | _contactData.github = contactData.github; 22 | _contactData.website = contactData.website; 23 | notifyListeners(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/providers/personal_data_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/personal.dart'; 3 | 4 | class PersonalDataProvider extends ChangeNotifier { 5 | final Personal _personalData = Personal(); 6 | 7 | Personal get personalData => _personalData; 8 | 9 | void setPersonalData(Personal personalData) { 10 | _personalData.id = personalData.id; 11 | _personalData.firstName = personalData.firstName; 12 | _personalData.lastName = personalData.lastName; 13 | _personalData.aboutMe = personalData.aboutMe; 14 | _personalData.dateOfBirth = personalData.dateOfBirth; 15 | _personalData.city = personalData.city; 16 | _personalData.state = personalData.state; 17 | _personalData.country = personalData.country; 18 | _personalData.nationality = personalData.nationality; 19 | _personalData.resumePicture = personalData.resumePicture; 20 | notifyListeners(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/models/applicant.dart: -------------------------------------------------------------------------------- 1 | class Applicant { 2 | int? id; 3 | String firstName = ''; 4 | String lastName = ''; 5 | String? profilePicture; 6 | String? phoneNumber; 7 | int? resume; 8 | 9 | Applicant({ 10 | this.id, 11 | required this.firstName, 12 | required this.lastName, 13 | this.profilePicture, 14 | this.phoneNumber, 15 | this.resume, 16 | }); 17 | 18 | Applicant.fromJson(Map json) { 19 | id = json['id']; 20 | firstName = json['first_name']; 21 | lastName = json['last_name']; 22 | profilePicture = json['profile_picture']; 23 | phoneNumber = json['phone_number']; 24 | resume = json['resume']; 25 | } 26 | 27 | Map toJson() { 28 | final Map data = {}; 29 | data['first_name'] = firstName; 30 | data['last_name'] = lastName; 31 | data['profile_picture'] = profilePicture; 32 | data['phone_number'] = phoneNumber; 33 | data['resume'] = resume; 34 | data['id'] = id; 35 | return data; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/models/skill.dart: -------------------------------------------------------------------------------- 1 | class Skill { 2 | int id = 0; 3 | int resume = 0; 4 | String name = ''; 5 | String? proficiency; 6 | String? description; 7 | bool? isActive; 8 | bool? isDeleted; 9 | int? createdBy; 10 | String? createdAt; 11 | int? updatedBy; 12 | String? updatedAt; 13 | 14 | Skill({ 15 | required this.id, 16 | required this.resume, 17 | required this.name, 18 | this.proficiency, 19 | this.description, 20 | this.isActive, 21 | this.isDeleted, 22 | this.createdBy, 23 | this.createdAt, 24 | this.updatedBy, 25 | this.updatedAt, 26 | }); 27 | 28 | Skill.fromJson(Map json) { 29 | resume = json['resume']; 30 | name = json['name']; 31 | proficiency = json['proficiency']; 32 | description = json['description']; 33 | id = json['id']; 34 | isActive = json['is_active']; 35 | isDeleted = json['is_deleted']; 36 | createdBy = json['created_by']; 37 | createdAt = json['created_at']; 38 | updatedBy = json['updated_by']; 39 | updatedAt = json['updated_at']; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/models/language.dart: -------------------------------------------------------------------------------- 1 | class Language { 2 | int id = 0; 3 | int resume = 0; 4 | String name = ''; 5 | String? proficiency; 6 | String? description; 7 | bool? isActive; 8 | bool? isDeleted; 9 | int? createdBy; 10 | String? createdAt; 11 | int? updatedBy; 12 | String? updatedAt; 13 | 14 | Language({ 15 | required this.id, 16 | required this.resume, 17 | required this.name, 18 | this.proficiency, 19 | this.description, 20 | this.isActive, 21 | this.isDeleted, 22 | this.createdBy, 23 | this.createdAt, 24 | this.updatedBy, 25 | this.updatedAt, 26 | }); 27 | 28 | Language.fromJson(Map json) { 29 | resume = json['resume']; 30 | name = json['name']; 31 | proficiency = json['proficiency']; 32 | description = json['description']; 33 | id = json['id']; 34 | isActive = json['is_active']; 35 | isDeleted = json['is_deleted']; 36 | createdBy = json['created_by']; 37 | createdAt = json['created_at']; 38 | updatedBy = json['updated_by']; 39 | updatedAt = json['updated_at']; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/providers/experience_list_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/experience.dart'; 3 | 4 | class ExperienceListProvider extends ChangeNotifier { 5 | final List _experienceList = []; 6 | 7 | List get experienceList => _experienceList; 8 | 9 | void setExperienceList(List experienceList) { 10 | _experienceList.clear(); 11 | _experienceList.addAll(experienceList); 12 | notifyListeners(); 13 | } 14 | 15 | void addExperience(Experience experience) { 16 | _experienceList.add(experience); 17 | notifyListeners(); 18 | } 19 | 20 | void updateExperience(String experienceId, Experience experience) { 21 | final int index = _experienceList 22 | .indexWhere((element) => element.id.toString() == experienceId); 23 | // print('index: $index'); 24 | if (index != -1) { 25 | _experienceList[index] = experience; 26 | notifyListeners(); 27 | } 28 | } 29 | 30 | void removeExperience(Experience experience) { 31 | _experienceList.remove(experience); 32 | notifyListeners(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/repositories/user.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class UserRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getUserProfile() async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | const String url = '${URLS.kUserUrl}profile/'; 15 | 16 | final data = await APIService().sendGetRequest(accessToken, url); 17 | return data; 18 | } catch (error) { 19 | return Helper().handleApiError(error); 20 | } 21 | } 22 | 23 | updateUserProfile(String userId, Map data) async { 24 | try { 25 | final String accessToken = getAccessToken(); 26 | final String url = '${URLS.kUserUrl}$userId/update/'; 27 | 28 | final response = await APIService().sendPatchRequest( 29 | accessToken, 30 | data, 31 | url, 32 | ); 33 | return response; 34 | } catch (error) { 35 | return Helper().handleApiError(error); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Meheraj Ul Mahmmud 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 | -------------------------------------------------------------------------------- /lib/models/certificate.dart: -------------------------------------------------------------------------------- 1 | class Certificate { 2 | int? resume; 3 | String? title; 4 | String? description; 5 | String? link; 6 | String? startDate; 7 | String? endDate; 8 | int? id; 9 | bool? isActive; 10 | bool? isDeleted; 11 | int? createdBy; 12 | String? createdAt; 13 | int? updatedBy; 14 | String? updatedAt; 15 | 16 | Certificate({ 17 | this.resume, 18 | this.title, 19 | this.description, 20 | this.link, 21 | this.startDate, 22 | this.endDate, 23 | this.id, 24 | this.isActive, 25 | this.isDeleted, 26 | this.createdBy, 27 | this.createdAt, 28 | this.updatedBy, 29 | this.updatedAt, 30 | }); 31 | 32 | Certificate.fromJson(Map json) { 33 | resume = json['resume']; 34 | title = json['title']; 35 | description = json['description']; 36 | link = json['link']; 37 | startDate = json['start_date']; 38 | endDate = json['end_date']; 39 | id = json['id']; 40 | isActive = json['is_active']; 41 | isDeleted = json['is_deleted']; 42 | createdBy = json['created_by']; 43 | createdAt = json['created_at']; 44 | updatedBy = json['updated_by']; 45 | updatedAt = json['updated_at']; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/providers/user_data_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../models/user.dart'; 4 | 5 | class UserProvider with ChangeNotifier { 6 | // Singleton instance 7 | static final UserProvider _singleton = UserProvider._internal(); 8 | 9 | // Private constructor 10 | UserProvider._internal(); 11 | 12 | // Factory method to provide access to the singleton instance 13 | factory UserProvider() { 14 | return _singleton; 15 | } 16 | 17 | final Map _tokens = { 18 | 'access': '', 19 | 'refresh': '', 20 | }; 21 | UserBase? _userData; 22 | 23 | // Getters for tokens and userData 24 | Map get tokens => _tokens; 25 | UserBase? get userData => _userData; 26 | 27 | // Methods to modify tokens and userData 28 | void setTokens(Map tokens) { 29 | _tokens['access'] = tokens['access']; 30 | _tokens['refresh'] = tokens['refresh']; 31 | notifyListeners(); 32 | } 33 | 34 | void setUserData(UserBase userData) { 35 | _userData = userData; 36 | notifyListeners(); 37 | } 38 | 39 | void clearData() { 40 | _tokens['access'] = ''; 41 | _tokens['refresh'] = ''; 42 | _userData = null; 43 | notifyListeners(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/models/reference.dart: -------------------------------------------------------------------------------- 1 | class Reference { 2 | int id = 0; 3 | int resume = 0; 4 | String name = ''; 5 | String email = ''; 6 | String? phone; 7 | String? companyName; 8 | String? position; 9 | String? description; 10 | String? portfolio; 11 | int? createdBy; 12 | String? createdAt; 13 | int? updatedBy; 14 | String? updatedAt; 15 | 16 | Reference({ 17 | required this.id, 18 | required this.resume, 19 | required this.name, 20 | required this.email, 21 | this.phone, 22 | this.companyName, 23 | this.position, 24 | this.description, 25 | this.portfolio, 26 | this.createdBy, 27 | this.createdAt, 28 | this.updatedBy, 29 | this.updatedAt, 30 | }); 31 | 32 | Reference.fromJson(Map json) { 33 | id = json['id']; 34 | resume = json['resume']; 35 | name = json['name']; 36 | email = json['email']; 37 | phone = json['phone']; 38 | companyName = json['company_name']; 39 | position = json['position']; 40 | description = json['description']; 41 | portfolio = json['portfolio']; 42 | createdBy = json['created_by']; 43 | createdAt = json['created_at']; 44 | updatedBy = json['updated_by']; 45 | updatedAt = json['updated_at']; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/award.dart: -------------------------------------------------------------------------------- 1 | class Award { 2 | int? resume; 3 | String? title; 4 | String? description; 5 | String? link; 6 | int? id; 7 | bool? isActive; 8 | bool? isDeleted; 9 | int? createdBy; 10 | String? createdAt; 11 | int? updatedBy; 12 | String? updatedAt; 13 | 14 | Award( 15 | {this.resume, 16 | this.title, 17 | this.description, 18 | this.link, 19 | this.id, 20 | this.createdBy, 21 | this.createdAt, 22 | this.updatedBy, 23 | this.updatedAt}); 24 | 25 | Award.fromJson(Map json) { 26 | resume = json['resume']; 27 | title = json['title']; 28 | description = json['description']; 29 | link = json['link']; 30 | id = json['id']; 31 | isActive = json['is_active']; 32 | isDeleted = json['is_deleted']; 33 | createdBy = json['created_by']; 34 | createdAt = json['created_at']; 35 | updatedBy = json['updated_by']; 36 | updatedAt = json['updated_at']; 37 | } 38 | 39 | Map toJson() { 40 | final Map data = {}; 41 | data['resume'] = resume; 42 | data['title'] = title; 43 | data['description'] = description; 44 | data['link'] = link; 45 | data['id'] = id; 46 | return data; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/repositories/contact.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class ContactRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | Future> getContactDetails(String resumeId) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | final String url = '${URLS.kContactUrl}$resumeId/details/'; 15 | 16 | final data = await APIService().sendGetRequest( 17 | accessToken, 18 | url, 19 | ); 20 | return data; 21 | } catch (error) { 22 | return Helper().handleApiError(error); 23 | } 24 | } 25 | 26 | Future> updateContactDetails( 27 | String contactId, 28 | Map updatedData, 29 | ) async { 30 | try { 31 | final String accessToken = getAccessToken(); 32 | final String url = '${URLS.kContactUrl}$contactId/update/'; 33 | 34 | final data = await APIService().sendPatchRequest( 35 | accessToken, 36 | updatedData, 37 | url, 38 | ); 39 | return data; 40 | } catch (error) { 41 | return Helper().handleApiError(error); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/utils/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | class LocalStorage { 6 | Future _getPreferences() async { 7 | return await SharedPreferences.getInstance(); 8 | } 9 | 10 | Future> readData(String key) async { 11 | try { 12 | final prefs = await _getPreferences(); 13 | final data = prefs.getString(key); 14 | return data != null ? jsonDecode(data) : {}; 15 | } catch (e) { 16 | print(e); 17 | return {}; 18 | } 19 | } 20 | 21 | Future writeData(String key, Map data) async { 22 | final prefs = await _getPreferences(); 23 | return prefs.setString(key, jsonEncode(data)); 24 | } 25 | 26 | Future removeData(String key) async { 27 | final prefs = await _getPreferences(); 28 | return prefs.remove(key); 29 | } 30 | 31 | Future clearData() async { 32 | final prefs = await _getPreferences(); 33 | return prefs.clear(); 34 | } 35 | 36 | Future getBool(String key) async { 37 | final prefs = await _getPreferences(); 38 | return prefs.getBool(key) ?? false; 39 | } 40 | 41 | Future getDouble(String key) async { 42 | final prefs = await _getPreferences(); 43 | return prefs.getDouble(key) ?? 0.0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/models/interest.dart: -------------------------------------------------------------------------------- 1 | class Interest { 2 | int resume = 0; 3 | String name = ''; 4 | String? description; 5 | int id = 0; 6 | bool? isActive; 7 | bool? isDeleted; 8 | int? createdBy; 9 | String? createdAt; 10 | int? updatedBy; 11 | String? updatedAt; 12 | 13 | Interest({ 14 | required this.resume, 15 | required this.name, 16 | this.description, 17 | required this.id, 18 | this.isActive, 19 | this.isDeleted, 20 | this.createdBy, 21 | this.createdAt, 22 | this.updatedBy, 23 | this.updatedAt, 24 | }); 25 | 26 | Interest.fromJson(Map json) { 27 | resume = json['resume']; 28 | name = json['name']; 29 | description = json['description']; 30 | id = json['id']; 31 | isActive = json['is_active']; 32 | isDeleted = json['is_deleted']; 33 | createdBy = json['created_by']; 34 | createdAt = json['created_at']; 35 | updatedBy = json['updated_by']; 36 | updatedAt = json['updated_at']; 37 | } 38 | 39 | Map toJson() { 40 | final Map data = {}; 41 | data['resume'] = resume; 42 | data['name'] = name; 43 | data['description'] = description; 44 | data['id'] = id; 45 | data['is_active'] = isActive; 46 | data['is_deleted'] = isDeleted; 47 | data['created_by'] = createdBy; 48 | data['created_at'] = createdAt; 49 | data['updated_by'] = updatedBy; 50 | data['updated_at'] = updatedAt; 51 | return data; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/models/education.dart: -------------------------------------------------------------------------------- 1 | class Education { 2 | int id = 0; 3 | int resume = 0; 4 | String schoolName = ''; 5 | String? degree; 6 | String? department; 7 | String? gradeScale; 8 | String? grade; 9 | String startDate = ''; 10 | bool? isCurrent; 11 | String? endDate; 12 | String? description; 13 | bool? isActive; 14 | bool? isDeleted; 15 | int? createdBy; 16 | String? createdAt; 17 | int? updatedBy; 18 | String? updatedAt; 19 | 20 | Education({ 21 | required this.id, 22 | required this.resume, 23 | required this.schoolName, 24 | this.degree, 25 | this.department, 26 | this.gradeScale, 27 | this.grade, 28 | required this.startDate, 29 | this.isCurrent, 30 | this.endDate, 31 | this.description, 32 | this.isActive, 33 | this.isDeleted, 34 | this.createdBy, 35 | this.createdAt, 36 | this.updatedBy, 37 | this.updatedAt, 38 | }); 39 | 40 | Education.fromJson(Map json) { 41 | resume = json['resume']; 42 | schoolName = json['school_name']; 43 | degree = json['degree']; 44 | department = json['department']; 45 | gradeScale = json['grade_scale']; 46 | grade = json['grade']; 47 | startDate = json['start_date']; 48 | isCurrent = json['is_current']; 49 | endDate = json['end_date']; 50 | description = json['description']; 51 | id = json['id']; 52 | isActive = json['is_active']; 53 | isDeleted = json['is_deleted']; 54 | createdBy = json['created_by']; 55 | createdAt = json['created_at']; 56 | updatedBy = json['updated_by']; 57 | updatedAt = json['updated_at']; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/models/experience.dart: -------------------------------------------------------------------------------- 1 | class Experience { 2 | int id = 0; 3 | int resume = 0; 4 | String companyName = ''; 5 | String position = ''; 6 | String type = ''; 7 | String startDate = ''; 8 | bool? isCurrent; 9 | String? endDate; 10 | String? description; 11 | double? salary; 12 | String? companyWebsite; 13 | bool? isActive; 14 | bool? isDeleted; 15 | int? createdBy; 16 | String? createdAt; 17 | int? updatedBy; 18 | String? updatedAt; 19 | 20 | Experience({ 21 | required this.id, 22 | required this.resume, 23 | required this.companyName, 24 | required this.position, 25 | required this.type, 26 | required this.startDate, 27 | this.isCurrent, 28 | this.endDate, 29 | this.description, 30 | this.salary, 31 | this.companyWebsite, 32 | this.isActive, 33 | this.isDeleted, 34 | this.createdBy, 35 | this.createdAt, 36 | this.updatedBy, 37 | this.updatedAt, 38 | }); 39 | 40 | Experience.fromJson(Map json) { 41 | resume = json['resume']; 42 | companyName = json['company_name']; 43 | position = json['position']; 44 | type = json['type']; 45 | startDate = json['start_date']; 46 | isCurrent = json['is_current']; 47 | endDate = json['end_date']; 48 | description = json['description']; 49 | salary = json['salary']; 50 | companyWebsite = json['company_website']; 51 | id = json['id']; 52 | isActive = json['is_active']; 53 | isDeleted = json['is_deleted']; 54 | createdBy = json['created_by']; 55 | createdAt = json['created_at']; 56 | updatedBy = json['updated_by']; 57 | updatedAt = json['updated_at']; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/utils/urls.dart: -------------------------------------------------------------------------------- 1 | class URLS { 2 | // static const String kBaseUrl = "http://192.168.0.108:8000/"; 3 | // static const String kBaseUrl = "http://10.0.2.2:8000/"; 4 | static const String kBaseUrl = 5 | 'http://127.0.0.1:8000/api/'; // for iOS simulator 6 | 7 | // Auth 8 | static const String kAuthUrl = '${kBaseUrl}auth/'; 9 | static const String kLoginUrl = '${kAuthUrl}login/'; 10 | static const String kRegisterUrl = '${kAuthUrl}register/'; 11 | static const String kRefreshTokenUrl = '${kAuthUrl}refresh/'; 12 | 13 | // User 14 | static const String kUserUrl = '${kBaseUrl}user/'; 15 | 16 | // Applicant 17 | static const String kApplicantUrl = '${kBaseUrl}applicant/'; 18 | 19 | // Resume 20 | static const String kResumeUrl = '${kBaseUrl}resume/'; 21 | 22 | // Personal 23 | static const String kPersonalUrl = '${kBaseUrl}personal/'; 24 | 25 | // Contact 26 | static const String kContactUrl = '${kBaseUrl}contact/'; 27 | 28 | // Experience 29 | static const String kExperienceUrl = '${kBaseUrl}experience/'; 30 | 31 | // Education 32 | static const String kEducationUrl = '${kBaseUrl}education/'; 33 | 34 | // Skill 35 | static const String kSkillUrl = '${kBaseUrl}skill/'; 36 | 37 | // Award 38 | static const String kAwardUrl = '${kBaseUrl}award/'; 39 | 40 | // Certification 41 | static const String kCertificationUrl = '${kBaseUrl}certification/'; 42 | 43 | // Interest 44 | static const String kInterestUrl = '${kBaseUrl}interest/'; 45 | 46 | // Language 47 | static const String kLanguageUrl = '${kBaseUrl}language/'; 48 | 49 | // Reference 50 | static const String kReferenceUrl = '${kBaseUrl}reference/'; 51 | } 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/screens/utility_screens/not_found_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/utils/constants.dart'; 3 | 4 | class NotFoundScreen extends StatelessWidget { 5 | static const routeName = Constants.notFoundScreenRouteName; 6 | 7 | const NotFoundScreen({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: const Text( 14 | 'Not Found', 15 | ), 16 | leading: GestureDetector( 17 | onTap: () { 18 | Navigator.pop(context); 19 | }, 20 | child: Container( 21 | padding: const EdgeInsets.all(10.0), 22 | margin: const EdgeInsets.only(left: 10.0), 23 | child: Container( 24 | margin: const EdgeInsets.only(left: 5.0), 25 | child: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | ), 29 | ), 30 | ), 31 | ), 32 | body: const SizedBox( 33 | width: double.infinity, 34 | child: Column( 35 | mainAxisAlignment: MainAxisAlignment.center, 36 | children: [ 37 | Text( 38 | '404', 39 | style: TextStyle( 40 | fontSize: 60, 41 | fontWeight: FontWeight.bold, 42 | color: Colors.red, 43 | ), 44 | ), 45 | SizedBox(height: 10), 46 | Text( 47 | 'Page not found', 48 | style: TextStyle( 49 | fontSize: 24, 50 | ), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/repositories/personal.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../apis/api.dart'; 4 | import '../providers/user_data_provider.dart'; 5 | import '../utils/helper.dart'; 6 | import '../utils/urls.dart'; 7 | 8 | class PersonalRepository { 9 | String getAccessToken() { 10 | return UserProvider().tokens['access']; 11 | } 12 | 13 | getPersonalDetails(String resumeId) async { 14 | try { 15 | final String accessToken = getAccessToken(); 16 | final String url = '${URLS.kPersonalUrl}$resumeId/details/'; 17 | 18 | final data = await APIService().sendGetRequest(accessToken, url); 19 | return data; 20 | } catch (error) { 21 | return Helper().handleApiError(error); 22 | } 23 | } 24 | 25 | updatePersonalDetails( 26 | String personalId, 27 | Map updatedData, 28 | ) async { 29 | try { 30 | final String accessToken = getAccessToken(); 31 | final String url = '${URLS.kPersonalUrl}$personalId/update/'; 32 | 33 | final data = await APIService().sendPatchRequest( 34 | accessToken, 35 | updatedData, 36 | url, 37 | ); 38 | return data; 39 | } catch (error) { 40 | return Helper().handleApiError(error); 41 | } 42 | } 43 | 44 | updatePersonalImage(String personalId, File imagePath) async { 45 | try { 46 | final String accessToken = getAccessToken(); 47 | final String url = '${URLS.kPersonalUrl}$personalId/update-image/'; 48 | 49 | final data = await APIService().sendPatchRequest( 50 | accessToken, 51 | {'resume_picture': imagePath}, 52 | url, 53 | isMultipart: true, 54 | ); 55 | return data; 56 | } catch (error) { 57 | return Helper().handleApiError(error); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.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: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 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: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 17 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 18 | - platform: android 19 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 20 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 21 | - platform: ios 22 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 23 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 24 | - platform: linux 25 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 26 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 27 | - platform: macos 28 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 29 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 30 | - platform: web 31 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 32 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 33 | - platform: windows 34 | create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 35 | base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /lib/providers/settings_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class SettingsProvider with ChangeNotifier { 5 | // Theme mode and language settings 6 | ThemeMode _themeMode = ThemeMode.system; 7 | String _language = 'en'; 8 | 9 | // Keys for SharedPreferences 10 | static const String themeModeKey = 'theme_mode'; 11 | static const String languageKey = 'language'; 12 | 13 | SettingsProvider() { 14 | _loadSettings(); 15 | } 16 | 17 | // Getters 18 | ThemeMode get themeMode => _themeMode; 19 | String get language => _language; 20 | 21 | // Load settings from SharedPreferences 22 | Future _loadSettings() async { 23 | final prefs = await SharedPreferences.getInstance(); 24 | _themeMode = _getThemeModeFromString(prefs.getString(themeModeKey) ?? 'system'); 25 | _language = prefs.getString(languageKey) ?? 'en'; 26 | notifyListeners(); 27 | } 28 | 29 | // Save theme mode 30 | Future setThemeMode(ThemeMode themeMode) async { 31 | _themeMode = themeMode; 32 | final prefs = await SharedPreferences.getInstance(); 33 | await prefs.setString(themeModeKey, _themeMode.toString().split('.').last); 34 | notifyListeners(); 35 | } 36 | 37 | // Save language 38 | Future setLanguage(String language) async { 39 | _language = language; 40 | final prefs = await SharedPreferences.getInstance(); 41 | await prefs.setString(languageKey, _language); 42 | notifyListeners(); 43 | } 44 | 45 | // Helper to convert string to ThemeMode 46 | ThemeMode _getThemeModeFromString(String themeString) { 47 | switch (themeString) { 48 | case 'light': 49 | return ThemeMode.light; 50 | case 'dark': 51 | return ThemeMode.dark; 52 | default: 53 | return ThemeMode.system; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/models/contact.dart: -------------------------------------------------------------------------------- 1 | class Contact { 2 | int id = 0; 3 | String? phoneNumber; 4 | String email = ''; 5 | String? address; 6 | String? zipCode; 7 | String? facebook; 8 | String? linkedin; 9 | String? github; 10 | String? website; 11 | int? resume; 12 | bool? isActive; 13 | bool? isDeleted; 14 | int? createdBy; 15 | String? createdAt; 16 | int? updatedBy; 17 | String? updatedAt; 18 | 19 | Contact({ 20 | required this.id, 21 | this.phoneNumber, 22 | required this.email, 23 | this.address, 24 | this.zipCode, 25 | this.facebook, 26 | this.linkedin, 27 | this.github, 28 | this.website, 29 | this.resume, 30 | this.isActive, 31 | this.isDeleted, 32 | this.createdBy, 33 | this.createdAt, 34 | this.updatedBy, 35 | this.updatedAt, 36 | }); 37 | 38 | Contact.fromJson(Map json) { 39 | phoneNumber = json['phone_number']; 40 | email = json['email']; 41 | address = json['address']; 42 | zipCode = json['zip_code']; 43 | facebook = json['facebook']; 44 | linkedin = json['linkedin']; 45 | github = json['github']; 46 | website = json['website']; 47 | id = json['id']; 48 | isActive = json['is_active']; 49 | isDeleted = json['is_deleted']; 50 | resume = json['resume']; 51 | createdBy = json['created_by']; 52 | createdAt = json['created_at']; 53 | updatedBy = json['updated_by']; 54 | updatedAt = json['updated_at']; 55 | } 56 | 57 | Map toJson() { 58 | final Map data = {}; 59 | data['phone_number'] = phoneNumber; 60 | data['email'] = email; 61 | data['address'] = address; 62 | data['zip_code'] = zipCode; 63 | data['facebook'] = facebook; 64 | data['linkedin'] = linkedin; 65 | data['github'] = github; 66 | data['website'] = website; 67 | data['resume'] = resume; 68 | return data; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/utils/constants.dart: -------------------------------------------------------------------------------- 1 | class Constants { 2 | static const String appName = 'GoCV'; 3 | static const String appVersion = '1.0.0'; 4 | static const String appUrl = 'https://flutterchat.com'; 5 | static const String appSupportEmail = ''; 6 | static const String appContactNumber = '+1234567890'; 7 | 8 | static const String defaultAvatarPath = 'assets/avatars/rdj.png'; 9 | 10 | static const String splashScreenRouteName = '/'; 11 | static const String loginScreenRouteName = '/login-screen'; 12 | static const String signUpScreenRouteName = '/signup-screen'; 13 | static const String otpScreenRouteName = '/otp-screen'; 14 | static const String forgotPasswordScreenRouteName = '/forgot-password'; 15 | 16 | static const String homeScreenRouteName = '/home_screen'; 17 | static const String resumePreviewScreenRouteName = '/resume-preview'; 18 | static const String profileScreenRouteName = '/profile-screen'; 19 | static const String updateProfileScreenRouteName = '/update-profile'; 20 | static const String settingsScreenRouteName = '/settings'; 21 | static const String accountSettingsScreenRouteName = '/account-settings'; 22 | static const String emailUpdateScreenRouteName = '/email-update'; 23 | static const String notFoundScreenRouteName = '/not-found'; 24 | 25 | static const int httpOkCode = 200; 26 | static const int httpCreatedCode = 201; 27 | static const int httpNoContentCode = 204; 28 | static const int httpBadRequestCode = 400; 29 | static const int httpUnauthorizedCode = 401; 30 | static const int httpForbiddenCode = 403; 31 | static const int httpNotFoundCode = 404; 32 | static const int httpInternalServerErrorCode = 500; 33 | 34 | static const String dataUpdatedMsg = 'Data updated successfully'; 35 | static const String sessionExpiredMsg = 'Session expired, please login again'; 36 | static const String genericErrorMsg = 37 | 'Unable to process your request, please try again later'; 38 | } 39 | -------------------------------------------------------------------------------- /lib/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'applicant.dart'; 2 | 3 | class UserBase { 4 | int? id; 5 | String? email; 6 | bool? isApplicant; 7 | bool? isOrganization; 8 | bool? isVerified; 9 | bool? isStaff; 10 | bool? isSuperuser; 11 | 12 | UserBase({ 13 | this.id, 14 | this.email, 15 | this.isApplicant, 16 | this.isOrganization, 17 | this.isVerified, 18 | this.isStaff, 19 | this.isSuperuser, 20 | }); 21 | 22 | UserBase.fromJson(Map json) { 23 | id = json['id']; 24 | email = json['email']; 25 | isApplicant = json['is_applicant']; 26 | isOrganization = json['is_organization']; 27 | isVerified = json['is_verified']; 28 | isStaff = json['is_staff']; 29 | isSuperuser = json['is_superuser']; 30 | } 31 | 32 | Map toJson() { 33 | final Map data = {}; 34 | data['email'] = email; 35 | data['is_applicant'] = isApplicant; 36 | data['is_organization'] = isOrganization; 37 | data['is_verified'] = isVerified; 38 | data['is_staff'] = isStaff; 39 | data['is_superuser'] = isSuperuser; 40 | return data; 41 | } 42 | } 43 | 44 | class UserProfile { 45 | UserBase? userData; 46 | Applicant? applicantData; 47 | 48 | UserProfile({this.userData, this.applicantData}); 49 | 50 | UserProfile.fromJson(Map json) { 51 | userData = 52 | json['user_data'] != null ? UserBase.fromJson(json['user_data']) : null; 53 | applicantData = json['applicant_data'] != null 54 | ? Applicant.fromJson(json['applicant_data']) 55 | : null; 56 | } 57 | 58 | Map toJson() { 59 | final Map data = {}; 60 | if (userData != null) { 61 | data['user_data'] = userData!.toJson(); 62 | } 63 | if (applicantData != null) { 64 | data['applicant_data'] = applicantData!.toJson(); 65 | } 66 | return data; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | # post_install do |installer| 38 | # installer.pods_project.targets.each do |target| 39 | # flutter_additional_ios_build_settings(target) 40 | # end 41 | # end 42 | 43 | post_install do |installer| 44 | installer.generated_projects.each do |project| 45 | project.targets.each do |target| 46 | target.build_configurations.each do |config| 47 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' 48 | end 49 | end 50 | end 51 | installer.pods_project.targets.each do |target| 52 | flutter_additional_ios_build_settings(target) 53 | end 54 | end 55 | 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to GoCV 2 | 3 | Thank you for considering contributing to the GoCV! We welcome any contributions, bug reports, feature requests, or general feedback to help improve the project. 4 | 5 | To contribute, please follow these guidelines: 6 | 7 | ## Reporting Issues 8 | 9 | If you encounter any bugs or issues with the project, please submit a detailed bug report. Include the following information: 10 | 11 | - Description of the issue 12 | - Steps to reproduce the issue 13 | - Expected behavior 14 | - Any relevant error messages or stack traces 15 | 16 | ## Feature Requests 17 | 18 | If you have ideas for new features or enhancements, please submit a feature request. Provide a clear description of the feature and why it would be valuable to the project. 19 | 20 | ## Pull Requests 21 | 22 | We welcome pull requests for bug fixes, new features, and improvements. Follow these steps to submit a pull request: 23 | 24 | 1. Fork the repository and create a new branch for your changes. 25 | 2. Implement your changes or additions. 26 | 3. Write tests to ensure the stability of your changes. 27 | 4. Ensure that your code follows the project's coding style and conventions. 28 | 5. Document any new functionality or changes in the project documentation. 29 | 6. Submit a pull request, describing the changes you made and the problem they solve. 30 | 7. Be prepared to address any feedback or questions during the review process. 31 | 32 | ## Code of Conduct 33 | 34 | Please note that this project adheres to a [Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project, you are expected to uphold the principles of the Code of Conduct and treat all contributors and users with respect. 35 | 36 | ## License 37 | 38 | By contributing to this project, you agree that your contributions will be licensed under the project's [MIT License](LICENSE). 39 | 40 | ## Contact 41 | 42 | If you have any questions, feedback, or need further assistance, please reach out to the project maintainer at `meherajmahmmd@gmail.com`. We appreciate your interest and support! 43 | -------------------------------------------------------------------------------- /lib/models/resume.dart: -------------------------------------------------------------------------------- 1 | import 'package:gocv/models/user.dart'; 2 | 3 | class Resume { 4 | String name = ''; 5 | int? id; 6 | UserBase? user; 7 | bool? isEducationVisible; 8 | bool? isExperienceVisible; 9 | bool? isSkillVisible; 10 | bool? isLanguageVisible; 11 | bool? isInterestVisible; 12 | bool? isReferenceVisible; 13 | bool? isAwardVisible; 14 | bool? isCertificationVisible; 15 | bool? isActive; 16 | bool? isDeleted; 17 | int? createdBy; 18 | String? createdAt; 19 | int? updatedBy; 20 | String? updatedAt; 21 | 22 | Resume({ 23 | required this.name, 24 | this.id, 25 | this.user, 26 | this.isEducationVisible, 27 | this.isExperienceVisible, 28 | this.isSkillVisible, 29 | this.isLanguageVisible, 30 | this.isInterestVisible, 31 | this.isReferenceVisible, 32 | this.isAwardVisible, 33 | this.isCertificationVisible, 34 | this.isActive, 35 | this.isDeleted, 36 | this.createdBy, 37 | this.createdAt, 38 | this.updatedBy, 39 | this.updatedAt, 40 | }); 41 | 42 | Resume.fromJson(Map json) { 43 | name = json['name']; 44 | id = json['id']; 45 | user = json['user'] != null ? UserBase.fromJson(json['user']) : null; 46 | isEducationVisible = json['is_education_visible']; 47 | isExperienceVisible = json['is_experience_visible']; 48 | isSkillVisible = json['is_skill_visible']; 49 | isLanguageVisible = json['is_language_visible']; 50 | isInterestVisible = json['is_interest_visible']; 51 | isReferenceVisible = json['is_reference_visible']; 52 | isAwardVisible = json['is_award_visible']; 53 | isCertificationVisible = json['is_certification_visible']; 54 | isActive = json['is_active']; 55 | isDeleted = json['is_deleted']; 56 | createdBy = json['created_by']; 57 | createdAt = json['created_at']; 58 | updatedBy = json['updated_by']; 59 | updatedAt = json['updated_at']; 60 | } 61 | 62 | Map toJson() { 63 | final Map data = {}; 64 | data['name'] = name; 65 | return data; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/models/personal.dart: -------------------------------------------------------------------------------- 1 | class Personal { 2 | int? id; 3 | String? firstName; 4 | String? lastName; 5 | String? aboutMe; 6 | String? dateOfBirth; 7 | String? nationality; 8 | String? city; 9 | String? state; 10 | String? country; 11 | int? resume; 12 | String? resumePicture; 13 | bool? isActive; 14 | bool? isDeleted; 15 | int? createdBy; 16 | String? createdAt; 17 | int? updatedBy; 18 | String? updatedAt; 19 | 20 | Personal({ 21 | this.id, 22 | this.firstName, 23 | this.lastName, 24 | this.aboutMe, 25 | this.dateOfBirth, 26 | this.nationality, 27 | this.city, 28 | this.state, 29 | this.country, 30 | this.resume, 31 | this.resumePicture, 32 | this.isActive, 33 | this.isDeleted, 34 | this.createdBy, 35 | this.createdAt, 36 | this.updatedBy, 37 | this.updatedAt, 38 | }); 39 | 40 | Personal.fromJson(Map json) { 41 | firstName = json['first_name']; 42 | lastName = json['last_name']; 43 | aboutMe = json['about_me']; 44 | dateOfBirth = json['date_of_birth']; 45 | nationality = json['nationality']; 46 | city = json['city']; 47 | state = json['state']; 48 | country = json['country']; 49 | id = json['id']; 50 | isActive = json['is_active']; 51 | isDeleted = json['is_deleted']; 52 | resume = json['resume']; 53 | resumePicture = json['resume_picture']; 54 | createdBy = json['created_by']; 55 | createdAt = json['created_at']; 56 | updatedBy = json['updated_by']; 57 | updatedAt = json['updated_at']; 58 | updatedAt = json['updated_at']; 59 | } 60 | 61 | Map toJson() { 62 | final Map data = {}; 63 | data['first_name'] = firstName; 64 | data['last_name'] = lastName; 65 | data['about_me'] = aboutMe; 66 | data['date_of_birth'] = dateOfBirth; 67 | data['nationality'] = nationality; 68 | data['city'] = city; 69 | data['state'] = state; 70 | data['country'] = country; 71 | data['resume'] = resume; 72 | data['resume_picture'] = resumePicture; 73 | return data; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/screens/utility_screens/account_settings_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:gocv/screens/utility_screens/email_update_screen.dart'; 4 | import 'package:gocv/utils/constants.dart'; 5 | 6 | class AccountSettingsScreen extends StatefulWidget { 7 | static const routeName = Constants.accountSettingsScreenRouteName; 8 | 9 | const AccountSettingsScreen({super.key}); 10 | 11 | @override 12 | State createState() => _AccountSettingsScreenState(); 13 | } 14 | 15 | class _AccountSettingsScreenState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: AppBar( 20 | title: Text( 21 | AppLocalizations.of(context)!.update_email_address, 22 | ), 23 | ), 24 | body: ListView( 25 | children: [ 26 | SettingsListItem( 27 | title: AppLocalizations.of(context)!.update_email_address, 28 | icon: Icons.email_outlined, 29 | onTap: () { 30 | Navigator.of(context).pushNamed( 31 | EmailUpdateScreen.routeName, 32 | ); 33 | }, 34 | ), 35 | const Divider(), 36 | ], 37 | )); 38 | } 39 | } 40 | 41 | class SettingsListItem extends StatelessWidget { 42 | final String title; 43 | final IconData icon; 44 | final Function()? onTap; 45 | 46 | const SettingsListItem({ 47 | required this.title, 48 | required this.icon, 49 | this.onTap, 50 | super.key, 51 | }); 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Container( 56 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 57 | child: ListTile( 58 | leading: Icon(icon, size: 30), 59 | title: Text(title), 60 | onTap: onTap, 61 | // onTap: () { 62 | // Navigator.of(context).pushNamed( 63 | // AccountSettingsScreen.routeName, 64 | // ); 65 | // }, 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/repositories/language.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class LanguageRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getLanguages(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kLanguageUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest( 18 | accessToken, 19 | url, 20 | ); 21 | return data; 22 | } catch (error) { 23 | return Helper().handleApiError(error); 24 | } 25 | } 26 | 27 | getLanguageDetails(String languageId) async { 28 | try { 29 | final String accessToken = getAccessToken(); 30 | final String url = '${URLS.kLanguageUrl}$languageId/details/'; 31 | 32 | final data = await APIService().sendGetRequest( 33 | accessToken, 34 | url, 35 | ); 36 | return data; 37 | } catch (error) { 38 | return Helper().handleApiError(error); 39 | } 40 | } 41 | 42 | createLanguage(Map languageData) async { 43 | try { 44 | final String accessToken = getAccessToken(); 45 | const String url = '${URLS.kLanguageUrl}create/'; 46 | 47 | final data = await APIService().sendPostRequest( 48 | accessToken, 49 | languageData, 50 | url, 51 | ); 52 | return data; 53 | } catch (error) { 54 | return Helper().handleApiError(error); 55 | } 56 | } 57 | 58 | updateLanguage(String languageId, Map languageData) async { 59 | try { 60 | final String accessToken = getAccessToken(); 61 | final String url = '${URLS.kLanguageUrl}$languageId/update/'; 62 | 63 | final data = await APIService().sendPatchRequest( 64 | accessToken, 65 | languageData, 66 | url, 67 | ); 68 | return data; 69 | } catch (error) { 70 | return Helper().handleApiError(error); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/screens/utility_screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import '../../models/user.dart'; 5 | import '../../providers/user_data_provider.dart'; 6 | import '../../utils/constants.dart'; 7 | import '../../utils/local_storage.dart'; 8 | import '../auth_screens/login_screen.dart'; 9 | import '../home_screen.dart'; 10 | 11 | class SplashScreen extends StatefulWidget { 12 | static const routeName = Constants.splashScreenRouteName; 13 | 14 | const SplashScreen({Key? key}) : super(key: key); 15 | 16 | @override 17 | State createState() => _SplashScreenState(); 18 | } 19 | 20 | class _SplashScreenState extends State { 21 | LocalStorage localStorage = LocalStorage(); 22 | 23 | void checkUser() async { 24 | final Map tokens = await localStorage.readData('tokens'); 25 | 26 | if (!mounted) return; 27 | final userProvider = Provider.of(context, listen: false); 28 | 29 | if (tokens['access'] == null || tokens['refresh'] == null) { 30 | Future.delayed(const Duration(seconds: 2), () { 31 | Navigator.pushReplacementNamed(context, LoginScreen.routeName); 32 | }); 33 | } else { 34 | // Set tokens in the provider 35 | userProvider.setTokens(tokens); 36 | 37 | // Load user data from local storage 38 | final userDataJson = await localStorage.readData('user'); 39 | 40 | UserBase userData = UserBase.fromJson(userDataJson); 41 | // Set user data in the provider 42 | userProvider.setUserData(userData); 43 | 44 | Future.delayed(const Duration(seconds: 2), () { 45 | Navigator.pushReplacementNamed(context, HomeScreen.routeName); 46 | }); 47 | } 48 | } 49 | 50 | @override 51 | void initState() { 52 | super.initState(); 53 | checkUser(); 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return Scaffold( 59 | body: Center( 60 | child: Text( 61 | Constants.appName, 62 | style: TextStyle( 63 | fontSize: 40, 64 | fontWeight: FontWeight.bold, 65 | color: Theme.of(context).primaryColor, 66 | ), 67 | ), 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/utils/routes_handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/screens/auth_screens/login_screen.dart'; 3 | import 'package:gocv/screens/auth_screens/sign_up_screen.dart'; 4 | import 'package:gocv/screens/home_screen.dart'; 5 | import 'package:gocv/screens/main_screens/resume_details/resume_details_screen.dart'; 6 | import 'package:gocv/screens/main_screens/resume_preview_screen.dart'; 7 | import 'package:gocv/screens/profile_screens/profile_screen.dart'; 8 | import 'package:gocv/screens/profile_screens/update_profile_screen.dart'; 9 | import 'package:gocv/screens/utility_screens/account_settings_screen.dart'; 10 | import 'package:gocv/screens/utility_screens/not_found_screen.dart'; 11 | import 'package:gocv/screens/utility_screens/settings_screen.dart'; 12 | import 'package:gocv/screens/utility_screens/splash_screen.dart'; 13 | 14 | Route generateRoute(RouteSettings settings) { 15 | switch (settings.name) { 16 | case SplashScreen.routeName: 17 | return MaterialPageRoute(builder: (context) => const SplashScreen()); 18 | case LoginScreen.routeName: 19 | return MaterialPageRoute(builder: (context) => const LoginScreen()); 20 | case SignUpScreen.routeName: 21 | return MaterialPageRoute(builder: (context) => const SignUpScreen()); 22 | case HomeScreen.routeName: 23 | return MaterialPageRoute(builder: (context) => const HomeScreen()); 24 | case ResumeDetailsScreen.routeName: 25 | return MaterialPageRoute( 26 | builder: (context) => const ResumeDetailsScreen()); 27 | case ResumePreviewScreen.routeName: 28 | return MaterialPageRoute( 29 | builder: (context) => const ResumePreviewScreen()); 30 | case ProfileScreen.routeName: 31 | return MaterialPageRoute(builder: (context) => const ProfileScreen()); 32 | case UpdateProfileScreen.routeName: 33 | return MaterialPageRoute( 34 | builder: (context) => const UpdateProfileScreen()); 35 | case SettingsScreen.routeName: 36 | return MaterialPageRoute(builder: (context) => const SettingsScreen()); 37 | case AccountSettingsScreen.routeName: 38 | return MaterialPageRoute( 39 | builder: (context) => const AccountSettingsScreen()); 40 | default: 41 | return MaterialPageRoute(builder: (context) => const NotFoundScreen()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Gocv 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | gocv 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSCameraUsageDescription 28 | Camera permission is required for barcode scanning 29 | NSMicrophoneUsageDescription 30 | Post videos to profile 31 | NSPhotoLibraryUsageDescription 32 | Upload images for screen background 33 | UIApplicationSupportsIndirectInputEvents 34 | 35 | UILaunchStoryboardName 36 | LaunchScreen 37 | UIMainStoryboardFile 38 | Main 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | UISupportedInterfaceOrientations~ipad 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | CADisableMinimumFrameDurationOnPhone 55 | 56 | UIApplicationSupportsIndirectInputEvents 57 | 58 | 59 | -------------------------------------------------------------------------------- /lib/repositories/certificate.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class CertificateRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getCertificates(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = 16 | '${URLS.kCertificationUrl}$resumeId/list/?$queryString'; 17 | 18 | final data = await APIService().sendGetRequest( 19 | accessToken, 20 | url, 21 | ); 22 | return data; 23 | } catch (error) { 24 | return Helper().handleApiError(error); 25 | } 26 | } 27 | 28 | getCertificateDetails(String certificateId) async { 29 | try { 30 | final String accessToken = getAccessToken(); 31 | final String url = '${URLS.kCertificationUrl}$certificateId/details/'; 32 | 33 | final data = await APIService().sendGetRequest( 34 | accessToken, 35 | url, 36 | ); 37 | return data; 38 | } catch (error) { 39 | return Helper().handleApiError(error); 40 | } 41 | } 42 | 43 | createCertificate(String resumeId, Map data) async { 44 | try { 45 | final String accessToken = getAccessToken(); 46 | final String url = '${URLS.kCertificationUrl}$resumeId/create/'; 47 | 48 | final response = await APIService().sendPostRequest( 49 | accessToken, 50 | data, 51 | url, 52 | ); 53 | return response; 54 | } catch (error) { 55 | return Helper().handleApiError(error); 56 | } 57 | } 58 | 59 | updateCertificate( 60 | String resumeId, int certificateId, Map data) async { 61 | try { 62 | final String accessToken = getAccessToken(); 63 | final String url = 64 | '${URLS.kCertificationUrl}$resumeId/update/$certificateId/'; 65 | 66 | final response = await APIService().sendPatchRequest( 67 | accessToken, 68 | data, 69 | url, 70 | ); 71 | return response; 72 | } catch (error) { 73 | return Helper().handleApiError(error); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | As contributors and maintainers of the GoCV, we pledge to create a respectful and inclusive environment for everyone involved. We value the participation of all individuals and are committed to providing a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 4 | 5 | ## Expected Behavior 6 | 7 | All contributors, users, and maintainers are expected to: 8 | 9 | - Be respectful and considerate towards others. 10 | - Use welcoming and inclusive language. 11 | - Be open to constructive feedback and discussions. 12 | - Be mindful of the impact of their words and actions on others. 13 | - Show empathy towards others' experiences and backgrounds. 14 | 15 | ## Unacceptable Behavior 16 | 17 | The following behaviors are considered unacceptable: 18 | 19 | - Harassment, discrimination, or offensive comments, whether written or verbal. 20 | - Personal attacks, insults, or trolling. 21 | - Publishing others' private information without explicit permission. 22 | - Any form of unwelcome sexual advances or attention. 23 | - Any other conduct that would be considered inappropriate in a professional setting. 24 | 25 | ## Reporting and Enforcement 26 | 27 | If you experience or witness any behavior that violates this Code of Conduct, please report it immediately by contacting the project maintainer at `meherajmahmmd@gmail.com`. All reports will be handled with utmost confidentiality and will be promptly reviewed and addressed. 28 | 29 | Consequences for violating the Code of Conduct may include: 30 | 31 | - A warning or request for clarification. 32 | - Temporary or permanent expulsion from project participation. 33 | - Reporting the incident to appropriate authorities, where applicable. 34 | 35 | ## Scope 36 | 37 | This Code of Conduct applies to all project spaces, including but not limited to project repositories, issue trackers, project-related events, and community communication channels. 38 | 39 | ## Attribution 40 | 41 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html). 42 | -------------------------------------------------------------------------------- /lib/repositories/skill.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class SkillRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getSkills(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kSkillUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest(accessToken, url); 18 | return data; 19 | } catch (error) { 20 | return Helper().handleApiError(error); 21 | } 22 | } 23 | 24 | getSkillDetails(String skillId) async { 25 | try { 26 | final String accessToken = getAccessToken(); 27 | final String url = '${URLS.kSkillUrl}$skillId/details/'; 28 | 29 | final data = await APIService().sendGetRequest(accessToken, url); 30 | return data; 31 | } catch (error) { 32 | return Helper().handleApiError(error); 33 | } 34 | } 35 | 36 | createSkill(Map skillData) async { 37 | try { 38 | final String accessToken = getAccessToken(); 39 | const String url = '${URLS.kSkillUrl}create/'; 40 | 41 | final data = await APIService().sendPostRequest( 42 | accessToken, 43 | skillData, 44 | url, 45 | ); 46 | return data; 47 | } catch (error) { 48 | return Helper().handleApiError(error); 49 | } 50 | } 51 | 52 | updateSkill(String skillId, Map skillData) async { 53 | try { 54 | final String accessToken = getAccessToken(); 55 | final String url = '${URLS.kSkillUrl}$skillId/update/'; 56 | 57 | final data = await APIService().sendPatchRequest( 58 | accessToken, 59 | skillData, 60 | url, 61 | ); 62 | return data; 63 | } catch (error) { 64 | return Helper().handleApiError(error); 65 | } 66 | } 67 | 68 | deleteSkill(String skillId) async { 69 | try { 70 | final String accessToken = getAccessToken(); 71 | final String url = '${URLS.kSkillUrl}$skillId/destroy/'; 72 | 73 | final data = await APIService().sendDeleteRequest(accessToken, url); 74 | return data; 75 | } catch (error) { 76 | return Helper().handleApiError(error); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /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.meheraj.gocv" 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-gradle-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 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: must_be_immutable 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class CustomButton extends StatelessWidget { 6 | final String text; 7 | IconData? icon; 8 | double? height; 9 | double? width; 10 | double? textFontSize; 11 | bool? isLoading = false; 12 | bool? isDisabled = false; 13 | final Function() onPressed; 14 | 15 | CustomButton({ 16 | Key? key, 17 | required this.text, 18 | this.icon, 19 | this.height, 20 | this.width, 21 | this.textFontSize, 22 | this.isLoading, 23 | this.isDisabled, 24 | required this.onPressed, 25 | }) : super(key: key); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return SizedBox( 30 | width: width ?? double.infinity, 31 | height: height ?? 55, 32 | child: ElevatedButton( 33 | onPressed: () => isDisabled != null && isDisabled == true 34 | ? null 35 | : isLoading != null && isLoading == true 36 | ? null 37 | : onPressed(), 38 | style: ElevatedButton.styleFrom( 39 | // backgroundColor: isDisabled == true || isLoading == true 40 | // ? Colors.grey 41 | // : Theme.of(context).elevatedButtonTheme.style.backgroundColor, 42 | shape: RoundedRectangleBorder( 43 | borderRadius: BorderRadius.circular(10), 44 | ), 45 | ), 46 | child: isLoading != null && isLoading == true 47 | ? const SizedBox( 48 | height: 20, 49 | width: 20, 50 | child: CircularProgressIndicator(color: Colors.white), 51 | ) 52 | : SizedBox( 53 | child: Row( 54 | mainAxisAlignment: MainAxisAlignment.center, 55 | children: [ 56 | if (icon != null) 57 | Icon( 58 | icon, 59 | color: Colors.white, 60 | size: 20, 61 | ), 62 | SizedBox(width: icon != null ? 10 : 0), 63 | Text( 64 | text, 65 | style: TextStyle( 66 | fontSize: textFontSize ?? 20, 67 | // fontWeight: FontWeight.bold, 68 | color: Colors.white, 69 | ), 70 | ), 71 | ], 72 | ), 73 | ), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/repositories/reference.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class ReferenceRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getReferences(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kReferenceUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest(accessToken, url); 18 | return data; 19 | } catch (error) { 20 | return Helper().handleApiError(error); 21 | } 22 | } 23 | 24 | getReferenceDetails(String referenceId) async { 25 | try { 26 | final String accessToken = getAccessToken(); 27 | final String url = '${URLS.kReferenceUrl}$referenceId/details/'; 28 | 29 | final data = await APIService().sendGetRequest(accessToken, url); 30 | return data; 31 | } catch (error) { 32 | return Helper().handleApiError(error); 33 | } 34 | } 35 | 36 | createReference(Map referenceData) async { 37 | try { 38 | final String accessToken = getAccessToken(); 39 | const String url = '${URLS.kReferenceUrl}create/'; 40 | 41 | final data = await APIService().sendPostRequest( 42 | accessToken, 43 | referenceData, 44 | url, 45 | ); 46 | return data; 47 | } catch (error) { 48 | return Helper().handleApiError(error); 49 | } 50 | } 51 | 52 | updateReference( 53 | String referenceId, 54 | Map referenceData, 55 | ) async { 56 | try { 57 | final String accessToken = getAccessToken(); 58 | final String url = '${URLS.kReferenceUrl}$referenceId/update/'; 59 | 60 | final data = await APIService().sendPatchRequest( 61 | accessToken, 62 | referenceData, 63 | url, 64 | ); 65 | return data; 66 | } catch (error) { 67 | return Helper().handleApiError(error); 68 | } 69 | } 70 | 71 | deleteReference(String referenceId) async { 72 | try { 73 | final String accessToken = getAccessToken(); 74 | final String url = '${URLS.kReferenceUrl}$referenceId/destroy/'; 75 | 76 | final data = await APIService().sendDeleteRequest(accessToken, url); 77 | return data; 78 | } catch (error) { 79 | return Helper().handleApiError(error); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:flutter_localizations/flutter_localizations.dart'; 4 | import 'package:gocv/providers/settings_provider.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | import 'providers/contact_data_provider.dart'; 8 | import 'providers/current_resume_provider.dart'; 9 | import 'providers/experience_list_provider.dart'; 10 | import 'providers/personal_data_provider.dart'; 11 | import 'providers/resume_list_provider.dart'; 12 | import 'providers/user_data_provider.dart'; 13 | import 'providers/user_profile_provider.dart'; 14 | import 'screens/utility_screens/splash_screen.dart'; 15 | import 'utils/constants.dart'; 16 | import 'utils/routes_handler.dart'; 17 | import 'utils/theme.dart'; 18 | 19 | void main() { 20 | runApp( 21 | MultiProvider( 22 | providers: [ 23 | ChangeNotifierProvider(create: (context) => SettingsProvider()), 24 | ChangeNotifierProvider(create: (context) => UserProvider()), 25 | ChangeNotifierProvider(create: (context) => UserProfileProvider()), 26 | ChangeNotifierProvider(create: (context) => ResumeListProvider()), 27 | ChangeNotifierProvider(create: (context) => CurrentResumeProvider()), 28 | ChangeNotifierProvider(create: (context) => PersonalDataProvider()), 29 | ChangeNotifierProvider(create: (context) => ContactDataProvider()), 30 | ChangeNotifierProvider(create: (context) => ExperienceListProvider()), 31 | ], 32 | child: const App(), 33 | ), 34 | ); 35 | } 36 | 37 | class App extends StatelessWidget { 38 | const App({super.key}); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | final settingsProvider = Provider.of(context); 43 | 44 | return MaterialApp( 45 | debugShowCheckedModeBanner: false, 46 | title: Constants.appName, 47 | theme: createLightTheme(), 48 | darkTheme: createDarkTheme(), 49 | themeMode: settingsProvider.themeMode, 50 | locale: Locale(settingsProvider.language), 51 | // Use the current language 52 | supportedLocales: const [ 53 | Locale('en'), // English 54 | Locale('bn'), // Bengali 55 | ], 56 | localizationsDelegates: const [ 57 | AppLocalizations.delegate, 58 | GlobalMaterialLocalizations.delegate, 59 | GlobalWidgetsLocalizations.delegate, 60 | GlobalCupertinoLocalizations.delegate, 61 | ], 62 | onGenerateRoute: generateRoute, 63 | home: const SplashScreen(), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/l10n/app_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "appTitle": "GoCV", 3 | "settings": "Settings", 4 | "english": "English", 5 | "bengali": "বাংলা", 6 | "loading": "Loading...", 7 | "darkMode": "Dark Mode", 8 | "language": "Language", 9 | "privacy": "Privacy", 10 | "aboutUs": "About Us", 11 | "account": "Account", 12 | "contact_us": "Contact Us", 13 | "licenses": "Licenses", 14 | "logout": "Logout", 15 | "new_resume": "New Resume", 16 | "my_resumes": "My Resumes", 17 | "resume_preview": "Resume Preview", 18 | "update_resume_title": "Update Resume Title", 19 | "delete_resume": "Delete Resume", 20 | "create_new_resume": "Create a New Resume", 21 | "resume_title": "Resume Title", 22 | "cancel": "Cancel", 23 | "create": "Create", 24 | "update": "Update", 25 | "delete": "Delete", 26 | "home": "Home", 27 | "profile": "Profile", 28 | "update_profile": "Update Profile", 29 | "account_settings": "Account Settings", 30 | "update_email_address": "Update Email Address", 31 | "not_found": "Not Found", 32 | "page_not_found": "Page Not Found", 33 | "personal": "Personal", 34 | "first_name": "First Name", 35 | "last_name": "Last Name", 36 | "about_me": "About Me", 37 | "date_of_birth": "Date of Birth", 38 | "city": "City", 39 | "state": "State", 40 | "country": "Country", 41 | "nationality": "Nationality", 42 | "contact": "Contact", 43 | "phone_number": "Phone Number", 44 | "email": "Email", 45 | "address": "Address", 46 | "linkedin": "LinkedIn", 47 | "facebook": "Facebook", 48 | "github": "GitHub", 49 | "work_experience": "Work Experience", 50 | "hide": "Hide", 51 | "show": "Show", 52 | "create_new_experience": "Create New Experience", 53 | "company_name": "Company Name", 54 | "position": "Position", 55 | "job_type": "Job Type", 56 | "full_time": "Full Time", 57 | "part_time": "Part Time", 58 | "internship": "Internship", 59 | "contractual": "Contractual", 60 | "freelance": "Freelance", 61 | "volunteer": "Volunteer", 62 | "apprenticeship": "Apprenticeship", 63 | "start_date": "Start Date", 64 | "currently_working": "Currently Working", 65 | "end_date": "End Date", 66 | "description": "Description", 67 | "company_website": "Company Website", 68 | "add_work_experience": "Add Work Experience", 69 | "education": "Education", 70 | "skills": "Skills", 71 | "awards": "Awards", 72 | "certifications": "Certifications", 73 | "interests": "Interests", 74 | "references": "References" 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /lib/repositories/award.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class AwardRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getAwards(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kAwardUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest( 18 | accessToken, 19 | url, 20 | ); 21 | return data; 22 | } catch (error) { 23 | return Helper().handleApiError(error); 24 | } 25 | } 26 | 27 | getAwardDetails(String awardId) async { 28 | try { 29 | final String accessToken = getAccessToken(); 30 | final String url = '${URLS.kAwardUrl}$awardId/details/'; 31 | 32 | final data = await APIService().sendGetRequest( 33 | accessToken, 34 | url, 35 | ); 36 | return data; 37 | } catch (error) { 38 | return Helper().handleApiError(error); 39 | } 40 | } 41 | 42 | createAward(String awardId, Map data) async { 43 | try { 44 | final String accessToken = getAccessToken(); 45 | final String url = '${URLS.kAwardUrl}$awardId/create/'; 46 | 47 | final response = await APIService().sendPostRequest( 48 | accessToken, 49 | data, 50 | url, 51 | ); 52 | return response; 53 | } catch (error) { 54 | return Helper().handleApiError(error); 55 | } 56 | } 57 | 58 | updateAward(String awardId, Map data) async { 59 | try { 60 | final String accessToken = getAccessToken(); 61 | final String url = '${URLS.kAwardUrl}$awardId/update/'; 62 | 63 | final response = await APIService().sendPatchRequest( 64 | accessToken, 65 | data, 66 | url, 67 | ); 68 | return response; 69 | } catch (error) { 70 | return Helper().handleApiError(error); 71 | } 72 | } 73 | 74 | deleteAward(String awardId) async { 75 | try { 76 | final String accessToken = getAccessToken(); 77 | final String url = '${URLS.kAwardUrl}$awardId/destroy/'; 78 | 79 | final response = await APIService().sendDeleteRequest( 80 | accessToken, 81 | url, 82 | ); 83 | return response; 84 | } catch (error) { 85 | return Helper().handleApiError(error); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/l10n/app_bn.arb: -------------------------------------------------------------------------------- 1 | { 2 | "appTitle": "GoCV", 3 | "settings": "সেটিংস", 4 | "english": "English", 5 | "bengali": "বাংলা", 6 | "loading": "লোড হচ্ছে...", 7 | "darkMode": "ডার্ক মোড", 8 | "language": "ভাষা", 9 | "privacy": "গোপনীয়তা", 10 | "aboutUs": "আমাদের সম্পর্কে", 11 | "account": "অ্যাকাউন্ট", 12 | "contact_us": "যোগাযোগ করুন", 13 | "licenses": "লাইসেন্স", 14 | "logout": "লগ আউট", 15 | "new_resume": "নতুন রেজিউম", 16 | "my_resumes": "আমার রেজিউম", 17 | "resume_preview": "রেজিউম প্রিভিউ", 18 | "update_resume_title": "রেজিউম শিরোনাম আপডেট করুন", 19 | "delete_resume": "রেজিউম মুছে ফেলুন", 20 | "create_new_resume": "নতুন রেজিউম তৈরি করুন", 21 | "resume_title": "রেজিউম শিরোনাম", 22 | "cancel": "বাতিল", 23 | "create": "তৈরি করুন", 24 | "update": "আপডেট করুন", 25 | "delete": "মুছে ফেলুন", 26 | "home": "হোম", 27 | "profile": "প্রোফাইল", 28 | "update_profile": "প্রোফাইল আপডেট করুন", 29 | "account_settings": "অ্যাকাউন্ট সেটিংস", 30 | "update_email_address": "ইমেল আপডেট করুন", 31 | "not_found": "পাওয়া যায়নি", 32 | "page_not_found": "পেজ পাওয়া যায়নি", 33 | "personal": "ব্যক্তিগত", 34 | "first_name": "প্রথম নাম", 35 | "last_name": "শেষ নাম", 36 | "about_me": "আমার সম্পর্কে", 37 | "date_of_birth": "জন্ম তারিখ", 38 | "city": "শহর", 39 | "state": "রাজ্য/জেলা", 40 | "country": "দেশ", 41 | "nationality": "জাতীয়তা", 42 | "contact": "যোগাযোগ", 43 | "phone_number": "ফোন নম্বর", 44 | "email": "ইমেল", 45 | "address": "ঠিকানা", 46 | "linkedin": "লিংকডইন", 47 | "facebook": "ফেসবুক", 48 | "github": "গিটহাব", 49 | "work_experience": "কর্ম অভিজ্ঞতা", 50 | "hide": "লুকান", 51 | "show": "দেখান", 52 | "create_new_experience": "নতুন অভিজ্ঞতা তৈরি করুন", 53 | "company_name": "কোম্পানির নাম", 54 | "position": "অবস্থান", 55 | "job_type": "কাজের ধরণ", 56 | "full_time": "ফুল টাইম", 57 | "part_time": "পার্ট টাইম", 58 | "internship": "ইন্টার্নশিপ", 59 | "contractual": "চুক্তিভিত্তিক", 60 | "freelance": "ফ্রিল্যান্স", 61 | "volunteer": "স্বেচ্ছাসেবক", 62 | "apprenticeship": "শিক্ষানবিশ", 63 | "start_date": "শুরুর তারিখ", 64 | "currently_working": "বর্তমানে কাজ করছেন", 65 | "end_date": "শেষ তারিখ", 66 | "description": "বর্ণনা", 67 | "company_website": "কোম্পানির ওয়েবসাইট", 68 | "add_work_experience": "কর্ম অভিজ্ঞতা যোগ করুন", 69 | "education": "শিক্ষা", 70 | "skills": "দক্ষতা", 71 | "awards": "পুরস্কার", 72 | "certifications": "সার্টিফিকেশন", 73 | "interests": "আগ্রহ", 74 | "references": "রেফারেন্স" 75 | } 76 | 77 | -------------------------------------------------------------------------------- /lib/repositories/interest.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class InterestRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getInterests(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kInterestUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest( 18 | accessToken, 19 | url, 20 | ); 21 | return data; 22 | } catch (error) { 23 | return Helper().handleApiError(error); 24 | } 25 | } 26 | 27 | getInterestDetails(String resumeId) async { 28 | try { 29 | final String accessToken = getAccessToken(); 30 | final String url = '${URLS.kInterestUrl}$resumeId/details/'; 31 | 32 | final data = await APIService().sendGetRequest( 33 | accessToken, 34 | url, 35 | ); 36 | return data; 37 | } catch (error) { 38 | return Helper().handleApiError(error); 39 | } 40 | } 41 | 42 | createInterest(String resumeId, Map interestData) async { 43 | try { 44 | final String accessToken = getAccessToken(); 45 | final String url = '${URLS.kInterestUrl}$resumeId/create/'; 46 | 47 | final data = await APIService().sendPostRequest( 48 | accessToken, 49 | interestData, 50 | url, 51 | ); 52 | return data; 53 | } catch (error) { 54 | return Helper().handleApiError(error); 55 | } 56 | } 57 | 58 | updateInterest(String resumeId, String interestId, 59 | Map interestData) async { 60 | try { 61 | final String accessToken = getAccessToken(); 62 | final String url = '${URLS.kInterestUrl}$resumeId/$interestId/update/'; 63 | 64 | final data = await APIService().sendPatchRequest( 65 | accessToken, 66 | interestData, 67 | url, 68 | ); 69 | return data; 70 | } catch (error) { 71 | return Helper().handleApiError(error); 72 | } 73 | } 74 | 75 | deleteInterest(String resumeId, String interestId) async { 76 | try { 77 | final String accessToken = getAccessToken(); 78 | final String url = '${URLS.kInterestUrl}$resumeId/$interestId/destroy/'; 79 | 80 | final data = await APIService().sendDeleteRequest( 81 | accessToken, 82 | url, 83 | ); 84 | return data; 85 | } catch (error) { 86 | return Helper().handleApiError(error); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/repositories/experience.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class ExperienceRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getExperiences(String resumeId, Map params) async { 12 | try { 13 | final String accessToken = getAccessToken(); 14 | String queryString = Uri(queryParameters: params).query; 15 | final String url = '${URLS.kExperienceUrl}$resumeId/list/?$queryString'; 16 | 17 | final data = await APIService().sendGetRequest( 18 | accessToken, 19 | url, 20 | ); 21 | return data; 22 | } catch (error) { 23 | return Helper().handleApiError(error); 24 | } 25 | } 26 | 27 | getExperienceDetails(String resumeId) async { 28 | try { 29 | final String accessToken = getAccessToken(); 30 | final String url = '${URLS.kExperienceUrl}$resumeId/details/'; 31 | 32 | final data = await APIService().sendGetRequest( 33 | accessToken, 34 | url, 35 | ); 36 | return data; 37 | } catch (error) { 38 | return Helper().handleApiError(error); 39 | } 40 | } 41 | 42 | createExperience( 43 | String resumeId, 44 | Map experienceData, 45 | ) async { 46 | try { 47 | final String accessToken = getAccessToken(); 48 | const String url = '${URLS.kExperienceUrl}create/'; 49 | 50 | final data = await APIService().sendPostRequest( 51 | accessToken, 52 | experienceData, 53 | url, 54 | ); 55 | return data; 56 | } catch (error) { 57 | return Helper().handleApiError(error); 58 | } 59 | } 60 | 61 | updateExperience( 62 | String experienceId, Map experienceData) async { 63 | try { 64 | final String accessToken = getAccessToken(); 65 | final String url = '${URLS.kExperienceUrl}$experienceId/update/'; 66 | 67 | final data = await APIService().sendPatchRequest( 68 | accessToken, 69 | experienceData, 70 | url, 71 | ); 72 | return data; 73 | } catch (error) { 74 | return Helper().handleApiError(error); 75 | } 76 | } 77 | 78 | deleteExperience(String experienceId) async { 79 | try { 80 | final String accessToken = getAccessToken(); 81 | final String url = '${URLS.kExperienceUrl}$experienceId/destroy/'; 82 | 83 | final data = await APIService().sendDeleteRequest( 84 | accessToken, 85 | url, 86 | ); 87 | return data; 88 | } catch (error) { 89 | return Helper().handleApiError(error); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/widgets/custom_text_form_field.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: must_be_immutable 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class CustomTextFormField extends StatefulWidget { 6 | final double width; 7 | bool? autofocus; 8 | final TextEditingController controller; 9 | final String labelText; 10 | final String hintText; 11 | final IconData prefixIcon; 12 | IconData? suffixIcon; 13 | final TextCapitalization textCapitalization; 14 | final double borderRadius; 15 | final TextInputType keyboardType; 16 | bool? isObscure; 17 | Function()? suffixIconOnPressed; 18 | final Function onChanged; 19 | String? Function(String?)? validator; 20 | 21 | CustomTextFormField({ 22 | Key? key, 23 | required this.width, 24 | this.autofocus, 25 | required this.controller, 26 | required this.labelText, 27 | required this.hintText, 28 | required this.prefixIcon, 29 | this.suffixIcon, 30 | required this.textCapitalization, 31 | required this.borderRadius, 32 | required this.keyboardType, 33 | this.isObscure, 34 | this.suffixIconOnPressed, 35 | required this.onChanged, 36 | this.validator, 37 | }) : super(key: key); 38 | 39 | @override 40 | State createState() => _CustomTextFormFieldState(); 41 | } 42 | 43 | class _CustomTextFormFieldState extends State { 44 | @override 45 | Widget build(BuildContext context) { 46 | return Container( 47 | margin: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0), 48 | width: widget.width, 49 | child: TextFormField( 50 | maxLines: widget.isObscure ?? false ? 1 : null, 51 | autofocus: widget.autofocus ?? false, 52 | textCapitalization: widget.textCapitalization, 53 | controller: widget.controller, 54 | decoration: InputDecoration( 55 | prefixIcon: Icon(widget.prefixIcon), 56 | suffixIcon: widget.suffixIcon != null 57 | ? IconButton( 58 | onPressed: () { 59 | widget.suffixIconOnPressed!(); 60 | }, 61 | icon: Icon(widget.suffixIcon)) 62 | : const SizedBox.shrink(), 63 | labelText: widget.labelText, 64 | hintText: widget.hintText, 65 | border: OutlineInputBorder( 66 | borderRadius: BorderRadius.circular(widget.borderRadius), 67 | ), 68 | focusedBorder: OutlineInputBorder( 69 | borderRadius: BorderRadius.circular(widget.borderRadius), 70 | borderSide: BorderSide( 71 | color: Theme.of(context).primaryColor, 72 | ), 73 | ), 74 | ), 75 | keyboardType: widget.keyboardType, 76 | obscureText: widget.isObscure ?? false, 77 | onChanged: (value) { 78 | widget.onChanged(value); 79 | }, 80 | validator: widget.validator, 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_keyboard_visibility (0.0.1): 4 | - Flutter 5 | - image_cropper (0.0.4): 6 | - Flutter 7 | - TOCropViewController (~> 2.6.1) 8 | - image_picker_ios (0.0.1): 9 | - Flutter 10 | - path_provider_foundation (0.0.1): 11 | - Flutter 12 | - FlutterMacOS 13 | - permission_handler_apple (9.3.0): 14 | - Flutter 15 | - pointer_interceptor_ios (0.0.1): 16 | - Flutter 17 | - printing (1.0.0): 18 | - Flutter 19 | - shared_preferences_foundation (0.0.1): 20 | - Flutter 21 | - FlutterMacOS 22 | - TOCropViewController (2.6.1) 23 | - url_launcher_ios (0.0.1): 24 | - Flutter 25 | 26 | DEPENDENCIES: 27 | - Flutter (from `Flutter`) 28 | - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) 29 | - image_cropper (from `.symlinks/plugins/image_cropper/ios`) 30 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) 31 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 32 | - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) 33 | - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) 34 | - printing (from `.symlinks/plugins/printing/ios`) 35 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) 36 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 37 | 38 | SPEC REPOS: 39 | trunk: 40 | - TOCropViewController 41 | 42 | EXTERNAL SOURCES: 43 | Flutter: 44 | :path: Flutter 45 | flutter_keyboard_visibility: 46 | :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" 47 | image_cropper: 48 | :path: ".symlinks/plugins/image_cropper/ios" 49 | image_picker_ios: 50 | :path: ".symlinks/plugins/image_picker_ios/ios" 51 | path_provider_foundation: 52 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 53 | permission_handler_apple: 54 | :path: ".symlinks/plugins/permission_handler_apple/ios" 55 | pointer_interceptor_ios: 56 | :path: ".symlinks/plugins/pointer_interceptor_ios/ios" 57 | printing: 58 | :path: ".symlinks/plugins/printing/ios" 59 | shared_preferences_foundation: 60 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" 61 | url_launcher_ios: 62 | :path: ".symlinks/plugins/url_launcher_ios/ios" 63 | 64 | SPEC CHECKSUMS: 65 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 66 | flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 67 | image_cropper: a3291c624a953049bc6a02e1f8c8ceb162a24b25 68 | image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 69 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 70 | permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 71 | pointer_interceptor_ios: 508241697ff0947f853c061945a8b822463947c1 72 | printing: 233e1b73bd1f4a05615548e9b5a324c98588640b 73 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 74 | TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 75 | url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe 76 | 77 | PODFILE CHECKSUM: e8eb2a0bcbd97b7946f310ad41c79a4f0017e1fb 78 | 79 | COCOAPODS: 1.15.2 80 | -------------------------------------------------------------------------------- /lib/repositories/education.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class EducationRepository { 7 | // Method to get the access token from UserProvider 8 | String getAccessToken() { 9 | return UserProvider().tokens['access']; 10 | } 11 | 12 | getEducations(String resumeId, Map params) async { 13 | try { 14 | final String accessToken = getAccessToken(); 15 | String queryString = Uri(queryParameters: params).query; 16 | final String url = '${URLS.kEducationUrl}$resumeId/list/?$queryString'; 17 | 18 | final data = await APIService().sendGetRequest(accessToken, url); 19 | return data; 20 | } catch (error) { 21 | return Helper().handleApiError(error); 22 | } 23 | } 24 | 25 | getEducationDetails(String educationId) async { 26 | try { 27 | final String accessToken = getAccessToken(); 28 | final String url = '${URLS.kEducationUrl}$educationId/details/'; 29 | 30 | final data = await APIService().sendGetRequest(accessToken, url); 31 | return data; 32 | } catch (error) { 33 | return Helper().handleApiError(error); 34 | } 35 | } 36 | 37 | createEducation(Map educationData) async { 38 | try { 39 | final String accessToken = getAccessToken(); 40 | const String url = '${URLS.kEducationUrl}create/'; 41 | 42 | final data = await APIService().sendPostRequest( 43 | accessToken, 44 | educationData, 45 | url, 46 | ); 47 | return data; 48 | } catch (error) { 49 | return Helper().handleApiError(error); 50 | } 51 | } 52 | 53 | updateEducation(String educationId, Map updatedData) async { 54 | try { 55 | final String accessToken = getAccessToken(); 56 | final String url = '${URLS.kEducationUrl}$educationId/update/'; 57 | 58 | final data = await APIService().sendPatchRequest( 59 | accessToken, 60 | updatedData, 61 | url, 62 | ); 63 | return data; 64 | } catch (error) { 65 | return Helper().handleApiError(error); 66 | } 67 | } 68 | 69 | updateSerial(String educationId, String newSerial) async { 70 | try { 71 | final String accessToken = getAccessToken(); 72 | final String url = '${URLS.kEducationUrl}$educationId/update-serial/'; 73 | 74 | final data = await APIService().sendPatchRequest( 75 | accessToken, 76 | { 77 | 'new_serial': newSerial, 78 | }, 79 | url, 80 | ); 81 | return data; 82 | } catch (error) { 83 | return Helper().handleApiError(error); 84 | } 85 | } 86 | 87 | deleteEducation(String educationId) async { 88 | try { 89 | final String accessToken = getAccessToken(); 90 | final String url = '${URLS.kEducationUrl}$educationId/destroy/'; 91 | 92 | final data = await APIService().sendDeleteRequest( 93 | accessToken, 94 | url, 95 | ); 96 | return data; 97 | } catch (error) { 98 | return Helper().handleApiError(error); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/utils/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | ThemeData createLightTheme() { 6 | return ThemeData( 7 | brightness: Brightness.light, 8 | useMaterial3: false, 9 | primarySwatch: createMaterialColor(const Color(0xFF532AD6)), 10 | primaryColor: createMaterialColor(const Color(0xFF532AD6)), 11 | appBarTheme: const AppBarTheme( 12 | centerTitle: true, 13 | backgroundColor: Color(0xFF532AD6), 14 | foregroundColor: Colors.white, 15 | elevation: 0, 16 | titleTextStyle: TextStyle(fontSize: 18.0), 17 | ), 18 | scaffoldBackgroundColor: Colors.white, 19 | inputDecorationTheme: InputDecorationTheme( 20 | prefixIconColor: Colors.grey, 21 | suffixIconColor: createMaterialColor(const Color(0xFF532AD6)), 22 | border: const OutlineInputBorder( 23 | borderRadius: BorderRadius.all(Radius.circular(10.0)), 24 | ), 25 | focusedBorder: OutlineInputBorder( 26 | borderSide: BorderSide( 27 | color: createMaterialColor(const Color(0xFF532AD6)), 28 | width: 2.0, 29 | ), 30 | borderRadius: const BorderRadius.all(Radius.circular(10.0)), 31 | ), 32 | ), 33 | textButtonTheme: TextButtonThemeData( 34 | style: TextButton.styleFrom( 35 | foregroundColor: createMaterialColor(const Color(0xFF100720)), 36 | ), 37 | ), 38 | elevatedButtonTheme: ElevatedButtonThemeData( 39 | style: ElevatedButton.styleFrom( 40 | backgroundColor: createMaterialColor(const Color(0xFF532AD6)), 41 | shape: RoundedRectangleBorder( 42 | borderRadius: BorderRadius.circular(10), 43 | ), 44 | ), 45 | ), 46 | ); 47 | } 48 | 49 | ThemeData createDarkTheme() { 50 | return ThemeData( 51 | brightness: Brightness.dark, 52 | useMaterial3: false, 53 | primarySwatch: createMaterialColor(const Color(0xFF532AD6)), 54 | primaryColor: createMaterialColor(const Color(0xFF532AD6)), 55 | appBarTheme: const AppBarTheme( 56 | centerTitle: true, 57 | backgroundColor: Color(0xFF1E1E1E), 58 | foregroundColor: Colors.white, 59 | elevation: 0, 60 | titleTextStyle: TextStyle(fontSize: 18.0), 61 | ), 62 | scaffoldBackgroundColor: const Color(0xFF121212), 63 | inputDecorationTheme: InputDecorationTheme( 64 | prefixIconColor: Colors.grey[400], 65 | suffixIconColor: createMaterialColor(const Color(0xFF532AD6)), 66 | border: const OutlineInputBorder( 67 | borderRadius: BorderRadius.all(Radius.circular(10.0)), 68 | ), 69 | focusedBorder: OutlineInputBorder( 70 | borderSide: BorderSide( 71 | color: createMaterialColor(const Color(0xFF532AD6)), 72 | width: 2.0, 73 | ), 74 | borderRadius: const BorderRadius.all(Radius.circular(10.0)), 75 | ), 76 | ), 77 | textButtonTheme: TextButtonThemeData( 78 | style: TextButton.styleFrom( 79 | foregroundColor: Colors.white, 80 | ), 81 | ), 82 | elevatedButtonTheme: ElevatedButtonThemeData( 83 | style: ElevatedButton.styleFrom( 84 | backgroundColor: createMaterialColor(const Color(0xFF532AD6)), 85 | shape: RoundedRectangleBorder( 86 | borderRadius: BorderRadius.circular(10), 87 | ), 88 | ), 89 | ), 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /lib/repositories/auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | 5 | import '../utils/constants.dart'; 6 | import '../utils/urls.dart'; 7 | 8 | class AuthRepository { 9 | Future> registerUser( 10 | String firstName, 11 | String lastName, 12 | String email, 13 | String password, 14 | ) async { 15 | try { 16 | final response = await http.post( 17 | Uri.parse(URLS.kRegisterUrl), 18 | headers: { 19 | 'Content-Type': 'application/json', 20 | }, 21 | body: jsonEncode({ 22 | 'first_name': firstName, 23 | 'last_name': lastName, 24 | 'email': email, 25 | 'password': password, 26 | 'password2': password, 27 | 'is_applicant': true, 28 | 'is_organization': false, 29 | }), 30 | ); 31 | if (response.statusCode == Constants.httpOkCode) { 32 | final data = jsonDecode(response.body); 33 | return { 34 | 'data': data, 35 | 'status': response.statusCode, 36 | }; 37 | } else { 38 | final data = jsonDecode(response.body); 39 | print(data); 40 | return { 41 | 'error': data['detail'], 42 | 'status': response.statusCode, 43 | }; 44 | } 45 | } catch (e) { 46 | print(e.toString()); 47 | return { 48 | 'error': e.toString(), 49 | 'status': 500, 50 | }; 51 | } 52 | } 53 | 54 | Future> loginUser(String email, String password) async { 55 | try { 56 | final response = await http.post( 57 | Uri.parse(URLS.kLoginUrl), 58 | body: { 59 | 'email': email, 60 | 'password': password, 61 | }, 62 | ); 63 | if (response.statusCode == Constants.httpOkCode) { 64 | final data = jsonDecode(response.body); 65 | return { 66 | 'data': data, 67 | 'status': response.statusCode, 68 | }; 69 | } else { 70 | final data = jsonDecode(response.body); 71 | return { 72 | 'error': data['detail'], 73 | 'status': response.statusCode, 74 | }; 75 | } 76 | } catch (e) { 77 | print(e.toString()); 78 | return { 79 | 'error': e.toString(), 80 | 'status': 500, 81 | }; 82 | } 83 | } 84 | 85 | Future> refreshToken(String refreshToken) async { 86 | try { 87 | final response = await http.post( 88 | Uri.parse(URLS.kRefreshTokenUrl), 89 | body: { 90 | 'refresh': refreshToken, 91 | }, 92 | ); 93 | if (response.statusCode == Constants.httpOkCode) { 94 | final data = jsonDecode(response.body); 95 | return { 96 | 'data': data, 97 | 'status': response.statusCode, 98 | }; 99 | } else { 100 | final data = jsonDecode(response.body); 101 | return { 102 | 'error': data['detail'], 103 | 'status': response.statusCode, 104 | }; 105 | } 106 | } catch (e) { 107 | print(e.toString()); 108 | return { 109 | 'error': e.toString(), 110 | 'status': 500, 111 | }; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/repositories/resume.dart: -------------------------------------------------------------------------------- 1 | import '../apis/api.dart'; 2 | import '../providers/user_data_provider.dart'; 3 | import '../utils/helper.dart'; 4 | import '../utils/urls.dart'; 5 | 6 | class ResumeRepository { 7 | String getAccessToken() { 8 | return UserProvider().tokens['access']; 9 | } 10 | 11 | getResumes( 12 | String userId, 13 | Map params, 14 | ) async { 15 | try { 16 | final String accessToken = getAccessToken(); 17 | String queryString = Uri(queryParameters: params).query; 18 | final String url = '${URLS.kResumeUrl}list/?user=$userId&$queryString'; 19 | 20 | final data = await APIService().sendGetRequest( 21 | accessToken, 22 | url, 23 | ); 24 | 25 | // Check if the data contains the expected keys 26 | if (data.containsKey('status') && data.containsKey('data')) { 27 | return { 28 | 'status': data['status'] ?? 500, 29 | 'message': data['message'] ?? '', 30 | 'data': data['data']['data'] ?? [], 31 | }; 32 | } else { 33 | return { 34 | 'status': 500, 35 | 'message': 'Invalid response format', 36 | 'data': [], 37 | }; 38 | } 39 | } catch (error) { 40 | return Helper().handleApiError(error); 41 | } 42 | } 43 | 44 | getResumeDetails(String resumeId) async { 45 | try { 46 | final String accessToken = getAccessToken(); 47 | final String url = '${URLS.kResumeUrl}$resumeId/details/'; 48 | 49 | final data = await APIService().sendGetRequest( 50 | accessToken, 51 | url, 52 | ); 53 | return data; 54 | } catch (error) { 55 | return Helper().handleApiError(error); 56 | } 57 | } 58 | 59 | getResumePreview(String resumeId) async { 60 | try { 61 | final String accessToken = getAccessToken(); 62 | final String url = '${URLS.kResumeUrl}$resumeId/preview/'; 63 | 64 | final data = await APIService().sendGetRequest( 65 | accessToken, 66 | url, 67 | ); 68 | return data; 69 | } catch (error) { 70 | return Helper().handleApiError(error); 71 | } 72 | } 73 | 74 | createResume(Map resumeData) async { 75 | try { 76 | final String accessToken = getAccessToken(); 77 | const String url = '${URLS.kResumeUrl}create/'; 78 | final data = await APIService().sendPostRequest( 79 | accessToken, 80 | resumeData, 81 | url, 82 | ); 83 | return data; 84 | } catch (error) { 85 | return Helper().handleApiError(error); 86 | } 87 | } 88 | 89 | updateResume(String resumeId, Map resumeData) async { 90 | try { 91 | final String accessToken = getAccessToken(); 92 | final String url = '${URLS.kResumeUrl}$resumeId/update/'; 93 | 94 | final data = await APIService().sendPatchRequest( 95 | accessToken, 96 | resumeData, 97 | url, 98 | ); 99 | return data; 100 | } catch (error) { 101 | return Helper().handleApiError(error); 102 | } 103 | } 104 | 105 | deleteResume(String resumeId) async { 106 | try { 107 | final String accessToken = getAccessToken(); 108 | final String url = '${URLS.kResumeUrl}$resumeId/destroy/'; 109 | 110 | final data = await APIService().sendDeleteRequest( 111 | accessToken, 112 | url, 113 | ); 114 | return data; 115 | } catch (error) { 116 | return Helper().handleApiError(error); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/apis/api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:http/http.dart' as http; 3 | 4 | class APIService { 5 | Map _buildHeaders(String accessToken, 6 | {bool isMultipart = false}) { 7 | return { 8 | 'Content-Type': isMultipart ? 'multipart/form-data' : 'application/json', 9 | 'Authorization': 'Bearer $accessToken', 10 | }; 11 | } 12 | 13 | Future> sendGetRequest( 14 | String accessToken, String url) async { 15 | try { 16 | final response = await http.get( 17 | Uri.parse(url), 18 | headers: _buildHeaders(accessToken), 19 | ); 20 | return { 21 | 'data': jsonDecode(response.body), 22 | 'status': response.statusCode, 23 | }; 24 | } catch (e) { 25 | print('Error in GET request: $e'); 26 | return { 27 | 'error': e.toString(), 28 | 'status': 500, 29 | }; 30 | } 31 | } 32 | 33 | Future> sendPostRequest( 34 | String accessToken, 35 | Map data, 36 | String url, 37 | ) async { 38 | try { 39 | final response = await http.post( 40 | Uri.parse(url), 41 | headers: _buildHeaders(accessToken), 42 | body: jsonEncode(data), 43 | ); 44 | return { 45 | 'data': jsonDecode(response.body), 46 | 'status': response.statusCode, 47 | }; 48 | } catch (e) { 49 | print('Error in POST request: $e'); 50 | return { 51 | 'error': e.toString(), 52 | 'status': 500, 53 | }; 54 | } 55 | } 56 | 57 | Future> sendPatchRequest( 58 | String accessToken, 59 | Map data, 60 | String url, { 61 | bool isMultipart = false, 62 | }) async { 63 | try { 64 | if (!isMultipart) { 65 | final response = await http.patch( 66 | Uri.parse(url), 67 | headers: _buildHeaders(accessToken), 68 | body: jsonEncode(data), 69 | ); 70 | return { 71 | 'data': jsonDecode(response.body), 72 | 'status': response.statusCode, 73 | }; 74 | } else { 75 | final request = http.MultipartRequest( 76 | 'PATCH', 77 | Uri.parse(url), 78 | ) 79 | ..headers.addAll(_buildHeaders(accessToken, isMultipart: true)) 80 | ..files.add( 81 | http.MultipartFile.fromBytes( 82 | 'resume_picture', // This is the key for the file 83 | data['resume_picture'].readAsBytesSync(), // This is the file 84 | filename: data['resume_picture'] 85 | .path 86 | .split('/') 87 | .last, // This is the file name 88 | ), 89 | ); 90 | 91 | final response = await request.send(); 92 | final responseBody = await response.stream.bytesToString(); 93 | 94 | return { 95 | 'data': jsonDecode(responseBody), 96 | 'status': response.statusCode, 97 | }; 98 | } 99 | } catch (e) { 100 | print('Error in PATCH request: $e'); 101 | return { 102 | 'error': e.toString(), 103 | 'status': 500, 104 | }; 105 | } 106 | } 107 | 108 | Future> sendDeleteRequest( 109 | String accessToken, String url) async { 110 | try { 111 | final response = await http.delete( 112 | Uri.parse(url), 113 | headers: _buildHeaders(accessToken), 114 | ); 115 | return { 116 | 'status': response.statusCode, 117 | }; 118 | } catch (e) { 119 | print('Error in DELETE request: $e'); 120 | return { 121 | 'error': e.toString(), 122 | 'status': 500, 123 | }; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/utils/helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:http/http.dart' as http; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:intl/intl.dart'; 7 | import 'package:provider/provider.dart'; 8 | import 'package:url_launcher/url_launcher.dart'; 9 | 10 | import '../providers/user_data_provider.dart'; 11 | import '../repositories/auth.dart'; 12 | import '../screens/auth_screens/login_screen.dart'; 13 | import 'constants.dart'; 14 | import 'local_storage.dart'; 15 | 16 | class Helper { 17 | Map handleApiError(dynamic error) { 18 | if (error is http.ClientException) { 19 | print('Client error: $error'); 20 | return { 21 | 'status': 400, 22 | 'message': 'Client error occurred: ${error.message}', 23 | }; 24 | } else if (error is SocketException) { 25 | print('No internet connection: $error'); 26 | return { 27 | 'status': 503, 28 | 'message': 'No internet connection', 29 | }; 30 | } else if (error is HttpException) { 31 | print('HTTP error: $error'); 32 | return { 33 | 'status': 500, 34 | 'message': 'HTTP error occurred: $error', 35 | }; 36 | } else { 37 | print('Unexpected error: $error'); 38 | return { 39 | 'status': 500, 40 | 'message': 'Unexpected error occurred: $error', 41 | }; 42 | } 43 | } 44 | 45 | static bool checkConnectionError(e) { 46 | if (e.toString().contains('SocketException') || 47 | e.toString().contains('HandshakeException')) { 48 | return true; 49 | } else { 50 | return false; 51 | } 52 | } 53 | 54 | bool isUnauthorizedAccess(int status) { 55 | return status == Constants.httpUnauthorizedCode || 56 | status == Constants.httpForbiddenCode; 57 | } 58 | 59 | Future launchInBrowser(String url) async { 60 | Uri uri = Uri.parse(url); 61 | if (!await launchUrl( 62 | uri, 63 | mode: LaunchMode.externalApplication, 64 | )) { 65 | throw Exception('Could not launch $url'); 66 | } 67 | } 68 | 69 | Future copyToClipboard(BuildContext context, String text, 70 | String successMsg, String errorMsg) async { 71 | await Clipboard.setData(ClipboardData(text: text)) 72 | .then((value) => showSnackBar( 73 | context, 74 | successMsg, 75 | Colors.green, 76 | )) 77 | .catchError((error) => showSnackBar( 78 | context, 79 | errorMsg, 80 | Colors.red, 81 | )); 82 | } 83 | 84 | bool isNullEmptyOrFalse(dynamic value) { 85 | if (value == null || value == '' || value == false) { 86 | return true; 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | String formatDateTime(String dateTime) { 93 | return DateFormat('MMM d, y h:mm a').format(DateTime.parse(dateTime)); 94 | } 95 | 96 | String formatDate(String date) { 97 | return DateFormat('MMM d, y').format(DateTime.parse(date)); 98 | } 99 | 100 | String formatMonthYear(String date) { 101 | return DateFormat('MMM y').format(DateTime.parse(date)); 102 | } 103 | 104 | void showSnackBar(BuildContext context, String message, Color color) { 105 | ScaffoldMessenger.of(context).showSnackBar( 106 | SnackBar( 107 | content: Text(message), 108 | duration: const Duration(seconds: 2), 109 | backgroundColor: color, 110 | ), 111 | ); 112 | } 113 | 114 | void logoutUser(BuildContext context) { 115 | LocalStorage localStorage = LocalStorage(); 116 | localStorage.clearData(); 117 | 118 | Provider.of(context, listen: false).clearData(); 119 | 120 | navigateAndClearStack(context, LoginScreen.routeName); 121 | } 122 | 123 | void navigateAndClearStack(BuildContext context, String route) { 124 | Navigator.of(context).pushNamedAndRemoveUntil(route, (route) => false); 125 | } 126 | 127 | void refreshToken(BuildContext context, String refreshToken) { 128 | LocalStorage localStorage = LocalStorage(); 129 | AuthRepository().refreshToken(refreshToken).then((data) { 130 | if (data['status'] == Constants.httpOkCode) { 131 | localStorage.writeData('tokens', { 132 | 'access': data['data']['access'], 133 | 'refresh': refreshToken, 134 | }); 135 | } else {} 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: gocv 2 | description: A new Flutter project. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ">=2.19.5 <3.0.0" 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: 37 | http: 38 | shared_preferences: 39 | permission_handler: 40 | intl: 41 | image_picker: 42 | image_cropper: 43 | url_launcher: 44 | printing: 45 | flutter_typeahead: 46 | google_fonts: 2.2.0 47 | provider: ^6.1.2 48 | flutter_localizations: 49 | sdk: flutter 50 | 51 | dev_dependencies: 52 | flutter_test: 53 | sdk: flutter 54 | 55 | # The "flutter_lints" package below contains a set of recommended lints to 56 | # encourage good coding practices. The lint set provided by the package is 57 | # activated in the `analysis_options.yaml` file located at the root of your 58 | # package. See that file for information about deactivating specific lint 59 | # rules and activating additional ones. 60 | flutter_lints: ^2.0.0 61 | 62 | # For information on the generic Dart part of this file, see the 63 | # following page: https://dart.dev/tools/pub/pubspec 64 | 65 | # The following section is specific to Flutter packages. 66 | flutter: 67 | # The following line ensures that the Material Icons font is 68 | # included with your application, so that you can use the icons in 69 | # the material Icons class. 70 | uses-material-design: true 71 | generate: true 72 | 73 | # To add assets to your application, add an assets section, like this: 74 | assets: 75 | - assets/avatars/ 76 | - assets/images/ 77 | 78 | # An image asset can refer to one or more resolution-specific "variants", see 79 | # https://flutter.dev/assets-and-images/#resolution-aware 80 | 81 | # For details regarding adding assets from package dependencies, see 82 | # https://flutter.dev/assets-and-images/#from-packages 83 | 84 | # To add custom fonts to your application, add a fonts section here, 85 | # in this "flutter" section. Each entry in this list should have a 86 | # "family" key with the font family name, and a "fonts" key with a 87 | # list giving the asset and other descriptors for the font. For 88 | # example: 89 | # fonts: 90 | # - family: Schyler 91 | # fonts: 92 | # - asset: fonts/Schyler-Regular.ttf 93 | # - asset: fonts/Schyler-Italic.ttf 94 | # style: italic 95 | # - family: Trajan Pro 96 | # fonts: 97 | # - asset: fonts/TrajanPro.ttf 98 | # - asset: fonts/TrajanPro_Bold.ttf 99 | # weight: 700 100 | # 101 | # For details regarding fonts from package dependencies, 102 | # see https://flutter.dev/custom-fonts/#from-packages 103 | -------------------------------------------------------------------------------- /lib/models/resume_preview.dart: -------------------------------------------------------------------------------- 1 | import 'package:gocv/models/award.dart'; 2 | import 'package:gocv/models/certificate.dart'; 3 | import 'package:gocv/models/contact.dart'; 4 | import 'package:gocv/models/education.dart'; 5 | import 'package:gocv/models/experience.dart'; 6 | import 'package:gocv/models/interest.dart'; 7 | import 'package:gocv/models/language.dart'; 8 | import 'package:gocv/models/personal.dart'; 9 | import 'package:gocv/models/reference.dart'; 10 | import 'package:gocv/models/skill.dart'; 11 | import 'package:gocv/models/user.dart'; 12 | 13 | class ResumePreview { 14 | String name = ''; 15 | int id = 0; 16 | UserBase user = UserBase(); 17 | Personal personal = Personal(); 18 | Contact contact = Contact(id: 0, resume: 0, email: ''); 19 | List education = []; 20 | List experience = []; 21 | List skill = []; 22 | List language = []; 23 | List interest = []; 24 | List reference = []; 25 | List award = []; 26 | List certification = []; 27 | bool? isEducationVisible; 28 | bool? isExperienceVisible; 29 | bool? isSkillVisible; 30 | bool? isLanguageVisible; 31 | bool? isInterestVisible; 32 | bool? isReferenceVisible; 33 | bool? isAwardVisible; 34 | bool? isCertificationVisible; 35 | bool? isActive; 36 | bool? isDeleted; 37 | int? createdBy; 38 | String? createdAt; 39 | int? updatedBy; 40 | String? updatedAt; 41 | 42 | ResumePreview({ 43 | required this.name, 44 | required this.id, 45 | required this.user, 46 | required this.personal, 47 | required this.contact, 48 | required this.education, 49 | required this.experience, 50 | required this.skill, 51 | required this.language, 52 | required this.interest, 53 | required this.reference, 54 | required this.award, 55 | required this.certification, 56 | this.isEducationVisible, 57 | this.isExperienceVisible, 58 | this.isSkillVisible, 59 | this.isLanguageVisible, 60 | this.isInterestVisible, 61 | this.isReferenceVisible, 62 | this.isAwardVisible, 63 | this.isCertificationVisible, 64 | this.isActive, 65 | this.isDeleted, 66 | this.createdBy, 67 | this.createdAt, 68 | this.updatedBy, 69 | this.updatedAt, 70 | }); 71 | 72 | ResumePreview.fromJson(Map json) { 73 | name = json['name']; 74 | id = json['id']; 75 | user = (json['user'] != null ? UserBase.fromJson(json['user']) : null)!; 76 | personal = (json['personal'] != null 77 | ? Personal.fromJson(json['personal']) 78 | : null)!; 79 | contact = 80 | (json['contact'] != null ? Contact.fromJson(json['contact']) : null)!; 81 | if (json['education'] != null) { 82 | education = []; 83 | json['education'].forEach((v) { 84 | education.add(Education.fromJson(v)); 85 | }); 86 | } 87 | if (json['experience'] != null) { 88 | experience = []; 89 | json['experience'].forEach((v) { 90 | experience.add(Experience.fromJson(v)); 91 | }); 92 | } 93 | if (json['skill'] != null) { 94 | skill = []; 95 | json['skill'].forEach((v) { 96 | skill.add(Skill.fromJson(v)); 97 | }); 98 | } 99 | if (json['language'] != null) { 100 | language = []; 101 | json['language'].forEach((v) { 102 | language.add(Language.fromJson(v)); 103 | }); 104 | } 105 | if (json['interest'] != null) { 106 | interest = []; 107 | json['interest'].forEach((v) { 108 | interest.add(Interest.fromJson(v)); 109 | }); 110 | } 111 | if (json['reference'] != null) { 112 | reference = []; 113 | json['reference'].forEach((v) { 114 | reference.add(Reference.fromJson(v)); 115 | }); 116 | } 117 | if (json['award'] != null) { 118 | award = []; 119 | json['award'].forEach((v) { 120 | award.add(Award.fromJson(v)); 121 | }); 122 | } 123 | if (json['certification'] != null) { 124 | certification = []; 125 | json['certification'].forEach((v) { 126 | certification.add(Certificate.fromJson(v)); 127 | }); 128 | } 129 | isEducationVisible = json['is_education_visible']; 130 | isExperienceVisible = json['is_experience_visible']; 131 | isSkillVisible = json['is_skill_visible']; 132 | isLanguageVisible = json['is_language_visible']; 133 | isInterestVisible = json['is_interest_visible']; 134 | isReferenceVisible = json['is_reference_visible']; 135 | isAwardVisible = json['is_award_visible']; 136 | isCertificationVisible = json['is_certification_visible']; 137 | isActive = json['is_active']; 138 | isDeleted = json['is_deleted']; 139 | createdBy = json['created_by']; 140 | createdAt = json['created_at']; 141 | updatedBy = json['updated_by']; 142 | updatedAt = json['updated_at']; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/screens/utility_screens/email_update_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:gocv/providers/user_data_provider.dart'; 4 | import 'package:gocv/utils/constants.dart'; 5 | import 'package:gocv/widgets/custom_text_form_field.dart'; 6 | 7 | class EmailUpdateScreen extends StatefulWidget { 8 | static const routeName = Constants.emailUpdateScreenRouteName; 9 | 10 | const EmailUpdateScreen({super.key}); 11 | 12 | @override 13 | State createState() => _EmailUpdateScreenState(); 14 | } 15 | 16 | class _EmailUpdateScreenState extends State { 17 | late String userId; 18 | 19 | final _formKey = GlobalKey(); 20 | 21 | bool isLoading = false; 22 | bool isSubmitting = false; 23 | bool isError = false; 24 | bool isOtpSent = false; 25 | String errorText = ''; 26 | 27 | TextEditingController emailController = TextEditingController(); 28 | TextEditingController otpController = TextEditingController(); 29 | 30 | String uuid = ''; 31 | late String email; 32 | String otp = ''; 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | 38 | setState(() { 39 | userId = UserProvider().userData!.id.toString(); 40 | 41 | email = UserProvider().userData!.email!; 42 | emailController.text = email; 43 | }); 44 | } 45 | 46 | @override 47 | void dispose() { 48 | emailController.dispose(); 49 | otpController.dispose(); 50 | 51 | super.dispose(); 52 | } 53 | 54 | handleSubmit() {} 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | double width = MediaQuery.of(context).size.width; 59 | 60 | return Scaffold( 61 | appBar: AppBar( 62 | title: Text(AppLocalizations.of(context)!.update_email_address), 63 | ), 64 | body: isLoading 65 | ? const Center(child: CircularProgressIndicator()) 66 | : Container( 67 | margin: const EdgeInsets.symmetric(horizontal: 10.0), 68 | child: SingleChildScrollView( 69 | child: Form( 70 | key: _formKey, 71 | child: Column( 72 | crossAxisAlignment: CrossAxisAlignment.center, 73 | children: [ 74 | const SizedBox(height: 10), 75 | CustomTextFormField( 76 | width: width, 77 | controller: emailController, 78 | labelText: 'Email Address', 79 | hintText: 'Email address', 80 | prefixIcon: Icons.email_outlined, 81 | textCapitalization: TextCapitalization.words, 82 | borderRadius: 10, 83 | keyboardType: TextInputType.emailAddress, 84 | onChanged: (value) { 85 | setState(() { 86 | email = value; 87 | }); 88 | }, 89 | validator: (value) { 90 | if (value == null || value.isEmpty) { 91 | return 'Please enter email address'; 92 | } else if (!RegExp( 93 | r'^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+$') 94 | .hasMatch(value)) { 95 | return 'Please enter valid email address'; 96 | } else if (value == email) { 97 | return 'Please enter different email address'; 98 | } 99 | return null; 100 | }, 101 | ), 102 | const SizedBox(height: 5), 103 | isError 104 | ? Text( 105 | errorText, 106 | style: const TextStyle( 107 | color: Colors.red, 108 | ), 109 | ) 110 | : Container(), 111 | isOtpSent == true 112 | ? CustomTextFormField( 113 | width: width, 114 | controller: otpController, 115 | labelText: 'OTP', 116 | hintText: 'OTP', 117 | prefixIcon: Icons.pin, 118 | textCapitalization: TextCapitalization.none, 119 | borderRadius: 10, 120 | keyboardType: TextInputType.number, 121 | onChanged: (value) { 122 | setState(() { 123 | otp = value; 124 | }); 125 | }, 126 | validator: (value) { 127 | if (value == null || value.isEmpty) { 128 | return 'Please enter OTP code'; 129 | } else if (value.length != 6) { 130 | return 'Please enter valid OTP code'; 131 | } 132 | return null; 133 | }, 134 | ) 135 | : Container(), 136 | const SizedBox(height: 5), 137 | ], 138 | ), 139 | ), 140 | ), 141 | ), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/screens/utility_screens/settings_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:gocv/providers/settings_provider.dart'; 4 | import 'package:gocv/screens/auth_screens/login_screen.dart'; 5 | import 'package:gocv/screens/utility_screens/account_settings_screen.dart'; 6 | import 'package:gocv/utils/constants.dart'; 7 | import 'package:gocv/utils/helper.dart'; 8 | import 'package:gocv/utils/local_storage.dart'; 9 | import 'package:provider/provider.dart'; 10 | 11 | class SettingsScreen extends StatefulWidget { 12 | static const routeName = Constants.settingsScreenRouteName; 13 | 14 | const SettingsScreen({Key? key}) : super(key: key); 15 | 16 | @override 17 | State createState() => _SettingsScreenState(); 18 | } 19 | 20 | class _SettingsScreenState extends State { 21 | final LocalStorage localStorage = LocalStorage(); 22 | 23 | handleLogout() { 24 | localStorage.clearData(); 25 | Helper().navigateAndClearStack(context, LoginScreen.routeName); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | final settingsProvider = Provider.of(context); 31 | 32 | return Scaffold( 33 | appBar: AppBar( 34 | title: Text( 35 | AppLocalizations.of(context)!.settings, 36 | ), 37 | leading: GestureDetector( 38 | onTap: () { 39 | Navigator.pop(context); 40 | }, 41 | child: Container( 42 | padding: const EdgeInsets.all(10.0), 43 | margin: const EdgeInsets.only(left: 10.0), 44 | child: const Icon( 45 | Icons.arrow_back_ios, 46 | ), 47 | ), 48 | ), 49 | ), 50 | body: ListView( 51 | children: [ 52 | SettingsListItem( 53 | title: AppLocalizations.of(context)!.account, 54 | icon: Icons.account_box_outlined, 55 | onTap: () { 56 | Navigator.of(context).pushNamed( 57 | AccountSettingsScreen.routeName, 58 | ); 59 | }, 60 | ), 61 | const Divider(), 62 | SettingsListItem( 63 | title: AppLocalizations.of(context)!.contact_us, 64 | icon: Icons.contact_page_outlined, 65 | onTap: () {}, 66 | ), 67 | const Divider(), 68 | SettingsListItem( 69 | title: AppLocalizations.of(context)!.aboutUs, 70 | icon: Icons.privacy_tip, 71 | onTap: () {}, 72 | ), 73 | const Divider(), 74 | SettingsListItem( 75 | title: AppLocalizations.of(context)!.licenses, 76 | icon: Icons.privacy_tip, 77 | onTap: () { 78 | Navigator.of(context).push(MaterialPageRoute( 79 | builder: (context) => const LicensePage(), 80 | )); 81 | }, 82 | ), 83 | const Divider(), 84 | 85 | // Theme Toggle Option 86 | Padding( 87 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 88 | child: SwitchListTile( 89 | title: Text(AppLocalizations.of(context)!.darkMode), 90 | value: settingsProvider.themeMode == ThemeMode.dark, 91 | onChanged: (bool value) { 92 | settingsProvider.setThemeMode( 93 | value ? ThemeMode.dark : ThemeMode.light, 94 | ); 95 | }, 96 | secondary: const Icon(Icons.dark_mode), 97 | ), 98 | ), 99 | const Divider(), 100 | 101 | // Language Selection Option 102 | Padding( 103 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 104 | child: ListTile( 105 | leading: const Icon(Icons.language), 106 | title: Text(AppLocalizations.of(context)!.language), 107 | trailing: DropdownButton( 108 | value: settingsProvider.language, 109 | items: const [ 110 | DropdownMenuItem(value: 'en', child: Text('English')), 111 | DropdownMenuItem(value: 'bn', child: Text('বাংলা')), 112 | // Add other languages as needed 113 | ], 114 | onChanged: (String? newValue) { 115 | if (newValue != null) { 116 | settingsProvider.setLanguage(newValue); 117 | } 118 | }, 119 | ), 120 | ), 121 | ), 122 | const Divider(), 123 | 124 | // Logout Option 125 | Container( 126 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 127 | child: ListTile( 128 | leading: const RotationTransition( 129 | turns: AlwaysStoppedAnimation(180 / 360), 130 | child: Icon( 131 | Icons.exit_to_app, 132 | color: Colors.red, 133 | size: 30, 134 | ), 135 | ), 136 | title: Text(AppLocalizations.of(context)!.logout), 137 | onTap: () => handleLogout(), 138 | ), 139 | ), 140 | ], 141 | ), 142 | ); 143 | } 144 | } 145 | 146 | class SettingsListItem extends StatelessWidget { 147 | final String title; 148 | final IconData icon; 149 | final Function()? onTap; 150 | 151 | const SettingsListItem({ 152 | required this.title, 153 | required this.icon, 154 | this.onTap, 155 | super.key, 156 | }); 157 | 158 | @override 159 | Widget build(BuildContext context) { 160 | return Container( 161 | padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), 162 | child: ListTile( 163 | leading: Icon(icon, size: 30), 164 | title: Text(title), 165 | onTap: onTap, 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/screens/auth_screens/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import '../../models/user.dart'; 5 | import '../../providers/user_data_provider.dart'; 6 | import '../../repositories/auth.dart'; 7 | import '../../utils/constants.dart'; 8 | import '../../utils/helper.dart'; 9 | import '../../utils/local_storage.dart'; 10 | import '../../widgets/custom_button.dart'; 11 | import '../../widgets/custom_text_form_field.dart'; 12 | import '../home_screen.dart'; 13 | import 'sign_up_screen.dart'; 14 | 15 | class LoginScreen extends StatefulWidget { 16 | static const routeName = Constants.loginScreenRouteName; 17 | 18 | const LoginScreen({Key? key}) : super(key: key); 19 | 20 | @override 21 | State createState() => _LoginScreenState(); 22 | } 23 | 24 | class _LoginScreenState extends State { 25 | final LocalStorage localStorage = LocalStorage(); 26 | late UserProvider userProvider; 27 | 28 | TextEditingController emailController = TextEditingController(); 29 | TextEditingController passwordController = TextEditingController(); 30 | 31 | String email = ''; 32 | String password = ''; 33 | 34 | bool isObscure = true; 35 | bool isLoading = false; 36 | 37 | final _formKey = GlobalKey(); 38 | 39 | @override 40 | void initState() { 41 | super.initState(); 42 | 43 | userProvider = Provider.of(context, listen: false); 44 | } 45 | 46 | @override 47 | void dispose() { 48 | emailController.dispose(); 49 | passwordController.dispose(); 50 | 51 | super.dispose(); 52 | } 53 | 54 | handleLogin() { 55 | FocusScope.of(context).unfocus(); 56 | setState(() { 57 | isLoading = true; 58 | }); 59 | 60 | AuthRepository() 61 | .loginUser( 62 | email, 63 | password, 64 | ) 65 | .then((data) async { 66 | if (data['status'] == Constants.httpOkCode) { 67 | await localStorage.writeData('user', data['data']['user']); 68 | await localStorage.writeData('tokens', data['data']['tokens']); 69 | 70 | UserBase user = UserBase.fromJson(data['data']['user']); 71 | userProvider.setUserData(user); 72 | userProvider.setTokens(data['data']['tokens']); 73 | 74 | if (!context.mounted) return; 75 | Helper().showSnackBar( 76 | context, 77 | 'Login Successful', 78 | Colors.green, 79 | ); 80 | setState(() { 81 | isLoading = false; 82 | }); 83 | 84 | Navigator.pushReplacementNamed(context, HomeScreen.routeName); 85 | } else { 86 | setState(() { 87 | isLoading = false; 88 | }); 89 | Helper().showSnackBar( 90 | context, 91 | data['error'], 92 | Colors.red, 93 | ); 94 | } 95 | }); 96 | } 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | double width = MediaQuery.of(context).size.width; 101 | 102 | return Scaffold( 103 | body: Container( 104 | padding: const EdgeInsets.all(10), 105 | child: Form( 106 | key: _formKey, 107 | child: Center( 108 | child: Column( 109 | mainAxisAlignment: MainAxisAlignment.center, 110 | children: [ 111 | const Text( 112 | Constants.appName, 113 | style: TextStyle( 114 | fontSize: 35, 115 | fontWeight: FontWeight.bold, 116 | ), 117 | ), 118 | const SizedBox(height: 20), 119 | const Text( 120 | 'Welcome back', 121 | style: TextStyle( 122 | fontSize: 30, 123 | fontWeight: FontWeight.bold, 124 | ), 125 | ), 126 | const SizedBox(height: 10), 127 | const Text( 128 | 'Login to continue', 129 | style: TextStyle( 130 | fontSize: 20, 131 | fontWeight: FontWeight.bold, 132 | ), 133 | ), 134 | const SizedBox( 135 | height: 20, 136 | ), 137 | CustomTextFormField( 138 | width: width, 139 | autofocus: true, 140 | controller: emailController, 141 | labelText: 'Email', 142 | hintText: 'Email Address', 143 | prefixIcon: Icons.email, 144 | textCapitalization: TextCapitalization.none, 145 | borderRadius: 10, 146 | keyboardType: TextInputType.emailAddress, 147 | onChanged: (value) { 148 | setState(() { 149 | email = value; 150 | }); 151 | }, 152 | validator: (value) { 153 | if (value!.isEmpty) { 154 | return 'Please enter your email'; 155 | } 156 | return null; 157 | }, 158 | ), 159 | const SizedBox(height: 5), 160 | CustomTextFormField( 161 | width: width, 162 | controller: passwordController, 163 | labelText: 'Password', 164 | hintText: 'Password', 165 | prefixIcon: Icons.lock, 166 | suffixIcon: 167 | !isObscure ? Icons.visibility : Icons.visibility_off, 168 | textCapitalization: TextCapitalization.none, 169 | borderRadius: 10, 170 | keyboardType: TextInputType.visiblePassword, 171 | isObscure: isObscure, 172 | suffixIconOnPressed: () { 173 | setState(() { 174 | isObscure = !isObscure; 175 | }); 176 | }, 177 | onChanged: (value) { 178 | setState(() { 179 | password = value; 180 | }); 181 | }, 182 | validator: (value) { 183 | if (value!.isEmpty) { 184 | return 'Please enter your password'; 185 | } 186 | return null; 187 | }, 188 | ), 189 | Container( 190 | alignment: Alignment.centerRight, 191 | child: TextButton( 192 | onPressed: () {}, 193 | child: const Text('Forgot Password?'), 194 | ), 195 | ), 196 | CustomButton( 197 | text: 'Login', 198 | isLoading: isLoading, 199 | isDisabled: isLoading, 200 | onPressed: () { 201 | if (_formKey.currentState!.validate()) { 202 | handleLogin(); 203 | } 204 | }, 205 | ), 206 | Row( 207 | mainAxisAlignment: MainAxisAlignment.center, 208 | children: [ 209 | const Text("Don't have an account?"), 210 | TextButton( 211 | onPressed: () { 212 | Navigator.pushReplacementNamed( 213 | context, 214 | SignUpScreen.routeName, 215 | ); 216 | }, 217 | child: const Text('Sign Up'), 218 | ), 219 | ], 220 | ), 221 | ], 222 | ), 223 | ), 224 | ), 225 | ), 226 | ); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /lib/screens/profile_screens/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | import '../../models/applicant.dart'; 6 | import '../../models/user.dart'; 7 | import '../../providers/user_data_provider.dart'; 8 | import '../../providers/user_profile_provider.dart'; 9 | import '../../repositories/user.dart'; 10 | import '../../utils/constants.dart'; 11 | import '../../utils/helper.dart'; 12 | 13 | class ProfileScreen extends StatefulWidget { 14 | static const String routeName = Constants.profileScreenRouteName; 15 | 16 | const ProfileScreen({super.key}); 17 | 18 | @override 19 | State createState() => _ProfileScreenState(); 20 | } 21 | 22 | class _ProfileScreenState extends State { 23 | UserRepository userRepository = UserRepository(); 24 | UserProvider userProvider = UserProvider(); 25 | 26 | late UserProfileProvider userProfileProvider; 27 | 28 | bool isLoading = true; 29 | bool isError = false; 30 | String errorText = ''; 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | 36 | userProfileProvider = Provider.of( 37 | context, 38 | listen: false, 39 | ); 40 | 41 | fetchUserProfile(); 42 | } 43 | 44 | fetchUserProfile() async { 45 | setState(() { 46 | isLoading = true; 47 | }); 48 | try { 49 | final response = await userRepository.getUserProfile(); 50 | 51 | if (response['status'] == Constants.httpOkCode) { 52 | final UserBase userBase = 53 | UserBase.fromJson(response['data']['user_data']); 54 | final Applicant applicant = 55 | Applicant.fromJson(response['data']['applicant_data']); 56 | final UserProfile userProfile = UserProfile( 57 | userData: userBase, 58 | applicantData: applicant, 59 | ); 60 | userProfileProvider.setUserProfile(userProfile); 61 | setState(() { 62 | isLoading = false; 63 | }); 64 | } else { 65 | if (Helper().isUnauthorizedAccess(response['status'])) { 66 | if (!mounted) return; 67 | Helper().showSnackBar( 68 | context, 69 | Constants.sessionExpiredMsg, 70 | Colors.red, 71 | ); 72 | Helper().logoutUser(context); 73 | } else { 74 | setState(() { 75 | isError = true; 76 | errorText = response['error']; 77 | }); 78 | } 79 | } 80 | } catch (error) { 81 | print('Error getting user profile: $error'); 82 | if (!mounted) return; 83 | setState(() { 84 | isLoading = false; 85 | isError = true; 86 | errorText = 'Error getting user profile: $error'; 87 | }); 88 | } 89 | } 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | return Scaffold( 94 | appBar: AppBar( 95 | title: Text( 96 | AppLocalizations.of(context)!.profile, 97 | ), 98 | ), 99 | floatingActionButton: FloatingActionButton( 100 | onPressed: () { 101 | Navigator.pushNamed(context, Constants.updateProfileScreenRouteName) 102 | .then((value) => {if (value == true) fetchUserProfile()}); 103 | }, 104 | child: const Icon(Icons.edit), 105 | ), 106 | body: isLoading 107 | ? const Center( 108 | child: CircularProgressIndicator(), 109 | ) 110 | : RefreshIndicator( 111 | onRefresh: () { 112 | return Future.delayed( 113 | const Duration(seconds: 1), 114 | () { 115 | fetchUserProfile(); 116 | }, 117 | ); 118 | }, 119 | child: Container( 120 | padding: const EdgeInsets.symmetric( 121 | horizontal: 10, 122 | ), 123 | child: ListView( 124 | children: [ 125 | const SizedBox(height: 10.0), 126 | SizedBox( 127 | height: 200, 128 | child: ClipRRect( 129 | borderRadius: BorderRadius.circular(8.0), 130 | child: userProfileProvider.userProfile.applicantData 131 | ?.profilePicture != 132 | null 133 | ? Image.network( 134 | userProfileProvider 135 | .userProfile.applicantData!.profilePicture!, 136 | height: 200.0, 137 | ) 138 | : Image.asset( 139 | Constants.defaultAvatarPath, 140 | height: 200.0, 141 | ), 142 | ), 143 | ), 144 | const SizedBox(height: 10.0), 145 | Text( 146 | '${userProfileProvider.userProfile.applicantData?.firstName} ${userProfileProvider.userProfile.applicantData?.lastName}', 147 | style: const TextStyle( 148 | fontSize: 22, 149 | ), 150 | textAlign: TextAlign.center, 151 | ), 152 | const Divider(), 153 | Row( 154 | children: [ 155 | Container( 156 | padding: const EdgeInsets.all(12), 157 | decoration: BoxDecoration( 158 | color: Colors.grey.shade300, 159 | borderRadius: BorderRadius.circular(30), 160 | ), 161 | child: const Icon( 162 | Icons.email, 163 | size: 24, 164 | color: Colors.black, 165 | ), 166 | ), 167 | const SizedBox(width: 10), 168 | Expanded( 169 | child: Text( 170 | userProvider.userData!.email ?? 'N/A', 171 | style: const TextStyle(fontSize: 18), 172 | ), 173 | ) 174 | ], 175 | ), 176 | const Divider(), 177 | Row( 178 | children: [ 179 | Container( 180 | padding: const EdgeInsets.all(12), 181 | decoration: BoxDecoration( 182 | color: Colors.grey.shade300, 183 | borderRadius: BorderRadius.circular(30), 184 | ), 185 | child: const Icon( 186 | Icons.phone, 187 | size: 24, 188 | color: Colors.black, 189 | ), 190 | ), 191 | const SizedBox(width: 15), 192 | Expanded( 193 | child: Text( 194 | userProfileProvider 195 | .userProfile.applicantData?.phoneNumber ?? 196 | 'N/A', 197 | style: const TextStyle(fontSize: 18), 198 | ), 199 | ) 200 | ], 201 | ), 202 | const Divider(), 203 | ], 204 | ), 205 | ), 206 | ), 207 | ); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /lib/screens/main_screens/resume_details/interest/add_edit_interest_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/repositories/interest.dart'; 3 | import 'package:gocv/widgets/custom_button.dart'; 4 | import 'package:gocv/widgets/custom_text_form_field.dart'; 5 | 6 | class AddEditInterestPage extends StatefulWidget { 7 | final String resumeId; 8 | String? interestId; 9 | 10 | AddEditInterestPage({ 11 | Key? key, 12 | required this.resumeId, 13 | this.interestId, 14 | }) : super(key: key); 15 | 16 | @override 17 | State createState() => _AddEditInterestPageState(); 18 | } 19 | 20 | class _AddEditInterestPageState extends State { 21 | InterestRepository interestRepository = InterestRepository(); 22 | 23 | final GlobalKey _formKey = GlobalKey(); 24 | 25 | bool isLoading = true; 26 | bool isError = false; 27 | String errorText = ''; 28 | 29 | TextEditingController interestController = TextEditingController(); 30 | TextEditingController descriptionController = TextEditingController(); 31 | 32 | int id = 0; 33 | String uuid = ''; 34 | String interest = ''; 35 | String description = ''; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | 41 | if (widget.interestId != null) { 42 | getInterest(widget.interestId!); 43 | } else { 44 | setState(() { 45 | isLoading = false; 46 | isError = false; 47 | }); 48 | } 49 | } 50 | 51 | @override 52 | void dispose() { 53 | interestController.dispose(); 54 | descriptionController.dispose(); 55 | 56 | super.dispose(); 57 | } 58 | 59 | getInterest(String interestId) { 60 | // final String url = '${URLS.kInterestUrl}${interestId}/details/'; 61 | 62 | // APIService().sendGetRequest(accessToken, url).then((data) { 63 | // print(data); 64 | // if (data['status'] == Constants.HTTP_OK) { 65 | // setState(() { 66 | // isLoading = false; 67 | // isError = false; 68 | // uuid = data['data']['uuid']; 69 | // interest = data['data']['interest']; 70 | // description = data['data']['description']; 71 | // interestController.text = interest; 72 | // descriptionController.text = description; 73 | // }); 74 | // } else { 75 | // if (Helper().isUnauthorizedAccess(data['status'])) { 76 | // Helper().showSnackBar( 77 | // context, 78 | // Constants.SESSION_EXPIRED_MSG, 79 | // Colors.red, 80 | // ); 81 | // Helper().logoutUser(context); 82 | // } else { 83 | // setState(() { 84 | // isLoading = false; 85 | // isError = true; 86 | // errorText = data['error']; 87 | // }); 88 | // } 89 | // } 90 | // }).catchError((error) { 91 | // setState(() { 92 | // isLoading = false; 93 | // isError = true; 94 | // errorText = error.toString(); 95 | // }); 96 | // Helper().showSnackBar( 97 | // context, 98 | // error.toString(), 99 | // Colors.red, 100 | // ); 101 | // }); 102 | } 103 | 104 | createInterest() { 105 | // Map data = { 106 | // 'interest': interest, 107 | // 'description': description, 108 | // }; 109 | // final String url = '${URLS.kInterestUrl}${widget.resumeId}/create/'; 110 | 111 | // APIService().sendPostRequest(accessToken, data, url).then((value) { 112 | // if (value['status'] == Constants.HTTP_CREATED) { 113 | // Navigator.pop(context); 114 | // } else { 115 | // if (Helper().isUnauthorizedAccess(data['status'])) { 116 | // Helper().showSnackBar( 117 | // context, 118 | // Constants.SESSION_EXPIRED_MSG, 119 | // Colors.red, 120 | // ); 121 | // Helper().logoutUser(context); 122 | // } else { 123 | // setState(() { 124 | // isLoading = false; 125 | // isError = true; 126 | // errorText = value['error']; 127 | // }); 128 | // } 129 | // } 130 | // }).catchError((error) { 131 | // setState(() { 132 | // isLoading = false; 133 | // isError = true; 134 | // errorText = error.toString(); 135 | // }); 136 | // Helper().showSnackBar( 137 | // context, 138 | // error.toString(), 139 | // Colors.red, 140 | // ); 141 | // }); 142 | } 143 | 144 | updateInterest() { 145 | // Map data = { 146 | // 'interest': interest, 147 | // 'description': description, 148 | // }; 149 | // final String url = '${URLS.kInterestUrl}${widget.interestId}/update/'; 150 | 151 | // APIService().sendPatchRequest(accessToken, data, url).then((data) async { 152 | // if (data['status'] == Constants.HTTP_OK) { 153 | // Navigator.pop(context); 154 | // } else { 155 | // if (Helper().isUnauthorizedAccess(data['status'])) { 156 | // Helper().showSnackBar( 157 | // context, 158 | // Constants.SESSION_EXPIRED_MSG, 159 | // Colors.red, 160 | // ); 161 | // Helper().logoutUser(context); 162 | // } else { 163 | // setState(() { 164 | // isLoading = false; 165 | // isError = true; 166 | // errorText = data['error']; 167 | // }); 168 | // } 169 | // } 170 | // }); 171 | } 172 | 173 | handleSubmit() { 174 | setState(() { 175 | isLoading = true; 176 | }); 177 | if (widget.interestId != null) { 178 | updateInterest(); 179 | } else { 180 | createInterest(); 181 | } 182 | } 183 | 184 | @override 185 | Widget build(BuildContext context) { 186 | double width = MediaQuery.of(context).size.width; 187 | 188 | return Scaffold( 189 | appBar: AppBar( 190 | title: widget.interestId != null 191 | ? const Text('Update Interest') 192 | : const Text('Add Interest'), 193 | ), 194 | resizeToAvoidBottomInset: false, 195 | bottomNavigationBar: Container( 196 | padding: const EdgeInsets.symmetric( 197 | horizontal: 10.0, 198 | vertical: 30.0, 199 | ), 200 | decoration: const BoxDecoration( 201 | borderRadius: BorderRadius.only( 202 | topLeft: Radius.circular(10), 203 | topRight: Radius.circular(10), 204 | ), 205 | boxShadow: [ 206 | BoxShadow( 207 | blurRadius: 5, 208 | offset: Offset(0, -2), 209 | ), 210 | ], 211 | ), 212 | child: CustomButton( 213 | text: widget.interestId == null ? 'Add Interest' : 'Update Interest', 214 | isLoading: isLoading, 215 | isDisabled: isLoading, 216 | onPressed: () { 217 | if (_formKey.currentState!.validate()) handleSubmit(); 218 | }, 219 | ), 220 | ), 221 | body: Container( 222 | margin: const EdgeInsets.symmetric(horizontal: 10.0), 223 | child: SingleChildScrollView( 224 | child: Form( 225 | key: _formKey, 226 | child: Column( 227 | children: [ 228 | const SizedBox(height: 10), 229 | CustomTextFormField( 230 | width: width, 231 | controller: interestController, 232 | labelText: 'Interest', 233 | hintText: 'Interest', 234 | prefixIcon: Icons.interests_outlined, 235 | textCapitalization: TextCapitalization.words, 236 | borderRadius: 10, 237 | keyboardType: TextInputType.name, 238 | onChanged: (value) { 239 | setState(() { 240 | interest = value!; 241 | }); 242 | }, 243 | validator: (value) { 244 | if (value == null || value.isEmpty) { 245 | return 'Please enter interest'; 246 | } 247 | return null; 248 | }, 249 | ), 250 | const SizedBox(height: 10), 251 | CustomTextFormField( 252 | width: width, 253 | controller: descriptionController, 254 | labelText: 'Description', 255 | hintText: 'Description', 256 | prefixIcon: Icons.description, 257 | textCapitalization: TextCapitalization.sentences, 258 | borderRadius: 10, 259 | keyboardType: TextInputType.text, 260 | onChanged: (value) { 261 | setState(() { 262 | description = value!; 263 | }); 264 | }, 265 | ), 266 | const SizedBox(height: 50), 267 | ], 268 | ), 269 | ), 270 | ), 271 | ), 272 | ); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /lib/screens/main_screens/resume_details/language/language_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gocv/models/language.dart'; 3 | import 'package:gocv/screens/main_screens/resume_details/language/add_edit_language_screen.dart'; 4 | import 'package:gocv/repositories/language.dart'; 5 | import 'package:gocv/utils/constants.dart'; 6 | import 'package:gocv/utils/helper.dart'; 7 | 8 | class LanguagePage extends StatefulWidget { 9 | final String resumeId; 10 | 11 | const LanguagePage({ 12 | Key? key, 13 | required this.resumeId, 14 | }) : super(key: key); 15 | 16 | @override 17 | State createState() => _LanguagePageState(); 18 | } 19 | 20 | class _LanguagePageState extends State { 21 | LanguageRepository languageRepository = LanguageRepository(); 22 | 23 | List languageList = []; 24 | 25 | bool isLoading = true; 26 | bool isError = false; 27 | String errorText = ''; 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | 33 | fetchLanguages(widget.resumeId); 34 | } 35 | 36 | fetchLanguages(String resumeId) async { 37 | Map params = { 38 | 'resume_id': resumeId, 39 | }; 40 | try { 41 | final response = await languageRepository.getLanguages(resumeId, params); 42 | 43 | if (response['status'] == Constants.httpOkCode) { 44 | final List fetchedLanguages = 45 | (response['data']['data'] as List) 46 | .map((data) => Language.fromJson(data)) 47 | .toList(); 48 | 49 | setState(() { 50 | languageList = fetchedLanguages; 51 | isLoading = false; 52 | isError = false; 53 | errorText = ''; 54 | }); 55 | } else { 56 | if (Helper().isUnauthorizedAccess(response['status'])) { 57 | if (!mounted) return; 58 | Helper().showSnackBar( 59 | context, 60 | Constants.sessionExpiredMsg, 61 | Colors.red, 62 | ); 63 | Helper().logoutUser(context); 64 | } else { 65 | setState(() { 66 | isLoading = false; 67 | errorText = response['message']; 68 | }); 69 | if (!mounted) return; 70 | Helper().showSnackBar( 71 | context, 72 | errorText, 73 | Colors.red, 74 | ); 75 | } 76 | } 77 | } catch (error) { 78 | if (!mounted) return; 79 | setState(() { 80 | isLoading = false; 81 | errorText = 'Error fetching data: $error'; 82 | }); 83 | Helper().showSnackBar( 84 | context, 85 | Constants.genericErrorMsg, 86 | Colors.red, 87 | ); 88 | } 89 | } 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | double width = MediaQuery.of(context).size.width; 94 | 95 | return Scaffold( 96 | resizeToAvoidBottomInset: false, 97 | floatingActionButton: FloatingActionButton( 98 | child: const Icon(Icons.add), 99 | onPressed: () { 100 | Navigator.push(context, MaterialPageRoute( 101 | builder: (context) { 102 | return AddEditLanguagePage( 103 | resumeId: widget.resumeId, 104 | ); 105 | }, 106 | )); 107 | }, 108 | ), 109 | body: isLoading 110 | ? const Center(child: CircularProgressIndicator()) 111 | : isError 112 | ? Center( 113 | child: Text( 114 | errorText, 115 | style: const TextStyle( 116 | color: Colors.red, 117 | fontSize: 20, 118 | ), 119 | ), 120 | ) 121 | : languageList.isEmpty 122 | ? const Center( 123 | child: Text( 124 | 'No languages added', 125 | style: TextStyle( 126 | fontSize: 22, 127 | ), 128 | ), 129 | ) 130 | : RefreshIndicator( 131 | onRefresh: () async { 132 | fetchLanguages(widget.resumeId); 133 | }, 134 | child: ListView.builder( 135 | itemCount: languageList.length, 136 | itemBuilder: (context, index) { 137 | return languageItem(context, index, width); 138 | }, 139 | ), 140 | ), 141 | ); 142 | } 143 | 144 | Widget languageItem(BuildContext context, int index, double width) { 145 | return Container( 146 | margin: const EdgeInsets.symmetric( 147 | horizontal: 10, 148 | vertical: 5, 149 | ), 150 | padding: const EdgeInsets.symmetric( 151 | horizontal: 10, 152 | vertical: 10, 153 | ), 154 | decoration: BoxDecoration( 155 | borderRadius: BorderRadius.circular(10), 156 | boxShadow: [ 157 | BoxShadow( 158 | blurRadius: 5, 159 | offset: const Offset(0, 3), 160 | ), 161 | ], 162 | ), 163 | child: Column( 164 | crossAxisAlignment: CrossAxisAlignment.start, 165 | children: [ 166 | Row( 167 | children: [ 168 | const Icon( 169 | Icons.language, 170 | ), 171 | const SizedBox(width: 10), 172 | SizedBox( 173 | width: width * 0.7, 174 | child: Text( 175 | '${languageList[index].name} - ${languageList[index].proficiency!}', 176 | style: const TextStyle( 177 | fontSize: 18, 178 | fontWeight: FontWeight.bold, 179 | ), 180 | ), 181 | ), 182 | const SizedBox(width: 5), 183 | PopupMenuButton( 184 | icon: const Icon(Icons.more_vert), 185 | itemBuilder: (context) => [ 186 | PopupMenuItem( 187 | onTap: () { 188 | Navigator.push( 189 | context, 190 | MaterialPageRoute( 191 | builder: (context) { 192 | return AddEditLanguagePage( 193 | resumeId: widget.resumeId, 194 | languageId: languageList[index].id.toString(), 195 | ); 196 | }, 197 | ), 198 | ); 199 | }, 200 | value: 'update', 201 | child: const Text('Update'), 202 | ), 203 | PopupMenuItem( 204 | onTap: () { 205 | showDialog( 206 | context: context, 207 | builder: (context) { 208 | return AlertDialog( 209 | title: const Text('Delete Language?'), 210 | content: const Text( 211 | 'Are you sure you want to delete this language?', 212 | ), 213 | actions: [ 214 | TextButton( 215 | onPressed: () { 216 | Navigator.pop(context); 217 | }, 218 | child: const Text('Cancel'), 219 | ), 220 | TextButton( 221 | onPressed: () async { 222 | // await deleteLanguage( 223 | // languageList[index].id.toString(), 224 | // ); 225 | }, 226 | child: const Text( 227 | 'Delete', 228 | style: TextStyle( 229 | color: Colors.red, 230 | ), 231 | ), 232 | ), 233 | ], 234 | ); 235 | }, 236 | ); 237 | }, 238 | value: 'delete', 239 | child: const Text( 240 | 'Delete', 241 | style: TextStyle( 242 | color: Colors.red, 243 | ), 244 | ), 245 | ), 246 | ], 247 | ), 248 | ], 249 | ), 250 | languageList[index].description == null || 251 | languageList[index].description == '' 252 | ? const SizedBox() 253 | : Container( 254 | margin: const EdgeInsets.only(top: 10), 255 | child: Row( 256 | children: [ 257 | const Icon( 258 | Icons.description, 259 | ), 260 | const SizedBox(width: 10), 261 | SizedBox( 262 | width: width * 0.7, 263 | child: Text( 264 | languageList[index].description ?? '', 265 | style: const TextStyle( 266 | fontSize: 16, 267 | ), 268 | textAlign: TextAlign.justify, 269 | ), 270 | ), 271 | ], 272 | ), 273 | ), 274 | ], 275 | ), 276 | ); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoCV 2 | 3 | Build Resume on the Go 4 | 5 | ## Table of Contents 6 | 7 | - [Introduction](#introduction) 8 | - [Features](#features) 9 | - [Installation](#installation) 10 | - [Usage](#usage) 11 | - [Dependencies](#dependencies) 12 | - [Contributing](#contributing) 13 | - [License](#license) 14 | - [Contact](#contact) 15 | 16 | ## Introduction 17 | 18 | This Flutter project aims to enable the people to build resumes easily. It is developed using Flutter framework, which allows for cross-platform mobile app development. 19 | 20 | ## Features 21 | 22 | ### Feature List 23 | 24 | - [x] **Resume Management**: 25 | - [x] Create, update, and delete resumes. 26 | - [x] **Personal Information**: 27 | - [x] Update personal details like name, address, etc. 28 | - [x] **Contact Information**: 29 | - [x] Update contact details such as email, phone number, etc. 30 | - [x] **Work Experience**: 31 | - [x] Add new Work Experience 32 | - [x] Update Work Experience 33 | - [x] Delete Work Experience 34 | - [x] **Education**: 35 | - [ ] Add new Education 36 | - [ ] Update Education 37 | - [ ] Delete Education 38 | - [ ] **Skills**: 39 | - [ ] Add new Skills 40 | - [ ] Update Skills 41 | - [ ] Delete Skills 42 | - [ ] **Projects**: 43 | - [ ] Add new Projects 44 | - [ ] Update Projects 45 | - [ ] Delete Projects 46 | - [ ] **Certifications**: 47 | - [ ] Add new Certifications 48 | - [ ] Update Certifications 49 | - [ ] Delete Certifications 50 | - [ ] **Awards**: 51 | - [ ] Add new Awards 52 | - [ ] Update Awards 53 | - [ ] Delete Awards 54 | - [ ] **Publications**: 55 | - [ ] Add new Publications 56 | - [ ] Update Publications 57 | - [ ] Delete Publications 58 | - [ ] **References**: 59 | - [ ] Add new References 60 | - [ ] Update References 61 | - [ ] Delete References 62 | - [ ] **Languages** 63 | - [ ] Add new Languages 64 | - [ ] Update Languages 65 | - [ ] Delete Languages: 66 | - [ ] **Interests**: 67 | - [ ] Add new Interests 68 | - [ ] Update Interests 69 | - [ ] Delete Interests 70 | - [x] **Preview Resume**: 71 | - [x] View a preview of the resume layout. 72 | - [x] **Export Resume as PDF**: 73 | - [x] Generate and export the resume as a PDF file. 74 | 75 | ### Additional Features 76 | 77 | - [x] **Multiple Language Support**: 78 | - [x] Bengali 79 | - [x] English 80 | - [x] **Theme Change** 81 | - [ ] **Customizable Templates**: 82 | - [ ] Provide different resume templates for users to choose from. 83 | - [ ] **Integration with LinkedIn/GitHub**: 84 | - [ ] Allow users to import data from their LinkedIn or GitHub profiles. 85 | - [ ] **Collaboration**: 86 | - [ ] Enable collaboration features for multiple users to work on a resume together. 87 | - [ ] **Version Control**: 88 | - [ ] Implement version control to track changes made to resumes over time. 89 | 90 | ## Installation 91 | 92 | To run this project locally, follow these steps: 93 | 94 | 1. Make sure you have Flutter SDK installed on your machine. If not, follow the official [Flutter installation guide](https://flutter.dev/docs/get-started/install) to set up Flutter. 95 | 2. Clone this repository to your local machine using the following command: 96 | ```shell 97 | git clone https://github.com/MeherajUlMahmmud/GoCV.git 98 | ``` 99 | 3. Navigate to the project directory: 100 | ```shell 101 | cd GoCV 102 | ``` 103 | 4. Fetch the project dependencies by running the following command: 104 | ```shell 105 | flutter pub get 106 | ``` 107 | 5. Connect your device or start an emulator. 108 | 109 | 6. Run the app using the following command: 110 | 111 | ```shell 112 | flutter gen-l10n 113 | flutter run 114 | ``` 115 | 116 | ## Usage 117 | 118 | Before using the Flutter app, please ensure that the [JobBoard REST API](https://github.com/MeherajUlMahmmud/JobBoardAPI) project is running and accessible. Follow these steps to set up the app correctly: 119 | 120 | 1. Start the REST API by running the appropriate command or script. Make sure the project is running on a server or local development environment. 121 | 122 | 2. Once the REST API is up and running, open the Flutter app's source code in your preferred code editor. 123 | 124 | 3. Locate the file or class responsible for making HTTP requests to the REST API. This is typically where the API client or services or URLs are defined. 125 | 126 | 4. Look for a variable or constant that represents the base URL of the API endpoints. It is usually defined as a string constant or assigned to a variable. 127 | 128 | 5. Update the value of the base URL to match the URL of your Django project's API. Ensure that you include the appropriate path and any necessary authentication or authorization headers if required by the REST API. 129 | 130 | Example: 131 | 132 | ```dart 133 | class URLS { 134 | // static const String kBaseUrl = "http://192.168.0.108:8000/"; 135 | // static const String kBaseUrl = "http://10.0.2.2:8000/"; 136 | static const String kBaseUrl = 137 | "http://127.0.0.1:8000/api/"; // for iOS simulator 138 | 139 | 140 | // Rest of your API client code... 141 | } 142 | ``` 143 | 144 | Replace `"http://127.0.0.1:8000/api/"` with the actual URL of your Django project's API. 145 | 146 | 6. Save the changes and ensure that the Flutter app's source code is updated with the correct base API URL. 147 | 148 | 7. Build and run the Flutter app on your device or emulator, and it should now be able to communicate with the REST API endpoints. 149 | 150 | By following these steps and updating the base API URL in your Flutter app, you can successfully utilize the functionalities provided by the Django project. This ensures proper communication between the Flutter app and the Django backend. 151 | 152 | ## Dependencies 153 | 154 | This project depends on the following packages: 155 | 156 | 1. `cupertino_icons: ^0.1.3`: This package provides the Cupertino Icons font, which includes a wide range of icons that follow the design principles of Apple's iOS platform. It allows you to easily include these icons in your Flutter app's UI. 157 | 158 | 2. `http: ^0.13.6`: The `http` package provides a set of high-level functions and classes for making HTTP requests in Flutter. It simplifies the process of sending HTTP requests and handling responses, enabling you to interact with web services and APIs. 159 | 160 | 3. `shared_preferences`: The `shared_preferences` package provides a simple way to store and retrieve key-value pairs on the device. It allows you to persist small amounts of data such as user preferences, settings, or cached data. The data is stored locally on the device using the platform's native storage mechanisms. 161 | 162 | 4. `permission_handler`: This package simplifies the process of requesting and managing permissions in a Flutter app. It provides a unified interface to handle various types of permissions, such as camera, microphone, location, and more. It helps you handle the permission request flow and provides convenient methods to check and request permissions. 163 | 164 | 5. `intl`: The `intl` package provides internationalization and localization support for Flutter apps. It allows you to localize your app's text, format dates, numbers, and currencies according to different locales. It provides tools to manage translations, pluralization, and other language-specific features. 165 | 166 | 6. `image_picker`: The `image_picker` package allows you to select images or videos from the device's gallery or capture them using the camera. It provides a simple interface to pick media files, allowing you to integrate image and video selection functionality into your app. 167 | 168 | 7. `image_cropper`: The `image_cropper` package provides a convenient way to crop images in a Flutter app. It allows users to select a portion of an image and crop it to the desired size or aspect ratio. It supports common image cropping features like rotation, zooming, and cropping aspect ratio customization. 169 | 170 | 8. `url_launcher: ^6.1.5`: The `url_launcher` package provides a simple way to launch external URLs or deep links from a Flutter app. It allows you to open web pages, email addresses, phone numbers, or launch other apps installed on the device by providing the corresponding URLs or deep links. 171 | 172 | 9. `printing: ^5.10.1`: The `printing` package provides utilities for printing documents and images from a Flutter app. It allows you to generate and print documents in various formats, such as PDF, and provides options for customizing the print output. 173 | 174 | 10. `flutter_typeahead: ^4.3.1`: The `flutter_typeahead` package provides an autocomplete or type-ahead feature for text input fields in a Flutter app. It suggests and displays a list of options based on the user's input, making it easier and faster for users to enter data. 175 | 176 | For more details, see the pubspec.yaml file. 177 | 178 | ## Screenshots 179 | 180 | - [Include screenshots or gifs to demonstrate your app's functionality] 181 | 182 | ## Contributing 183 | 184 | Contributions, bug reports, and feature requests are welcome! If you have any suggestions or improvements for the project, please follow these steps: 185 | 186 | 1. Fork the repository. 187 | 2. Create a new branch for your changes. 188 | 3. Implement your changes or additions. 189 | 4. Commit and push your changes to your forked repository. 190 | 5. Submit a pull request, describing the changes you made and the problem they solve. 191 | 6. Be prepared to address any feedback or questions during the review process. 192 | 193 | ## License 194 | 195 | This project is licensed under the [MIT License](LICENSE). 196 | 197 | ## Contact 198 | 199 | If you have any questions or need assistance, feel free to reach out to the project maintainer at `meherajmahmmd@gmail.com`. We appreciate your interest and support! 200 | 201 | Feel free to customize the content, update the placeholders, and add/remove sections as needed to suit your specific Flutter project. 202 | --------------------------------------------------------------------------------