From 443b64c703972e95e64c1d947935f52a06cd9159 Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Sun, 3 Apr 2022 12:44:16 +0300 Subject: [PATCH] Customer App Updates --- android/app/build.gradle | 4 +- android/build.gradle | 2 +- assets/images/bn_map.png | Bin 0 -> 43970 bytes assets/images/ic_car.svg | 11 + assets/images/ic_edit.svg | 4 + assets/images/ic_lock.svg | 3 + assets/images/ic_logout.svg | 11 + assets/images/ic_mobile.svg | 10 + assets/images/ic_notes.svg | 12 + assets/images/ic_notification.svg | 4 + assets/images/ic_settings.svg | 4 + lib/api/client/branch_api_client.dart | 69 ++++++ lib/api/client/profile_api_client.dart | 52 +++++ lib/api/client/user_api_client.dart | 205 ++++++++++++++++++ lib/api/user_api_client.dart | 91 -------- lib/classes/consts.dart | 32 ++- lib/config/routes.dart | 47 +++- lib/extensions/string_extensions.dart | 4 + lib/models/account.dart | 1 + lib/models/m_response.dart | 33 +++ lib/models/profile/branch.dart | 85 ++++++++ lib/models/profile/categroy.dart | 69 ++++++ lib/models/profile/document.dart | 83 +++++++ lib/models/profile/services.dart | 80 +++++++ lib/models/user/change_email.dart | 51 +++++ lib/models/user/change_mobile.dart | 51 +++++ lib/models/user/change_password.dart | 34 +++ lib/models/user/cities.dart | 65 ++++++ lib/models/user/confirm_email.dart | 51 +++++ lib/models/user/confirm_mobile.dart | 53 +++++ lib/models/user/confirm_password.dart | 2 - .../user/forget_password_otp_compare.dart | 2 - .../user/forget_password_otp_request.dart | 2 - lib/models/user/image_response.dart | 37 ++++ lib/models/user/role.dart | 65 ++++++ lib/models/user/verify_email.dart | 53 +++++ lib/models/user/verify_email_otp.dart | 27 +++ lib/pages/dashboard/dashboard_page.dart | 166 +++++++++++++- lib/pages/user/change_email_page.dart | 116 ++++++++++ lib/pages/user/change_mobile_page.dart | 119 ++++++++++ lib/pages/user/change_password_page.dart | 112 ++++++++++ lib/pages/user/complete_profile_page.dart | 125 +++++++++-- lib/pages/user/confirm_new_password.dart | 4 +- lib/pages/user/confirm_new_password_page.dart | 119 ++++++++++ lib/pages/user/edit_account_page.dart | 158 ++++++++++++++ .../user/forget_password_method_page.dart | 95 ++++++++ lib/pages/user/forget_password_page.dart | 150 +++++++++---- .../user/login_method_selection_page.dart | 17 +- lib/pages/user/login_verification_page.dart | 4 +- lib/pages/user/login_verify_account_page.dart | 4 +- lib/pages/user/login_with_password_page.dart | 104 +++++++-- lib/pages/user/register_page.dart | 60 +++-- lib/pages/user/register_selection_page.dart | 1 + lib/pages/user/vertify_password_page.dart | 162 ++++++++++++++ lib/utils/utils.dart | 32 ++- lib/widgets/tab/login_email_tab.dart | 70 ++++++ pubspec.lock | 79 ++++++- pubspec.yaml | 7 +- 58 files changed, 2858 insertions(+), 255 deletions(-) create mode 100644 assets/images/bn_map.png create mode 100644 assets/images/ic_car.svg create mode 100644 assets/images/ic_edit.svg create mode 100644 assets/images/ic_lock.svg create mode 100644 assets/images/ic_logout.svg create mode 100644 assets/images/ic_mobile.svg create mode 100644 assets/images/ic_notes.svg create mode 100644 assets/images/ic_notification.svg create mode 100644 assets/images/ic_settings.svg create mode 100644 lib/api/client/branch_api_client.dart create mode 100644 lib/api/client/profile_api_client.dart create mode 100644 lib/api/client/user_api_client.dart delete mode 100644 lib/api/user_api_client.dart create mode 100644 lib/models/m_response.dart create mode 100644 lib/models/profile/branch.dart create mode 100644 lib/models/profile/categroy.dart create mode 100644 lib/models/profile/document.dart create mode 100644 lib/models/profile/services.dart create mode 100644 lib/models/user/change_email.dart create mode 100644 lib/models/user/change_mobile.dart create mode 100644 lib/models/user/change_password.dart create mode 100644 lib/models/user/cities.dart create mode 100644 lib/models/user/confirm_email.dart create mode 100644 lib/models/user/confirm_mobile.dart create mode 100644 lib/models/user/image_response.dart create mode 100644 lib/models/user/role.dart create mode 100644 lib/models/user/verify_email.dart create mode 100644 lib/models/user/verify_email_otp.dart create mode 100644 lib/pages/user/change_email_page.dart create mode 100644 lib/pages/user/change_mobile_page.dart create mode 100644 lib/pages/user/change_password_page.dart create mode 100644 lib/pages/user/confirm_new_password_page.dart create mode 100644 lib/pages/user/edit_account_page.dart create mode 100644 lib/pages/user/forget_password_method_page.dart create mode 100644 lib/pages/user/vertify_password_page.dart create mode 100644 lib/widgets/tab/login_email_tab.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 47bcd00..5942955 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -45,7 +45,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.car_customer_app" minSdkVersion 16 - targetSdkVersion 30 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/build.gradle b/android/build.gradle index ed45c65..09fbd64 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() diff --git a/assets/images/bn_map.png b/assets/images/bn_map.png new file mode 100644 index 0000000000000000000000000000000000000000..b04b5aa34b49f37ab94d59b80b0866a9b5991d29 GIT binary patch literal 43970 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 literal 0 HcmV?d00001 diff --git a/assets/images/ic_car.svg b/assets/images/ic_car.svg new file mode 100644 index 0000000..804f607 --- /dev/null +++ b/assets/images/ic_car.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/ic_edit.svg b/assets/images/ic_edit.svg new file mode 100644 index 0000000..c14165f --- /dev/null +++ b/assets/images/ic_edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/ic_lock.svg b/assets/images/ic_lock.svg new file mode 100644 index 0000000..1273157 --- /dev/null +++ b/assets/images/ic_lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_logout.svg b/assets/images/ic_logout.svg new file mode 100644 index 0000000..2be107f --- /dev/null +++ b/assets/images/ic_logout.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/ic_mobile.svg b/assets/images/ic_mobile.svg new file mode 100644 index 0000000..068cf7a --- /dev/null +++ b/assets/images/ic_mobile.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/images/ic_notes.svg b/assets/images/ic_notes.svg new file mode 100644 index 0000000..009cf17 --- /dev/null +++ b/assets/images/ic_notes.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/images/ic_notification.svg b/assets/images/ic_notification.svg new file mode 100644 index 0000000..e45bab2 --- /dev/null +++ b/assets/images/ic_notification.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/ic_settings.svg b/assets/images/ic_settings.svg new file mode 100644 index 0000000..83b64c9 --- /dev/null +++ b/assets/images/ic_settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/api/client/branch_api_client.dart b/lib/api/client/branch_api_client.dart new file mode 100644 index 0000000..973e3bf --- /dev/null +++ b/lib/api/client/branch_api_client.dart @@ -0,0 +1,69 @@ +import 'dart:async'; +import 'package:car_customer_app/classes/app_state.dart'; +import 'package:car_customer_app/models/m_response.dart'; +import 'package:car_customer_app/models/profile/branch.dart'; +import 'package:car_customer_app/models/profile/categroy.dart'; +import 'package:car_customer_app/models/profile/document.dart'; +import 'package:car_customer_app/models/profile/services.dart'; +import 'package:car_customer_app/models/user/country.dart'; +import 'package:car_customer_app/models/user/role.dart'; +import 'package:http/http.dart'; +import 'package:car_customer_app/classes/consts.dart'; +import 'package:car_customer_app/models/content_info_model.dart'; +import 'package:car_customer_app/models/member_model.dart'; +import 'package:car_customer_app/models/surah_model.dart'; +import 'package:car_customer_app/models/user/basic_otp.dart'; +import 'package:car_customer_app/models/user/register_user.dart'; + +import '../api_client.dart'; + +class BranchApiClent { + static final BranchApiClent _instance = BranchApiClent._internal(); + + BranchApiClent._internal(); + + factory BranchApiClent() => _instance; + + Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude) async { + var postParams = { + // "id": 0, + "serviceProviderID": AppState().getUser.data?.userInfo?.providerId ?? "", + "branchName": branchName, + "branchDescription": branchDescription, + "cityID": cityId, + "address": address, + "latitude": latitude, + "longitude": longitude, + "isActive": true + }; + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createProviderBranch, postParams, token: t); + } + + Future fetchAllBranches() async { + var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().getJsonForObject((json) => Branch.fromJson(json), ApiConsts.ServiceProviderBranchGet, queryParameters: postParams, token: t); + } + + Future fetchBranchCategory() async { + var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().getJsonForObject((json) => Category.fromJson(json), ApiConsts.ServiceCategory_Get, queryParameters: postParams, token: t); + } + + Future fetchServices(String serviceCategoryId) async { + var postParams = {"ServiceCategoryID": serviceCategoryId}; + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().getJsonForObject((json) => Services.fromJson(json), ApiConsts.Services_Get, queryParameters: postParams, token: t); + } + + Future createService(List> map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderService_Create, map, token: t); + } +} diff --git a/lib/api/client/profile_api_client.dart b/lib/api/client/profile_api_client.dart new file mode 100644 index 0000000..de796b0 --- /dev/null +++ b/lib/api/client/profile_api_client.dart @@ -0,0 +1,52 @@ +import 'dart:async'; +import 'package:car_customer_app/classes/app_state.dart'; +import 'package:car_customer_app/models/m_response.dart'; +import 'package:car_customer_app/models/profile/document.dart'; +import 'package:car_customer_app/models/user/country.dart'; +import 'package:car_customer_app/models/user/role.dart'; +import 'package:http/http.dart'; +import 'package:car_customer_app/classes/consts.dart'; +import 'package:car_customer_app/models/content_info_model.dart'; +import 'package:car_customer_app/models/member_model.dart'; +import 'package:car_customer_app/models/surah_model.dart'; +import 'package:car_customer_app/models/user/basic_otp.dart'; +import 'package:car_customer_app/models/user/register_user.dart'; + +import '../api_client.dart'; + +class ProfileApiClent { + static final ProfileApiClent _instance = ProfileApiClent._internal(); + + ProfileApiClent._internal(); + + factory ProfileApiClent() => _instance; + + Future getServiceProviderDocument(dynamic userId) async { + var queryParameters = { + "ServiceProviderID": userId.toString(), + }; + String? token = AppState().getUser.data?.accessToken; + print(token); + return await ApiClient().getJsonForObject((json) => Document.fromJson(json), ApiConsts.GetProviderDocument, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); + } + + Future serviceProviderDocumentsUpdate(List? documents) async { + List> map = []; + for (int i = 0; i < documents!.length; i++) { + if (documents[i].document != null) { + var postParams = { + "id": documents[i].id, + "serviceProviderID": documents[i].serviceProviderId, + "documentID": documents[i].documentId, + "documentExt": documents[i].fileExt, + "documentImage": documents[i].document, + "isActive": true + }; + map.add(postParams); + } + } + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderDocument_Update, map, token: t); + } +} diff --git a/lib/api/client/user_api_client.dart b/lib/api/client/user_api_client.dart new file mode 100644 index 0000000..087fbcb --- /dev/null +++ b/lib/api/client/user_api_client.dart @@ -0,0 +1,205 @@ +import 'dart:async'; +import 'package:car_customer_app/models/user/change_email.dart'; +import 'package:car_customer_app/models/user/change_mobile.dart'; +import 'package:car_customer_app/models/user/cities.dart'; +import 'package:car_customer_app/models/user/confirm_email.dart'; +import 'package:car_customer_app/models/user/confirm_mobile.dart'; +import 'package:car_customer_app/models/user/country.dart'; +import 'package:car_customer_app/models/user/image_response.dart'; +import 'package:car_customer_app/models/user/role.dart'; +import 'package:car_customer_app/models/user/confirm_password.dart'; +import 'package:car_customer_app/models/user/forget_password_otp_compare.dart'; +import 'package:car_customer_app/models/user/forget_password_otp_request.dart'; +import 'package:car_customer_app/models/user/verify_email.dart'; +import 'package:car_customer_app/models/user/verify_email_otp.dart'; +import 'package:http/http.dart'; +import 'package:car_customer_app/classes/consts.dart'; +import 'package:car_customer_app/models/content_info_model.dart'; +import 'package:car_customer_app/models/member_model.dart'; +import 'package:car_customer_app/models/surah_model.dart'; +import 'package:car_customer_app/models/user/basic_otp.dart'; +import 'package:car_customer_app/models/user/register_user.dart'; + +import '../../classes/app_state.dart'; +import '../../models/m_response.dart'; +import '../api_client.dart'; + + + +class UserApiClent { + static final UserApiClent _instance = UserApiClent._internal(); + + UserApiClent._internal(); + + factory UserApiClent() => _instance; + + Future basicOtp(String phoneNo, {int otpType = 1, int roleId = 4, int countryId = 1}) async { + var postParams = {"countryID": countryId, "userMobileNo": phoneNo, "otpType": otpType, "userRole": roleId}; + return await ApiClient().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.BasicOTP, postParams); + } + + Future basicVerify(String phoneNo, String otp, String userToken) async { + var postParams = { + "userMobileNo": phoneNo, + "userOTP": otp, + "userToken": userToken, + }; + return await ApiClient().postJsonForObject((json) => RegisterUser.fromJson(json), ApiConsts.BasicVerify, postParams); + } + + Future basicComplete(String userId, String firstName, String lastName, String email, String password) async { + var postParams; + if(email.isEmpty){ + postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "companyName": "string", "isEmailVerified": true, "password": password}; + }else{ + postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "email": email, "companyName": "string", "isEmailVerified": true, "password": password}; + } + + return await ApiClient().postJsonForObject((json) => RegisterUser.fromJson(json), ApiConsts.BasicComplete, postParams); + } + + Future login_V1(String phoneNo, String password) async { + var postParams = { + "mobileorEmail": phoneNo, + "password": password, + }; + return await ApiClient().postJsonForResponse(ApiConsts.Login_V1, postParams); + //return await ApiClient().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.Login_V1, postParams); + } + + Future login_V2_OTP(String userToken, String loginType) async { + var postParams = { + "userToken": userToken, + "loginType": loginType, + }; + return await ApiClient().postJsonForResponse(ApiConsts.Login_V2_OTP, postParams); + } + + Future login_V2_OTPVerify(String userToken, String otp) async { + var postParams = {"userToken": userToken, "userOTP": otp}; + return await ApiClient().postJsonForResponse(ApiConsts.Login_V2_OTPVerify, postParams); + } + + Future getAllCountries() async { + var postParams = {}; + return await ApiClient().getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry); + } + + Future getAllCites(String countryId) async { + var postParams = { + "CountryID": countryId, + }; + return await ApiClient().getJsonForObject((json) => Cities.fromJson(json), ApiConsts.GetAllCities, queryParameters: postParams); + } + + Future getRoles() async { + return await ApiClient().getJsonForObject((json) => Role.fromJson(json), ApiConsts.GetProviderRoles); + } + + Future ForgetPasswordOTPRequest(String userName, int otpType) async { + var postParams = { + "userName": userName, + "otpType": 1, + }; + return await ApiClient().postJsonForResponse(ApiConsts.ForgetPasswordOTPRequest, postParams); + //return await ApiClient().postJsonForObject((json) => PasswordOTPRequest.fromJson(json), ApiConsts.ForgetPasswordOTPRequest, postParams); + } + + Future ForgetPasswordOTPCompare(String userToken, String userOTP) async { + var postParams = {"userToken": userToken, "userOTP": userOTP}; + return await ApiClient().postJsonForResponse(ApiConsts.ForgetPasswordOTPCompare, postParams); + // return await ApiClient().postJsonForObject((json) => PasswordOTPCompare.fromJson(json), ApiConsts.ForgetPasswordOTPCompare, postParams); + } + + Future ForgetPassword(String userToken, String newPassword) async { + var postParams = { + "userToken": userToken, + "newPassword": newPassword, + }; + return await ApiClient().postJsonForResponse(ApiConsts.ForgetPassword, postParams); + //return await ApiClient().postJsonForObject((json) => ConfirmPassword.fromJson(json), ApiConsts.ForgetPassword, postParams); + } + + Future ChangePassword(String currentPasswor, String newPassword) async { + var postParams = { + "currentPassword": currentPasswor, + "newPassword": newPassword, + }; + // return await ApiClient().postJsonForResponse(ApiConsts.ChangePassword, postParams); + + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ChangePassword, postParams, token: t); + + } + + Future ChangeMobileNoOTPRequest( + countryID, + String mobileNo, + String password, + ) async { + var postParams = {"countryID": 1, "mobileNo": mobileNo, "password": password}; + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => ChangeMobile.fromJson(json), ApiConsts.ChangeMobileNoOTPRequest, postParams, token: t); + } + + Future ChangeMobileNo(String userToken, String userOTP) async { + var postParams = { + "userToken": userToken, + "userOTP": userOTP, + }; + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => ConfirmMobile.fromJson(json), ApiConsts.ChangeMobileNo, postParams, token: t); + } + + Future ChangeEmailOTPRequest(String email, String password) async { + var postParams = {"email": email, "password": password}; + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => ChanEmail.fromJson(json), ApiConsts.ChangeEmailOTPRequest, postParams, token: t); + } + + Future ChangeEmail(String userToken, String userOTP) async { + var postParams = {"userToken": userToken, "userOTP": userOTP}; + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => ConfirmEmail.fromJson(json), ApiConsts.ChangeEmail, postParams, token: t); + } + + Future EmailVerify(String email, String userID) async { + var postParams = { + // "email": email, + // "userID": userID, + "email": AppState().getUser.data!.userInfo!.email ?? "", + "userID": AppState().getUser.data!.userInfo!.userId ?? "", + }; + + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => VerifyEmail.fromJson(json), ApiConsts.EmailVerify, postParams, token: t); + } + + Future EmailVerifyOTPVerify(String userToken, String userOTP) async { + var postParams = + {"userToken": userToken, + "userOTP": userOTP}; + + String t = AppState().getUser.data!.accessToken ?? ""; + return await ApiClient().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.EmailVerifyOTPVerify, postParams, token: t); + } + + Future UpdateUserImage(String image) async { + var postParams = {"userID": AppState().getUser.data!.userInfo!.userId, "userImage": image}; + // return await ApiClient().postJsonForResponse(ApiConsts.ChangePassword, postParams); + + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t); + } + + Future GetUserImage(String image) async { + var postParams = {}; + // return await ApiClient().postJsonForResponse(ApiConsts.ChangePassword, postParams); + + String t = AppState().getUser.data!.accessToken ?? ""; + print("tokeen " + t); + return await ApiClient().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t); + } +} diff --git a/lib/api/user_api_client.dart b/lib/api/user_api_client.dart deleted file mode 100644 index 1d644ef..0000000 --- a/lib/api/user_api_client.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; -import 'package:car_customer_app/models/user/country.dart'; -import 'package:http/http.dart'; -import 'package:car_customer_app/classes/consts.dart'; -import 'package:car_customer_app/models/content_info_model.dart'; -import 'package:car_customer_app/models/member_model.dart'; -import 'package:car_customer_app/models/surah_model.dart'; -import 'package:car_customer_app/models/user/basic_otp.dart'; -import 'package:car_customer_app/models/user/register_user.dart'; - -import 'api_client.dart'; - -class UserApiClent { - static final UserApiClent _instance = UserApiClent._internal(); - - UserApiClent._internal(); - - factory UserApiClent() => _instance; - - Future basicOtp(String phoneNo, {int otpType = 1}) async { - var postParams = {"countryID": 1, "userMobileNo": phoneNo, "otpType": otpType, "userRole": 4}; - return await ApiClient().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.BasicOTP, postParams); - } - - Future basicVerify(String phoneNo, String otp, String userToken) async { - var postParams = { - "userMobileNo": phoneNo, - "userOTP": otp, - "userToken": userToken, - }; - return await ApiClient().postJsonForObject((json) => RegisterUser.fromJson(json), ApiConsts.BasicVerify, postParams); - } - - Future basicComplete(String userId, String firstName, String lastName, String email, String password) async { - var postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "email": email, "companyName": "string", "isEmailVerified": true, "password": password}; - return await ApiClient().postJsonForObject((json) => RegisterUser.fromJson(json), ApiConsts.BasicComplete, postParams); - } - - Future login_V1(String phoneNo, String password) async { - var postParams = { - "mobileorEmail": phoneNo, - "password": password, - }; - return await ApiClient().postJsonForResponse(ApiConsts.Login_V1, postParams); - //return await ApiClient().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.Login_V1, postParams); - } - - Future login_V2_OTP(String userToken, String loginType) async { - var postParams = { - "userToken": userToken, - "loginType": loginType, - }; - return await ApiClient().postJsonForResponse(ApiConsts.Login_V2_OTP, postParams); - } - - Future login_V2_OTPVerify(String userToken, String otp) async { - var postParams = {"userToken": userToken, "userOTP": otp}; - return await ApiClient().postJsonForResponse(ApiConsts.Login_V2_OTPVerify, postParams); - } - - Future getAllCountries() async { - var postParams = {}; - return await ApiClient().getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry); - } - - - Future ForgetPasswordOTPRequest(String userName, int otpType) async { - var postParams = { - "userName": userName, - "otpType": 1, - }; - return await ApiClient().postJsonForResponse(ApiConsts.ForgetPasswordOTPRequest, postParams); - } - - Future ForgetPasswordOTPCompare(String userToken, String otp) async { - var postParams = - {"userToken": userToken, - "userOTP": otp}; - return await ApiClient().postJsonForResponse(ApiConsts.ForgetPasswordOTPCompare, postParams); - - } - - Future ForgetPassword(String userToken, String newPassword) async { - var postParams = { - "userToken": userToken, - "newPassword": newPassword, - }; - return await ApiClient().postJsonForResponse(ApiConsts.ForgetPassword, postParams); - - } -} diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 09b8ad8..bda0cac 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -1,24 +1,48 @@ class ApiConsts { - //static String baseUrl = "http://10.200.204.20:2801/"; // Local server + // static String baseUrl = "http://10.200.204.20:2801/"; // Local server static String baseUrl = "https://mdlaboratories.com"; // production server static String baseUrlServices = baseUrl + "/mc/"; // production server - // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server static String BasicOTP = baseUrlServices + "api/Register/BasicOTP"; static String BasicVerify = baseUrlServices + "api/Register/BasicVerify"; static String BasicComplete = baseUrlServices + "api/Register/BasicComplete"; + + //User static String Login_V1 = baseUrlServices + "api/Account/Login_V1"; static String Login_V2_OTP = baseUrlServices + "api/Account/Login_V2_OTP"; static String Login_V2_OTPVerify = baseUrlServices + "api/Account/Login_V2_OTPVerify"; static String user = baseUrlServices + "api/User/"; static String GetAllCountry = baseUrlServices + "api/Master/Country_Get"; - + static String GetProviderRoles = baseUrlServices + "api/Master/RoleServiceProvider_Get"; + static String GetAllCities = baseUrlServices + "api/Master/City_Get"; static String ForgetPasswordOTPRequest = baseUrlServices + "api/Account/ForgetPasswordOTPRequest"; static String ForgetPasswordOTPCompare = baseUrlServices + "api/Account/ForgetPasswordOTPCompare"; static String ForgetPassword = baseUrlServices + "api/Account/ForgetPassword"; -} + static String Login_Email_OTP = baseUrlServices + "api/Account/EmailVerify"; + static String Login_Email_OTPVerify = baseUrlServices + "api/Account/EmailVerifyOTPVerify"; + static String ChangePassword = baseUrlServices + "api/Account/ChangePassword"; + static String ChangeMobileNoOTPRequest = baseUrlServices + "api/Account/ChangeMobileNoOTPRequest"; + static String ChangeMobileNo = baseUrlServices + "api/Account/ChangeMobileNo"; + static String ChangeEmailOTPRequest = baseUrlServices + "api/Account/ChangeEmailOTPRequest"; + static String ChangeEmail = baseUrlServices + "api/Account/ChangeEmail"; + static String EmailVerify = baseUrlServices + "api/Account/EmailVerify"; + static String EmailVerifyOTPVerify = baseUrlServices + "api/Account/EmailVerifyOTPVerify"; + static String UpdateUserImage = baseUrlServices + "api/User_UpdateProfileImage"; + static String GetUserImage = baseUrlServices + "api/ProfileImage"; + //Profile + static String GetProviderDocument = baseUrlServices + "api/ServiceProviders/ServiceProviderDocument_Get"; + static String ServiceProviderDocument_Update = baseUrlServices + "api/ServiceProviders/ServiceProviderDocument_Update"; + + //Branch + static String createProviderBranch = baseUrlServices + "api/ServiceProviders/ServiceProviderBranch_Create"; + static String ServiceProviderBranchGet = baseUrlServices + "api/ServiceProviders/ServiceProviderBranch_Get"; + static String ServiceCategory_Get = baseUrlServices + "api/Master/ServiceCategory_Get"; + static String Services_Get = baseUrlServices + "api/ServiceProviders/Services_Get"; + static String ServiceProviderService_Create = baseUrlServices + "api/ServiceProviders/ServiceProviderService_Create"; +} + class GlobalConsts { static String isRememberMe = "remember_me"; static String email = "email"; diff --git a/lib/config/routes.dart b/lib/config/routes.dart index ab9223c..d74fc0b 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,16 +1,29 @@ import 'package:car_customer_app/models/user/register_user.dart'; import 'package:car_customer_app/pages/dashboard/dashboard_page.dart'; +// import 'package:car_customer_app/pages/settings/create_services_page.dart'; +// import 'package:car_customer_app/pages/settings/dealership_page.dart'; +// import 'package:car_customer_app/pages/settings/define_branch_page.dart'; +// import 'package:car_customer_app/pages/settings/define_license_page.dart'; +import 'package:car_customer_app/pages/user/change_email_page.dart'; +import 'package:car_customer_app/pages/user/change_mobile_page.dart'; +import 'package:car_customer_app/pages/user/change_password_page.dart'; import 'package:car_customer_app/pages/user/complete_profile_page.dart'; +import 'package:car_customer_app/pages/user/confirm_new_password_page.dart'; +import 'package:car_customer_app/pages/user/edit_account_page.dart'; +import 'package:car_customer_app/pages/user/forget_password_method_page.dart'; import 'package:car_customer_app/pages/user/forget_password_page.dart'; import 'package:car_customer_app/pages/user/login_method_selection_page.dart'; import 'package:car_customer_app/pages/user/login_verification_page.dart'; import 'package:car_customer_app/pages/user/login_verify_account_page.dart'; import 'package:car_customer_app/pages/user/login_with_password_page.dart'; +// import 'package:car_customer_app/pages/user/profile/profile_1_page.dart'; +// import 'package:car_customer_app/pages/user/profile/profile_2_page.dart'; +// import 'package:car_customer_app/pages/user/profile/profile_3_page.dart'; import 'package:car_customer_app/pages/user/register_page.dart'; import 'package:car_customer_app/pages/user/register_selection_page.dart'; import 'package:car_customer_app/pages/user/splash_page.dart'; +import 'package:car_customer_app/pages/user/vertify_password_page.dart'; import 'package:flutter/material.dart'; -import 'package:car_customer_app/pages/user/confirm_new_password.dart'; class AppRoutes { //User @@ -23,11 +36,25 @@ class AppRoutes { static final String loginWithPassword = "/loginWithPassword"; static final String loginMethodSelection = "/loginMethodSelection"; static final String completeProfile = "/completeProfile"; - + static final String profile1 = "/profile1"; + static final String profile2 = "/profile2"; + static final String profile3 = "/profile3"; + static final String vertifyPassword = "/vertifyPassword"; static final String confirmNewPasswordPage = "/confirmNewPasswordPage"; + static final String defineLicense = "/defineLicese"; + static final String changePassword = "/changePassword"; + static final String forgetPasswordMethodPage = "/forgetPasswordMethodPage"; + static final String changeMobilePage = "/changeMobilePage"; + static final String changeEmailPage = "/changeEmailPage"; + static final String editAccoundPage = "/editAccoundPage"; static final String dashboard = "/dashboard"; + //settings + static final String dealershipSetting = "/dealershipSetting"; + static final String defineBranch = "/defineBranch"; + static final String createServices = "/createServices"; + static final String initialRoute = splash; static final Map routes = { @@ -41,9 +68,23 @@ class AppRoutes { loginWithPassword: (context) => LoginWithPassword(), loginMethodSelection: (context) => LoginMethodSelectionPage(ModalRoute.of(context)!.settings.arguments as String), completeProfile: (context) => CompleteProfilePage(ModalRoute.of(context)!.settings.arguments as RegisterUser), + // profile1: (context) => Profile1Page(), + // profile2: (context) => Profile2Page(), + // profile3: (context) => Profile3Page(), + // defineLicense: (context) => DefineLicensePage(), + vertifyPassword: (context) => VerifyPasswordPage(), confirmNewPasswordPage: (context) => ConfirmNewPasswordPage(ModalRoute.of(context)!.settings.arguments as String), - + changePassword: (context) => ChangePasswordPage(), + forgetPasswordMethodPage: (context) => ForgetPasswordMethodPage(ModalRoute.of(context)!.settings.arguments as String), + changeMobilePage: (context) => ChangeMobilePage(), + changeEmailPage : (context) => ChangeEmailPage(), + editAccoundPage : (context) => EditAccountPage(), //Home page dashboard: (context) => DashboardPage(), + + //setting + // dealershipSetting: (context) => DealershipPage(), + // defineBranch: (context) => DefineBranchPage(), + // createServices: (context) => CreateServicesPage(), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 9b8fc19..dbcde00 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -76,6 +76,10 @@ extension EmailValidator on String { return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this); } + bool isNum() { + return RegExp(r'[0-9]').hasMatch(this); + } + String toFormattedDate() { String date = this.split("T")[0]; String time = this.split("T")[1]; diff --git a/lib/models/account.dart b/lib/models/account.dart index 8657751..1cfb548 100644 --- a/lib/models/account.dart +++ b/lib/models/account.dart @@ -8,6 +8,7 @@ import 'package:car_customer_app/models/parent_list.dart'; + Account accountFromJson(String str) => Account.fromJson(json.decode(str)); String accountToJson(Account data) => json.encode(data.toJson()); diff --git a/lib/models/m_response.dart b/lib/models/m_response.dart new file mode 100644 index 0000000..bd9d3a1 --- /dev/null +++ b/lib/models/m_response.dart @@ -0,0 +1,33 @@ +// To parse this JSON data, do +// +// final mResponse = mResponseFromJson(jsonString); + +import 'dart:convert'; + +MResponse mResponseFromJson(String str) => MResponse.fromJson(json.decode(str)); + +String mResponseToJson(MResponse data) => json.encode(data.toJson()); + +class MResponse { + MResponse({ + this.totalItemsCount, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + int? messageStatus; + String? message; + + factory MResponse.fromJson(Map json) => MResponse( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} diff --git a/lib/models/profile/branch.dart b/lib/models/profile/branch.dart new file mode 100644 index 0000000..cc7d68a --- /dev/null +++ b/lib/models/profile/branch.dart @@ -0,0 +1,85 @@ +// To parse this JSON data, do +// +// final branch = branchFromJson(jsonString); + +import 'dart:convert'; + +Branch branchFromJson(String str) => Branch.fromJson(json.decode(str)); + +String branchToJson(Branch data) => json.encode(data.toJson()); + +class Branch { + Branch({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Branch.fromJson(Map json) => Branch( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => BranchData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class BranchData { + BranchData({ + this.id, + this.serviceProviderId, + this.branchName, + this.branchDescription, + this.cityId, + this.address, + this.latitude, + this.longitude, + this.status, + }); + + int? id; + int? serviceProviderId; + String? branchName; + String? branchDescription; + int? cityId; + String? address; + String? latitude; + String? longitude; + int? status; + + factory BranchData.fromJson(Map json) => BranchData( + id: json["id"] == null ? null : json["id"], + serviceProviderId: json["serviceProviderID"] == null ? null : json["serviceProviderID"], + branchName: json["branchName"] == null ? null : json["branchName"], + branchDescription: json["branchDescription"] == null ? null : json["branchDescription"], + cityId: json["cityID"] == null ? null : json["cityID"], + address: json["address"] == null ? null : json["address"], + latitude: json["latitude"] == null ? null : json["latitude"], + longitude: json["longitude"] == null ? null : json["longitude"], + status: json["status"] == null ? null : json["status"], + ); + + Map toJson() => { + "id": id == null ? null : id, + "serviceProviderID": serviceProviderId == null ? null : serviceProviderId, + "branchName": branchName == null ? null : branchName, + "branchDescription": branchDescription == null ? null : branchDescription, + "cityID": cityId == null ? null : cityId, + "address": address == null ? null : address, + "latitude": latitude == null ? null : latitude, + "longitude": longitude == null ? null : longitude, + "status": status == null ? null : status, + }; +} diff --git a/lib/models/profile/categroy.dart b/lib/models/profile/categroy.dart new file mode 100644 index 0000000..6f566f1 --- /dev/null +++ b/lib/models/profile/categroy.dart @@ -0,0 +1,69 @@ +// To parse this JSON data, do +// +// final category = categoryFromJson(jsonString); + +import 'dart:convert'; + +Category categoryFromJson(String str) => Category.fromJson(json.decode(str)); + +String categoryToJson(Category data) => json.encode(data.toJson()); + +class Category { + Category({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Category.fromJson(Map json) => Category( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => CategoryData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class CategoryData { + CategoryData({ + this.id, + this.categoryName, + this.categoryNameN, + this.serviceCategoryIconUrl, + this.serviceCategoryImageUrl, + }); + + int? id; + String? categoryName; + String? categoryNameN; + dynamic? serviceCategoryIconUrl; + dynamic? serviceCategoryImageUrl; + + factory CategoryData.fromJson(Map json) => CategoryData( + id: json["id"] == null ? null : json["id"], + categoryName: json["categoryName"] == null ? null : json["categoryName"], + categoryNameN: json["categoryNameN"] == null ? null : json["categoryNameN"], + serviceCategoryIconUrl: json["serviceCategoryIconUrl"], + serviceCategoryImageUrl: json["serviceCategoryImageUrl"], + ); + + Map toJson() => { + "id": id == null ? null : id, + "categoryName": categoryName == null ? null : categoryName, + "categoryNameN": categoryNameN == null ? null : categoryNameN, + "serviceCategoryIconUrl": serviceCategoryIconUrl, + "serviceCategoryImageUrl": serviceCategoryImageUrl, + }; +} diff --git a/lib/models/profile/document.dart b/lib/models/profile/document.dart new file mode 100644 index 0000000..da082b3 --- /dev/null +++ b/lib/models/profile/document.dart @@ -0,0 +1,83 @@ +// To parse this JSON data, do +// +// final document = documentFromJson(jsonString); + +import 'dart:convert'; + +Document documentFromJson(String str) => Document.fromJson(json.decode(str)); + +String documentToJson(Document data) => json.encode(data.toJson()); + +class Document { + Document({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Document.fromJson(Map json) => Document( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => DocumentData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class DocumentData { + DocumentData({ + this.id, + this.serviceProviderId, + this.documentId, + this.documentUrl, + this.status, + this.comment, + this.isActive, + this.document, + this.fileExt, + }); + + int? id; + int? serviceProviderId; + int? documentId; + dynamic? documentUrl; + int? status; + dynamic? comment; + bool? isActive; + String? document; + String? fileExt; + + factory DocumentData.fromJson(Map json) => DocumentData( + id: json["id"] == null ? null : json["id"], + serviceProviderId: json["serviceProviderID"] == null ? null : json["serviceProviderID"], + documentId: json["documentID"] == null ? null : json["documentID"], + documentUrl: json["documentURL"], + status: json["status"] == null ? null : json["status"], + comment: json["comment"], + isActive: json["isActive"] == null ? null : json["isActive"], + document: null, + fileExt: null, + ); + + Map toJson() => { + "id": id == null ? null : id, + "serviceProviderID": serviceProviderId == null ? null : serviceProviderId, + "documentID": documentId == null ? null : documentId, + "documentURL": documentUrl, + "status": status == null ? null : status, + "comment": comment, + "isActive": isActive == null ? null : isActive, + }; +} diff --git a/lib/models/profile/services.dart b/lib/models/profile/services.dart new file mode 100644 index 0000000..d296300 --- /dev/null +++ b/lib/models/profile/services.dart @@ -0,0 +1,80 @@ +// To parse this JSON data, do +// +// final services = servicesFromJson(jsonString); + +import 'dart:convert'; + +Services servicesFromJson(String str) => Services.fromJson(json.decode(str)); + +String servicesToJson(Services data) => json.encode(data.toJson()); + +class Services { + Services({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Services.fromJson(Map json) => Services( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => ServicesData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class ServicesData { + ServicesData({ + this.id, + this.description, + this.descriptionN, + this.serviceIconUrl, + this.serviceImageUrl, + this.serviceCategoryId, + this.categoryName, + this.isSelected, + }); + + int? id; + String? description; + String? descriptionN; + dynamic? serviceIconUrl; + dynamic? serviceImageUrl; + int? serviceCategoryId; + dynamic? categoryName; + bool? isSelected; + + factory ServicesData.fromJson(Map json) => ServicesData( + id: json["id"] == null ? null : json["id"], + description: json["description"] == null ? null : json["description"], + descriptionN: json["descriptionN"] == null ? null : json["descriptionN"], + serviceIconUrl: json["serviceIconUrl"], + serviceImageUrl: json["serviceImageUrl"], + serviceCategoryId: json["serviceCategoryID"] == null ? null : json["serviceCategoryID"], + categoryName: json["categoryName"], + isSelected: false, + ); + + Map toJson() => { + "id": id == null ? null : id, + "description": description == null ? null : description, + "descriptionN": descriptionN == null ? null : descriptionN, + "serviceIconUrl": serviceIconUrl, + "serviceImageUrl": serviceImageUrl, + "serviceCategoryID": serviceCategoryId == null ? null : serviceCategoryId, + "categoryName": categoryName, + }; +} diff --git a/lib/models/user/change_email.dart b/lib/models/user/change_email.dart new file mode 100644 index 0000000..f7f6af5 --- /dev/null +++ b/lib/models/user/change_email.dart @@ -0,0 +1,51 @@ + + +import 'dart:convert'; + +ChanEmail changeEmailFromJson(String str) => ChanEmail.fromJson(json.decode(str)); + +String changeEmailToJson(ChanEmail data) => json.encode(data.toJson()); + +class ChanEmail { + int? messageStatus; + Null? totalItemsCount; + Data? data; + String? message; + + ChanEmail( + {this.messageStatus, this.totalItemsCount, this.data, this.message}); + + ChanEmail.fromJson(Map json) { + messageStatus = json['messageStatus']; + totalItemsCount = json['totalItemsCount']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['messageStatus'] = this.messageStatus; + data['totalItemsCount'] = this.totalItemsCount; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + data['message'] = this.message; + return data; + } +} + +class Data { + String? userToken; + + Data({this.userToken}); + + Data.fromJson(Map json) { + userToken = json['userToken']; + } + + Map toJson() { + final Map data = new Map(); + data['userToken'] = this.userToken; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/change_mobile.dart b/lib/models/user/change_mobile.dart new file mode 100644 index 0000000..fed5923 --- /dev/null +++ b/lib/models/user/change_mobile.dart @@ -0,0 +1,51 @@ + + +import 'dart:convert'; + +ChangeMobile changeMobileFromJson(String str) => ChangeMobile.fromJson(json.decode(str)); + +String changeMobileToJson(ChangeMobile data) => json.encode(data.toJson()); + +class ChangeMobile { + int? messageStatus; + Null? totalItemsCount; + Data? data; + String? message; + + ChangeMobile( + {this.messageStatus, this.totalItemsCount, this.data, this.message}); + + ChangeMobile.fromJson(Map json) { + messageStatus = json['messageStatus']; + totalItemsCount = json['totalItemsCount']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['messageStatus'] = this.messageStatus; + data['totalItemsCount'] = this.totalItemsCount; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + data['message'] = this.message; + return data; + } +} + +class Data { + String? userToken; + + Data({this.userToken}); + + Data.fromJson(Map json) { + userToken = json['userToken']; + } + + Map toJson() { + final Map data = new Map(); + data['userToken'] = this.userToken; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/change_password.dart b/lib/models/user/change_password.dart new file mode 100644 index 0000000..fc74d4b --- /dev/null +++ b/lib/models/user/change_password.dart @@ -0,0 +1,34 @@ + + + +import 'dart:convert'; + +ChangePassword changePasswordFromJson(String str) => ChangePassword.fromJson(json.decode(str)); + +String changePasswordToJson(ChangePassword data) => json.encode(data.toJson()); + +class ChangePassword { + int? messageStatus; + Null? totalItemsCount; + bool? data; + String? message; + + ChangePassword( + {this.messageStatus, this.totalItemsCount, this.data, this.message}); + + ChangePassword.fromJson(Map json) { + messageStatus = json['messageStatus']; + totalItemsCount = json['totalItemsCount']; + data = json['data']; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['messageStatus'] = this.messageStatus; + data['totalItemsCount'] = this.totalItemsCount; + data['data'] = this.data; + data['message'] = this.message; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/cities.dart b/lib/models/user/cities.dart new file mode 100644 index 0000000..5c32405 --- /dev/null +++ b/lib/models/user/cities.dart @@ -0,0 +1,65 @@ +// To parse this JSON data, do +// +// final cities = citiesFromJson(jsonString); + +import 'dart:convert'; + +Cities citiesFromJson(String str) => Cities.fromJson(json.decode(str)); + +String citiesToJson(Cities data) => json.encode(data.toJson()); + +class Cities { + Cities({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Cities.fromJson(Map json) => Cities( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => CityData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class CityData { + CityData({ + this.id, + this.cityName, + this.cityNameN, + this.countryId, + }); + + int? id; + String? cityName; + String? cityNameN; + int? countryId; + + factory CityData.fromJson(Map json) => CityData( + id: json["id"] == null ? null : json["id"], + cityName: json["cityName"] == null ? null : json["cityName"], + cityNameN: json["cityNameN"] == null ? null : json["cityNameN"], + countryId: json["countryID"] == null ? null : json["countryID"], + ); + + Map toJson() => { + "id": id == null ? null : id, + "cityName": cityName == null ? null : cityName, + "cityNameN": cityNameN == null ? null : cityNameN, + "countryID": countryId == null ? null : countryId, + }; +} diff --git a/lib/models/user/confirm_email.dart b/lib/models/user/confirm_email.dart new file mode 100644 index 0000000..e64dc25 --- /dev/null +++ b/lib/models/user/confirm_email.dart @@ -0,0 +1,51 @@ + + +import 'dart:convert'; + +ConfirmEmail confirmEmailFromJson(String str) => ConfirmEmail.fromJson(json.decode(str)); + +String confirmEmailToJson(ConfirmEmail data) => json.encode(data.toJson()); + +class ConfirmEmail { + int? messageStatus; + Null? totalItemsCount; + Data? data; + String? message; + + ConfirmEmail( + {this.messageStatus, this.totalItemsCount, this.data, this.message}); + + ConfirmEmail.fromJson(Map json) { + messageStatus = json['messageStatus']; + totalItemsCount = json['totalItemsCount']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['messageStatus'] = this.messageStatus; + data['totalItemsCount'] = this.totalItemsCount; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + data['message'] = this.message; + return data; + } +} + +class Data { + String? userID; + + Data({this.userID}); + + Data.fromJson(Map json) { + userID = json['userID']; + } + + Map toJson() { + final Map data = new Map(); + data['userID'] = this.userID; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/confirm_mobile.dart b/lib/models/user/confirm_mobile.dart new file mode 100644 index 0000000..5ea817b --- /dev/null +++ b/lib/models/user/confirm_mobile.dart @@ -0,0 +1,53 @@ + + + +import 'dart:convert'; + +ConfirmMobile confirmMobileFromJson(String str) => ConfirmMobile.fromJson(json.decode(str)); + +String confirmMobileToJson(ConfirmMobile data) => json.encode(data.toJson()); + + +class ConfirmMobile { + int? messageStatus; + Null? totalItemsCount; + Data? data; + String? message; + + ConfirmMobile( + {this.messageStatus, this.totalItemsCount, this.data, this.message}); + + ConfirmMobile.fromJson(Map json) { + messageStatus = json['messageStatus']; + totalItemsCount = json['totalItemsCount']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['messageStatus'] = this.messageStatus; + data['totalItemsCount'] = this.totalItemsCount; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + data['message'] = this.message; + return data; + } +} + +class Data { + String? userID; + + Data({this.userID}); + + Data.fromJson(Map json) { + userID = json['userID']; + } + + Map toJson() { + final Map data = new Map(); + data['userID'] = this.userID; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/confirm_password.dart b/lib/models/user/confirm_password.dart index ce87f4c..03891f9 100644 --- a/lib/models/user/confirm_password.dart +++ b/lib/models/user/confirm_password.dart @@ -1,6 +1,4 @@ - - import 'dart:convert'; ConfirmPassword confirmPasswordFromJson(String str) => ConfirmPassword.fromJson(json.decode(str)); diff --git a/lib/models/user/forget_password_otp_compare.dart b/lib/models/user/forget_password_otp_compare.dart index 9376d00..20c260b 100644 --- a/lib/models/user/forget_password_otp_compare.dart +++ b/lib/models/user/forget_password_otp_compare.dart @@ -1,5 +1,3 @@ - - import 'dart:convert'; PasswordOTPCompare otpCompareFromJson(String str) => PasswordOTPCompare.fromJson(json.decode(str)); diff --git a/lib/models/user/forget_password_otp_request.dart b/lib/models/user/forget_password_otp_request.dart index e7954bb..7ab7e5c 100644 --- a/lib/models/user/forget_password_otp_request.dart +++ b/lib/models/user/forget_password_otp_request.dart @@ -1,6 +1,4 @@ - - import 'dart:convert'; PasswordOTPRequest otpRequestFromJson(String str) => PasswordOTPRequest.fromJson(json.decode(str)); diff --git a/lib/models/user/image_response.dart b/lib/models/user/image_response.dart new file mode 100644 index 0000000..6ca0b41 --- /dev/null +++ b/lib/models/user/image_response.dart @@ -0,0 +1,37 @@ +// To parse this JSON data, do +// +// final imageResponse = imageResponseFromJson(jsonString); + +import 'dart:convert'; + +ImageResponse imageResponseFromJson(String str) => ImageResponse.fromJson(json.decode(str)); + +String imageResponseToJson(ImageResponse data) => json.encode(data.toJson()); + +class ImageResponse { + ImageResponse({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + dynamic? totalItemsCount; + String? data; + int? messageStatus; + String? message; + + factory ImageResponse.fromJson(Map json) => ImageResponse( + totalItemsCount: json["totalItemsCount"], + data: json["data"] == null ? null : json["data"], + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount, + "data": data == null ? null : data, + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} diff --git a/lib/models/user/role.dart b/lib/models/user/role.dart new file mode 100644 index 0000000..2ea0054 --- /dev/null +++ b/lib/models/user/role.dart @@ -0,0 +1,65 @@ +// To parse this JSON data, do +// +// final role = roleFromJson(jsonString); + +import 'dart:convert'; + +Role roleFromJson(String str) => Role.fromJson(json.decode(str)); + +String roleToJson(Role data) => json.encode(data.toJson()); + +class Role { + Role({ + this.totalItemsCount, + this.data, + this.messageStatus, + this.message, + }); + + int? totalItemsCount; + List? data; + int? messageStatus; + String? message; + + factory Role.fromJson(Map json) => Role( + totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + data: json["data"] == null ? null : List.from(json["data"].map((x) => RoleData.fromJson(x))), + messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], + message: json["message"] == null ? null : json["message"], + ); + + Map toJson() => { + "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), + "messageStatus": messageStatus == null ? null : messageStatus, + "message": message == null ? null : message, + }; +} + +class RoleData { + RoleData({ + this.id, + this.roleName, + this.roleNameN, + this.isActive, + }); + + int? id; + String? roleName; + String? roleNameN; + bool? isActive; + + factory RoleData.fromJson(Map json) => RoleData( + id: json["id"] == null ? null : json["id"], + roleName: json["roleName"] == null ? null : json["roleName"], + roleNameN: json["roleNameN"] == null ? null : json["roleNameN"], + isActive: json["isActive"] == null ? null : json["isActive"], + ); + + Map toJson() => { + "id": id == null ? null : id, + "roleName": roleName == null ? null : roleName, + "roleNameN": roleNameN == null ? null : roleNameN, + "isActive": isActive == null ? null : isActive, + }; +} diff --git a/lib/models/user/verify_email.dart b/lib/models/user/verify_email.dart new file mode 100644 index 0000000..bf0858f --- /dev/null +++ b/lib/models/user/verify_email.dart @@ -0,0 +1,53 @@ + + + +import 'dart:convert'; + +VerifyEmail verifyEmailFromJson(String str) => VerifyEmail.fromJson(json.decode(str)); + +String verifyEmailToJson(VerifyEmail data) => json.encode(data.toJson()); + + +class VerifyEmail { + Null? totalItemsCount; + Data? data; + int? messageStatus; + String? message; + + VerifyEmail( + {this.totalItemsCount, this.data, this.messageStatus, this.message}); + + VerifyEmail.fromJson(Map json) { + totalItemsCount = json['totalItemsCount']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + messageStatus = json['messageStatus']; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['totalItemsCount'] = this.totalItemsCount; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + data['messageStatus'] = this.messageStatus; + data['message'] = this.message; + return data; + } +} + +class Data { + String? userToken; + + Data({this.userToken}); + + Data.fromJson(Map json) { + userToken = json['userToken']; + } + + Map toJson() { + final Map data = new Map(); + data['userToken'] = this.userToken; + return data; + } +} \ No newline at end of file diff --git a/lib/models/user/verify_email_otp.dart b/lib/models/user/verify_email_otp.dart new file mode 100644 index 0000000..aa0827c --- /dev/null +++ b/lib/models/user/verify_email_otp.dart @@ -0,0 +1,27 @@ + + + +import 'dart:convert'; + +VerifyEmailOTP verifyEmailOTPFromJson(String str) => VerifyEmailOTP.fromJson(json.decode(str)); + +String verifyEmailOTPToJson(VerifyEmailOTP data) => json.encode(data.toJson()); + +class VerifyEmailOTP { + bool? success; + Null? errors; + + VerifyEmailOTP({this.success, this.errors}); + + VerifyEmailOTP.fromJson(Map json) { + success = json['success']; + errors = json['errors']; + } + + Map toJson() { + final Map data = new Map(); + data['success'] = this.success; + data['errors'] = this.errors; + return data; + } +} \ No newline at end of file diff --git a/lib/pages/dashboard/dashboard_page.dart b/lib/pages/dashboard/dashboard_page.dart index 91caad2..fd052f2 100644 --- a/lib/pages/dashboard/dashboard_page.dart +++ b/lib/pages/dashboard/dashboard_page.dart @@ -8,6 +8,17 @@ import 'package:car_customer_app/extensions/string_extensions.dart'; import 'package:car_customer_app/extensions/widget_extensions.dart'; import 'package:flutter/material.dart'; +import '../../api/client/user_api_client.dart'; +import '../../classes/app_state.dart'; +import '../../classes/consts.dart'; +import 'package:image_picker/image_picker.dart'; +import 'dart:io'; + +import '../../classes/utils.dart'; +import '../../config/routes.dart'; +import '../../models/user/image_response.dart'; +import '../../utils/utils.dart'; + class DashboardPage extends StatefulWidget { @override State createState() => _DashboardPageState(); @@ -16,6 +27,15 @@ class DashboardPage extends StatefulWidget { class _DashboardPageState extends State { String userName = ""; + // void _handleURLButtonPress(BuildContext context, var type) { + // Navigator.push(context, + // MaterialPageRoute(builder: (context) => ImageFromGalleryEx(type))); + // } + + File? imagePicked; + String image64 = ""; + final _picker = ImagePicker(); + @override void initState() { // TODO: implement initState @@ -48,16 +68,66 @@ class _DashboardPageState extends State { child: Container( child: Column( children: [ - Container( - width: double.infinity, - height: 200, - color: accentColor.withOpacity(0.3), - child: Icon( - Icons.person, - size: 80, + Stack(children: [ + Container( + width: double.infinity, + height: 200, color: accentColor.withOpacity(0.3), + child: Image.network(ApiConsts.baseUrlServices + AppState().getUser.data!.userInfo!.userImageUrl.toString()), ), - ), + Positioned( + top: 10, + right: 10, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(30), + ), + child: Icon( + Icons.edit, + color: Colors.blue, + ).onPress(() { + _openImagePicker(); + // _handleURLButtonPress(context, ImageSourceType.camera); + }), + ), + 12.height, + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(30), + ), + child: Icon( + Icons.delete, + color: Colors.blue, + ).onPress(() async { + Utils.showLoading(context); + ImageResponse response = await UserApiClent().UpdateUserImage(""); + if (response.messageStatus == 1) { + Utils.showToast("Image is Deleted"); + setState(() { + AppState().getUser.data!.userInfo!.userImageUrl = response.data; + }); + } + Utils.hideLoading(context); + }), + ), + ], + ), + ], + ), + ) + ]), Container( width: double.infinity, color: accentColor.withOpacity(0.1), @@ -76,7 +146,9 @@ class _DashboardPageState extends State { ), ShowFillButton( title: "EDIT", - onPressed: () {}, + onPressed: () { + navigateWithName(context, AppRoutes.editAccoundPage); + }, ), ], ), @@ -106,4 +178,80 @@ class _DashboardPageState extends State { ), ); } + + void _openImagePicker() { + showDialog( + context: context, + builder: (context) => AlertDialog(content: Text("Choose image source"), actions: [ + FlatButton(child: Text("Camera"), onPressed: () => cameraImage()), + FlatButton(child: Text("Gallery"), onPressed: () => gallaryImage()), + ]), + // .then((ImageSource source) async { + // if (source != null) { + // final pickedFile = await ImagePicker().getImage(source: source); + // setState(() => imagePicked = File(pickedFile.path)); + // } + // } + ); + } + + void gallaryImage() async { + final picker = ImagePicker(); + final pickedImage = await picker.pickImage( + source: ImageSource.gallery, + ); + final pickedImageFile = File(pickedImage!.path); + int sizeInBytes = pickedImageFile.lengthSync(); + // double sizeInMb = sizeInBytes / (1024 * 1024); + if (sizeInBytes > 1000) { + Utils.showToast("File is larger then 1KB"); + } else { + image64 = convertFileToBase64(pickedImageFile); + + Utils.showLoading(context); + ImageResponse response = await UserApiClent().UpdateUserImage(image64); + Utils.hideLoading(context); + Navigator.pop(context); + if (response.messageStatus == 1) { + Utils.showToast("Image is uploaded"); + AppState().getUser.data!.userInfo!.userImageUrl = response.data; + } else { + Utils.showToast(response.message ?? ""); + } + + setState(() { + imagePicked = pickedImageFile; + }); + } + } + + void cameraImage() async { + final picker = ImagePicker(); + final pickedImage = await picker.pickImage( + source: ImageSource.camera, + ); + final pickedImageFile = File(pickedImage!.path); + int sizeInBytes = pickedImageFile.lengthSync(); + // double sizeInMb = sizeInBytes / (1024 * 1024); + if (sizeInBytes > 1000) { + Utils.showToast("File is larger then 1KB"); + } else { + image64 = convertFileToBase64(pickedImageFile); + + Utils.showLoading(context); + ImageResponse response = await UserApiClent().UpdateUserImage(image64); + Utils.hideLoading(context); + Navigator.pop(context); + if (response.messageStatus == 1) { + Utils.showToast("Image is uploaded"); + AppState().getUser.data!.userInfo!.userImageUrl = response.data; + } else { + Utils.showToast(response.message ?? ""); + } + + setState(() { + imagePicked = pickedImageFile; + }); + } + } } diff --git a/lib/pages/user/change_email_page.dart b/lib/pages/user/change_email_page.dart new file mode 100644 index 0000000..27f4db0 --- /dev/null +++ b/lib/pages/user/change_email_page.dart @@ -0,0 +1,116 @@ + +import 'package:car_customer_app/api/client/user_api_client.dart'; +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/models/user/change_email.dart'; +import 'package:car_customer_app/models/user/confirm_email.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:car_customer_app/widgets/show_fill_button.dart'; +import 'package:flutter/material.dart'; + +import 'dart:convert'; +import 'package:http/http.dart'; + +class ChangeEmailPage extends StatefulWidget { + + @override + State createState() => _ChangeEmailPageState(); +} + +class _ChangeEmailPageState extends State { + String password = ""; + String email = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Change Email"), + body: SingleChildScrollView( + child: Container( + // width: double.infinity, + // height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "Enter Email".toText24(), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter New Email", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: false, + onChanged: (v) => email = v, + ), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter Current Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => password = v, + ), + 40.height, + ShowFillButton( + title: "Confirm", + width: double.infinity, + onPressed: () { + changeEmail(context); + }, + ), + ], + ), + ), + ), + ); + } + + Future changeEmail(BuildContext context) async { + Utils.showLoading(context); + ChanEmail otpRequest = await UserApiClent().ChangeEmailOTPRequest(email, password); + Utils.hideLoading(context); + if (otpRequest.messageStatus == 1) { + showMDialog(context, child: OtpDialog( + onClick: (String code) async { + pop(context); + Utils.showLoading(context); + ConfirmEmail otpCompare = await UserApiClent().ChangeEmail(otpRequest.data!.userToken ?? "", code); + Utils.hideLoading(context); + if (otpCompare.messageStatus == 1) { + showMDialog( + context, + child: MessageDialog( + title: "Email is Changed Successfully", + onClick: () { + Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.loginWithPassword, (Route route) => false); + }, + ), + ); + } else { + Utils.showToast(otpCompare.message ?? ""); + } + }, + )); + } else { + Utils.showToast(otpRequest.message ?? ""); + } + } + +} diff --git a/lib/pages/user/change_mobile_page.dart b/lib/pages/user/change_mobile_page.dart new file mode 100644 index 0000000..a2455f4 --- /dev/null +++ b/lib/pages/user/change_mobile_page.dart @@ -0,0 +1,119 @@ + +import 'package:car_customer_app/api/client/user_api_client.dart'; +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/models/user/change_mobile.dart'; +import 'package:car_customer_app/models/user/confirm_mobile.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:car_customer_app/widgets/show_fill_button.dart'; +import 'package:flutter/material.dart'; + +import 'dart:convert'; +import 'package:http/http.dart'; + +class ChangeMobilePage extends StatefulWidget { + + @override + State createState() => _ChangeMobilePageState(); +} + +class _ChangeMobilePageState extends State { + int countryID=1 ; + String mobileNo = ''; + String password = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Change Mobile Number"), + body: SingleChildScrollView( + child: Container( + // width: double.infinity, + // height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "Enter New Phone Number".toText24(), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter New Phone Number", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: false, + onChanged: (v) => mobileNo = v, + ), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter Current Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => password = v, + ), + 40.height, + ShowFillButton( + title: "Confirm", + width: double.infinity, + onPressed: () { + changeMobile(context); + }, + ), + ], + ), + ), + ), + ); + } + + Future changeMobile(BuildContext context) async { + Utils.showLoading(context); + ChangeMobile otpRequest = await UserApiClent().ChangeMobileNoOTPRequest(countryID, mobileNo, password); + Utils.hideLoading(context); + if (otpRequest.messageStatus == 1) { + showMDialog(context, child: OtpDialog( + onClick: (String code) async { + pop(context); + Utils.showLoading(context); + ConfirmMobile otpCompare = await UserApiClent().ChangeMobileNo(otpRequest.data!.userToken ?? "", code); + Utils.hideLoading(context); + if (otpCompare.messageStatus == 1) { + showMDialog( + context, + child: MessageDialog( + title: "Phone Number Verified", + onClick: () { + Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.loginWithPassword, (Route route) => false); + }, + ), + ); + } else { + Utils.showToast(otpCompare.message ?? ""); + } + }, + )); + } else { + Utils.showToast(otpRequest.message ?? ""); + } + } + + + +} diff --git a/lib/pages/user/change_password_page.dart b/lib/pages/user/change_password_page.dart new file mode 100644 index 0000000..18e8214 --- /dev/null +++ b/lib/pages/user/change_password_page.dart @@ -0,0 +1,112 @@ +import 'package:car_customer_app/api/client/user_api_client.dart'; + +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/models/user/change_password.dart'; +import 'package:car_customer_app/models/user/confirm_password.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/utils/utils.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/show_fill_button.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/widgets/txt_field.dart'; +import 'package:flutter/material.dart'; + +import 'dart:convert'; +import 'package:http/http.dart'; + +import '../../models/m_response.dart'; + +class ChangePasswordPage extends StatefulWidget { + ChangePasswordPage({Key? key}) : super(key: key); + + @override + State createState() => _ChangePasswordPageState(); +} + +class _ChangePasswordPageState extends State { + String newPassword = ""; + String currentPasswor = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Change Password"), + body: SingleChildScrollView( + child: Container( + // width: double.infinity, + // height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "Enter New Password".toText24(), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter Old Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => currentPasswor = v, + ), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter New Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => newPassword = v, + ), + 40.height, + ShowFillButton( + title: "Confirm", + width: double.infinity, + onPressed: () { + changePassword(context); + }, + ), + ], + ), + ), + ), + ); + } + + Future changePassword(BuildContext context) async { + if (validateStructure(newPassword ?? "")) { + Utils.showLoading(context); + MResponse res = await UserApiClent().ChangePassword(currentPasswor, newPassword); + Utils.hideLoading(context); + if (res.messageStatus == 1) { + Utils.showToast("Password is Updated"); + // navigateWithName(context, AppRoutes.loginWithPassword); + Navigator.of(context) + .pushNamedAndRemoveUntil(AppRoutes.loginWithPassword, (Route route) => false); + } else { + Utils.showToast(res.message ?? ""); + } + } else { + Utils.showToast("Password Should contains Character, Number, Capital and small letters"); + } + } + + bool validateStructure(String value) { + String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{6,}$'; + RegExp regExp = new RegExp(pattern); + return regExp.hasMatch(value); + } +} diff --git a/lib/pages/user/complete_profile_page.dart b/lib/pages/user/complete_profile_page.dart index 162cf22..b5f7791 100644 --- a/lib/pages/user/complete_profile_page.dart +++ b/lib/pages/user/complete_profile_page.dart @@ -1,6 +1,7 @@ -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/models/m_response.dart'; import 'package:car_customer_app/models/user/basic_otp.dart'; import 'package:car_customer_app/models/user/register_user.dart'; import 'package:car_customer_app/utils/navigator.dart'; @@ -27,6 +28,7 @@ class CompleteProfilePage extends StatefulWidget { class _CompleteProfilePageState extends State { String? firstName = "", lastName = "", email = "", password = "", confirmPassword = ""; + bool isChecked = false; @override void initState() { @@ -43,21 +45,23 @@ class _CompleteProfilePageState extends State { height: double.infinity, child: SingleChildScrollView( child: Padding( - padding: EdgeInsets.all(40), + padding: EdgeInsets.all(20), child: Column( children: [ "Complete Profile".toText24(), 12.height, TxtField( - hint: "First Name", + hint: "First Name *", + value: firstName, onChanged: (v) { firstName = v; }, ), 12.height, TxtField( - hint: "Surname", + hint: "Surname *", + value: lastName, onChanged: (v) { lastName = v; }, @@ -65,24 +69,29 @@ class _CompleteProfilePageState extends State { 12.height, TxtField( hint: "Email", + value: email, + isButtonEnable: email!.length > 0 ? true : false, + buttonTitle: "Verify", onChanged: (v) { email = v; }, ), 12.height, TxtField( - hint: "Create Password", + hint: "Create Password *", isPasswordEnabled: true, maxLines: 1, + value: password, onChanged: (v) { password = v; }, ), 12.height, TxtField( - hint: "Confirm Password", + hint: "Confirm Password *", isPasswordEnabled: true, maxLines: 1, + value: confirmPassword, onChanged: (v) { confirmPassword = v; }, @@ -92,13 +101,18 @@ class _CompleteProfilePageState extends State { // hint: "Phone Number", // ), 50.height, - "By creating an account you agree to our Terms of Service and Privacy Policy".toText12(), + Row( + children: [ + buildCheckbox(), + "By creating an account you agree to our Terms of Service and\n Privacy Policy".toText12(), + ], + ), 16.height, ShowFillButton( title: "Continue", width: double.infinity, onPressed: () { - performCompleteProfile(); + if (validation()) performCompleteProfile(); }, ), ], @@ -109,21 +123,94 @@ class _CompleteProfilePageState extends State { ); } + Widget buildCheckbox() => Checkbox( + value: isChecked, + activeColor: Colors.blue, + onChanged: (value) { + setState(() { + isChecked = value!; + }); + }, + ); + + // Future performCompleteProfile() async { + // if(validateStructure(password??"")){ + // if (password == confirmPassword) { + // print(widget.user.data!.userId??"userId"); + // Utils.showLoading(context); + // RegisterUser user = await UserApiClent().basicComplete(widget.user.data?.userId ?? "", firstName!, lastName!, email!, password!); + // Utils.hideLoading(context); + // if (user.messageStatus == 1) { + // Utils.showToast( "Successfully registered, Please login once"); + // pop(context); + // // pop(context); + // // navigateReplaceWithName(context, AppRoutes.dashboard,arguments: user); + // } else { + // Utils.showToast(user.message ?? ""); + // } + // } else { + // Utils.showToast("Please enter same password"); + // } + // }else{ + // Utils.showToast("Password Should contains character, Number, Capital and small letters"); + // } + // + // } + Future performCompleteProfile() async { - if (password == confirmPassword) { - print(widget.user.data!.userId??"userId"); - Utils.showLoading(context); - RegisterUser user = await UserApiClent().basicComplete(widget.user.data?.userId ?? "", firstName!, lastName!, email!, password!); - Utils.hideLoading(context); - if (user.messageStatus == 1) { - Utils.showToast( "Successfully Registered, Please login once"); - pop(context); - // navigateReplaceWithName(context, AppRoutes.dashboard,arguments: user); + if (validateStructure(password ?? "")) { + if (password == confirmPassword) { + print(widget.user.data!.userId ?? "userId"); + Utils.showLoading(context); + RegisterUser user = await UserApiClent().basicComplete(widget.user.data?.userId ?? "", firstName!, lastName!, email!, password!); + Utils.hideLoading(context); + if (user.messageStatus == 1) { + Utils.showToast("Successfully Registered, Please login once"); + pop(context); + // navigateReplaceWithName(context, AppRoutes.dashboard,arguments: user); + } else { + Utils.showToast(user.message ?? ""); + } } else { - Utils.showToast(user.message ?? ""); + Utils.showToast("Please enter same password"); } } else { - Utils.showToast("Please enter same password"); + Utils.showToast("Password Should contains Character, Number, Capital and small letters"); + } + } + + bool validateStructure(String value) { + String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{6,}$'; + RegExp regExp = new RegExp(pattern); + return regExp.hasMatch(value); + } + + bool isEmail(String em) { + String p = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; + RegExp regExp = new RegExp(p); + return regExp.hasMatch(em); + } + + bool validation() { + bool isValid = true; + if (firstName!.isEmpty) { + Utils.showToast("First name is mandatory"); + isValid = false; + } else if (lastName!.isEmpty) { + Utils.showToast("Surname is mandatory"); + isValid = false; + } else if (email!.isNotEmpty) { + if(!isEmail(email!)){ + Utils.showToast("Enter Valid Email"); + isValid = false; + } + }else if (password!.isEmpty) { + Utils.showToast("Password is mandatory"); + isValid = false; + } else if (!isChecked) { + Utils.showToast("Please accept terms"); + isValid = false; } + return isValid; } } diff --git a/lib/pages/user/confirm_new_password.dart b/lib/pages/user/confirm_new_password.dart index 851fa46..1231995 100644 --- a/lib/pages/user/confirm_new_password.dart +++ b/lib/pages/user/confirm_new_password.dart @@ -1,6 +1,6 @@ -import 'package:car_customer_app/api/user_api_client.dart'; + import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/routes.dart'; import 'package:car_customer_app/extensions/int_extensions.dart'; @@ -15,6 +15,8 @@ import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:http/http.dart'; +import '../../api/client/user_api_client.dart'; + class ConfirmNewPasswordPage extends StatelessWidget { String userToken; diff --git a/lib/pages/user/confirm_new_password_page.dart b/lib/pages/user/confirm_new_password_page.dart new file mode 100644 index 0000000..7d3cd0c --- /dev/null +++ b/lib/pages/user/confirm_new_password_page.dart @@ -0,0 +1,119 @@ +import 'package:car_customer_app/api/client/user_api_client.dart'; + +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/models/user/confirm_password.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/utils/utils.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/show_fill_button.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/widgets/txt_field.dart'; +import 'package:flutter/material.dart'; + +import 'dart:convert'; +import 'package:http/http.dart'; + +class ConfirmNewPasswordPage extends StatefulWidget { + String userToken; + + ConfirmNewPasswordPage(this.userToken, {Key? key}) : super(key: key); + + @override + State createState() => _ConfirmNewPasswordPageState(); +} + +class _ConfirmNewPasswordPageState extends State { + String newPassword = ""; + String confirmPassword = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Forget Password"), + body: SingleChildScrollView( + child: Container( + // width: double.infinity, + // height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "New Password".toText24(), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Enter New Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => newPassword = v, + ), + 12.height, + TextFormField( + decoration: InputDecoration( + hintText: "Confirm Password", + hintStyle: TextStyle(color: Colors.grey), + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(5.0), + ), + ), + ), + obscureText: true, + onChanged: (v) => confirmPassword = v, + ), + 40.height, + ShowFillButton( + title: "Confirm", + width: double.infinity, + onPressed: () { + if (validation()) confirmPasswordOTP(context); + }, + ), + ], + ), + ), + ), + ); + } + + Future confirmPasswordOTP(BuildContext context) async { + if(validateStructure(newPassword??"")){ + Utils.showLoading(context); + Response res = await UserApiClent().ForgetPassword(widget.userToken, newPassword); + Utils.hideLoading(context); + ConfirmPassword data = ConfirmPassword.fromJson(jsonDecode(res.body)); + if (data.messageStatus == 1) { + Utils.showToast("Password is Updated"); + navigateWithName(context, AppRoutes.loginWithPassword); + } else { + Utils.showToast(data.message ?? ""); + } + }else{ + Utils.showToast("Password Should contains Character, Number, Capital and small letters"); + } + } + + bool validation() { + bool isValid = true; + if (newPassword != confirmPassword) { + Utils.showToast("Confirmation password does not match the entered password"); + isValid = false; + } + return isValid; + } + + bool validateStructure(String value){ + String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{6,}$'; + RegExp regExp = new RegExp(pattern); + return regExp.hasMatch(value); + } +} diff --git a/lib/pages/user/edit_account_page.dart b/lib/pages/user/edit_account_page.dart new file mode 100644 index 0000000..a44de2e --- /dev/null +++ b/lib/pages/user/edit_account_page.dart @@ -0,0 +1,158 @@ +import 'dart:convert'; + +import 'package:car_customer_app/api/client/user_api_client.dart'; +import 'package:car_customer_app/classes/app_state.dart'; +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/models/m_response.dart'; +import 'package:car_customer_app/models/user/verify_email.dart'; +import 'package:car_customer_app/models/user/verify_email_otp.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class EditAccountPage extends StatefulWidget { + @override + State createState() => _EditAccountPageState(); +} + +class _EditAccountPageState extends State { + String userID = ""; + String email = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Edit Account"), + body: Container( + width: double.infinity, + height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + 20.height, + ListTile( + leading: SvgPicture.asset("assets/images/ic_lock.svg"), + title: "Change Password".toText12(), + onTap: () { + navigateWithName(context, AppRoutes.changePassword); + }, + ), + 15.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SvgPicture.asset("assets/images/ic_mobile.svg"), + "Change Mobile".toText12(), + "Verify".toText12(), + RaisedButton( + onPressed: () { + navigateWithName(context, AppRoutes.changeMobilePage); + }, + child: Text( + "Change", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + color: Colors.blue, + textColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8)) + ], + ), + 20.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Icon( + Icons.email, + color: Colors.blue, + ), + "Change Email".toText12(), + InkWell( + child: ((AppState().getUser.data!.userInfo!.isEmailVerified ?? false) ? "Verified" : "Verify").toText12(), + onTap: (AppState().getUser.data!.userInfo!.isEmailVerified ?? false) + ? null + : () { + verifyEmail(context); + }, + ), + RaisedButton( + onPressed: () { + navigateWithName(context, AppRoutes.changeEmailPage); + }, + child: Text( + "Change", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + color: Colors.blue, + textColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8)) + ], + ) + + // ListTile( + // leading: SvgPicture.asset("assets/images/ic_mobile.svg"), + // title: "Change Mobile".toText12(), + // onTap: () { + // navigateWithName(context, AppRoutes.changeMobilePage); + // }, + // ), + // ListTile( + // leading: Icon(Icons.email_outlined, color: Colors.blue,), + // title: "Change Email".toText12(), + // onTap: () { + // navigateWithName(context, AppRoutes.changeEmailPage); + // }, + // ), + ], + ), + ), + ); + } + + Future verifyEmail(BuildContext context) async { + Utils.showLoading(context); + VerifyEmail otpRequest = await UserApiClent().EmailVerify(email, userID); + Utils.hideLoading(context); + if (otpRequest.messageStatus == 1) { + showMDialog(context, child: OtpDialog( + onClick: (String code) async { + pop(context); + Utils.showLoading(context); + MResponse otpCompare = await UserApiClent().EmailVerifyOTPVerify(otpRequest.data!.userToken ?? "", code); + Utils.hideLoading(context); + if (otpCompare.messageStatus == 1) { + AppState().getUser.data!.userInfo!.isEmailVerified = true; + setState(() {}); + Utils.showToast("Email is verified successfully"); + // Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.dashboard, (Route route) => false); + // showMDialog( + // context, + // child: MessageDialog( + // title: "Email is Verified", + // onClick: () { + // Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.dashboard, (Route route) => false); + // }, + // ), + // ); + } else { + Utils.showToast(otpCompare.message ?? ""); + } + }, + )); + } else { + Utils.showToast(otpRequest.message ?? ""); + } + } +} diff --git a/lib/pages/user/forget_password_method_page.dart b/lib/pages/user/forget_password_method_page.dart new file mode 100644 index 0000000..30e016c --- /dev/null +++ b/lib/pages/user/forget_password_method_page.dart @@ -0,0 +1,95 @@ + + + +import 'dart:convert'; +import 'package:car_customer_app/api/client/user_api_client.dart'; +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/constants.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/models/user/forget_password_otp_compare.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/utils/utils.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/button/show_image_button.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; + +class ForgetPasswordMethodPage extends StatefulWidget { + String userToken; + + ForgetPasswordMethodPage(this.userToken); + + @override + State createState() => _ForgetPasswordMethodPageState(); +} + +class _ForgetPasswordMethodPageState extends State { + int otpType = 1; + String userOTP = ""; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Forget Password"), + body: Container( + width: double.infinity, + height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "Select Method".toText24(), + 12.height, + Row( + children: [ + Expanded( + child: ShowImageButton( + onClick: () { + otpType = 1; + forgetPasswordOTPMethod(context); + }, + title: 'With SMS', + icon: icons + "ic_sms.png", + ), + ), + 20.width, + Expanded( + child: ShowImageButton( + onClick: () { + otpType = 1; + forgetPasswordOTPMethod(context); + }, + title: 'With Whatsapp', + icon: icons + "ic_whatsapp.png", + ), + ), + ], + ), + mFlex(10), + ], + ), + ), + ); + } + + Future forgetPasswordOTPMethod(BuildContext context) async { + showMDialog(context, child: OtpDialog( + onClick: (String code) async { + pop(context); + Utils.showLoading(context); + Response res = await UserApiClent().ForgetPasswordOTPCompare(widget.userToken?? "", code); + Utils.hideLoading(context); + PasswordOTPCompare otpCompare = PasswordOTPCompare.fromJson(jsonDecode(res.body)); + if (otpCompare.messageStatus == 1) { + var userToken = otpCompare.data!.userToken; + navigateWithName(context, AppRoutes.confirmNewPasswordPage, arguments: userToken); + } else { + Utils.showToast(otpCompare.message ?? ""); + } + }, + )); + } +} diff --git a/lib/pages/user/forget_password_page.dart b/lib/pages/user/forget_password_page.dart index 5469b91..71236b1 100644 --- a/lib/pages/user/forget_password_page.dart +++ b/lib/pages/user/forget_password_page.dart @@ -1,102 +1,154 @@ -import 'dart:convert'; +import 'package:car_customer_app/api/client/user_api_client.dart'; +import 'package:car_customer_app/api/shared_prefrence.dart'; -import 'package:car_customer_app/api/user_api_client.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/constants.dart'; import 'package:car_customer_app/config/routes.dart'; import 'package:car_customer_app/models/user/forget_password_otp_compare.dart'; import 'package:car_customer_app/models/user/forget_password_otp_request.dart'; +import 'package:car_customer_app/pages/user/vertify_password_page.dart'; import 'package:car_customer_app/utils/navigator.dart'; import 'package:car_customer_app/utils/utils.dart'; import 'package:car_customer_app/widgets/app_bar.dart'; import 'package:car_customer_app/widgets/button/show_image_button.dart'; import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:car_customer_app/widgets/show_fill_button.dart'; import 'package:car_customer_app/extensions/string_extensions.dart'; import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/widget_extensions.dart'; import 'package:car_customer_app/widgets/txt_field.dart'; import 'package:flutter/material.dart'; +import 'package:car_customer_app/models/user/user.dart'; +import 'dart:convert'; import 'package:http/http.dart'; -class ForgetPasswordPage extends StatelessWidget { +import '../../models/user/country.dart'; +import '../../widgets/dropdown/dropdow_field.dart'; +import '../../widgets/tab/login_email_tab.dart'; + +class ForgetPasswordPage extends StatefulWidget { + @override + State createState() => _ForgetPasswordPageState(); +} + +class _ForgetPasswordPageState extends State { int otpType = 1; + String userName = ""; + ClassType type = ClassType.NUMBER; + Country? _country; + String countryCode = ""; + + @override + void initState() { + super.initState(); + getCountryList(); + } + + getCountryList() async { + _country = await UserApiClent().getAllCountries(); + setState(() {}); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: appBar(title: "Forget Password"), + appBar: appBar(title: "Change Password"), body: Container( width: double.infinity, height: double.infinity, padding: EdgeInsets.all(40), child: Column( children: [ + mFlex(1), + LoginEmailTab( + onSelection: (ClassType type) { + setState(() { + this.type = type; + }); + }, + ), + 50.height, "Retrieve Password".toText24(), 12.height, - TxtField( - hint: "Phone Number", + type == ClassType.NUMBER ? Column(children: [ + getCountry(), + TxtField( + hint: "5********", + value: userName, + onChanged: (v) { + userName = v; + }, + ) + ],) : TxtField( + hint: "Email Address", value: userName, onChanged: (v) { userName = v; }, ), - // TxtField( - // hint: "Email", - // ), - mFlex(2), - Row( - children: [ - Expanded( - child: ShowImageButton( - onClick: () { - otpType = 1; - forgetPasswordOTP(context); - }, - title: 'With SMS', - icon: icons + "ic_sms.png", - ), - ), - 20.width, - Expanded( - child: ShowImageButton( - onClick: () { - otpType = 1; - forgetPasswordOTP(context); - }, - title: 'With Whatsapp', - icon: icons + "ic_whatsapp.png", - ), - ), - ], + 50.height, + ShowFillButton( + title: "Continue", + width: double.infinity, + onPressed: () { + if (userName.isNum() && type==ClassType.NUMBER) { + forgetPasswordPhoneOTP(context); + } else if (!userName.isNum() && type==ClassType.EMAIL) { + forgetPasswordEmailOTP(context); + } + }, ), mFlex(10), - // 40.height, - // ShowFillButton( - // title: "Continue", - // width: double.infinity, - // onPressed: () { - // showMDialog( - // context, - // child: MessageDialog( - // title: "New Password sent used", - // ), - // ); - // }, - // ), ], ), ), ); } - Future forgetPasswordOTP(BuildContext context) async { + Widget getCountry() { + if (_country != null) { + List dropList = []; + _country!.data?.forEach((element) { + dropList.add(new DropValue(element.id ?? 0, (element.countryName ?? "") + " " + (element.countryCode ?? ""), element.countryCode ?? "")); + }); + return Padding( + padding: const EdgeInsets.all(2.0), + child: DropdownField((DropValue value) { + countryCode = value.subValue; + }, list: dropList, hint: "Chose Country"), + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + + Future forgetPasswordPhoneOTP(BuildContext context) async { + Utils.showLoading(context); + Response response = await UserApiClent().ForgetPasswordOTPRequest(countryCode+userName, otpType); + Utils.hideLoading(context); + PasswordOTPRequest otpRequest = PasswordOTPRequest.fromJson(jsonDecode(response.body)); + if (otpRequest.messageStatus == 1) { + var userToken = otpRequest.data!.userToken; + navigateReplaceWithName(context, AppRoutes.forgetPasswordMethodPage, arguments: userToken); + } else { + Utils.showToast(otpRequest.message ?? ""); + } + } + + Future forgetPasswordEmailOTP(BuildContext context) async { Utils.showLoading(context); Response response = await UserApiClent().ForgetPasswordOTPRequest(userName, otpType); Utils.hideLoading(context); PasswordOTPRequest otpRequest = PasswordOTPRequest.fromJson(jsonDecode(response.body)); if (otpRequest.messageStatus == 1) { + Utils.showToast("Code is sent to email"); showMDialog(context, child: OtpDialog( onClick: (String code) async { pop(context); @@ -106,6 +158,8 @@ class ForgetPasswordPage extends StatelessWidget { PasswordOTPCompare otpCompare = PasswordOTPCompare.fromJson(jsonDecode(res.body)); if (otpCompare.messageStatus == 1) { var userToken = otpCompare.data!.userToken; + print("token is ________"); + print(userToken); navigateWithName(context, AppRoutes.confirmNewPasswordPage, arguments: userToken); } else { Utils.showToast(otpCompare.message ?? ""); diff --git a/lib/pages/user/login_method_selection_page.dart b/lib/pages/user/login_method_selection_page.dart index 9f5d7e0..e43febf 100644 --- a/lib/pages/user/login_method_selection_page.dart +++ b/lib/pages/user/login_method_selection_page.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:car_customer_app/api/shared_prefrence.dart'; -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/app_state.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/constants.dart'; @@ -108,12 +108,19 @@ class LoginMethodSelectionPage extends StatelessWidget { Response response2 = await UserApiClent().login_V2_OTPVerify(user.data!.userToken??"", code); Utils.hideLoading(context); RegisterUser verifiedUser = RegisterUser.fromJson(jsonDecode(response2.body)); + if (verifiedUser.messageStatus == 1) { User user = User.fromJson(jsonDecode(response2.body)); - AppState().setUser = user; - SharedPrefManager.setUserToken(user.data!.accessToken ?? ""); - SharedPrefManager.setUserId(user.data!.userInfo!.userId ?? ""); - navigateReplaceWithName(context, AppRoutes.dashboard); + if(user.data!.userInfo!.roleId==5||user.data!.userInfo!.roleId==6){ + + AppState().setUser = user; + SharedPrefManager.setUserToken(user.data!.accessToken ?? ""); + SharedPrefManager.setUserId(user.data!.userInfo!.userId ?? ""); + navigateReplaceWithName(context, AppRoutes.dashboard); + }else{ + Utils.showToast("Sorry, Only Provider's can log in this app"); + } + } else { Utils.showToast(verifiedUser.message??""); } diff --git a/lib/pages/user/login_verification_page.dart b/lib/pages/user/login_verification_page.dart index cc95b05..0bee3ae 100644 --- a/lib/pages/user/login_verification_page.dart +++ b/lib/pages/user/login_verification_page.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:car_customer_app/api/shared_prefrence.dart'; -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/app_state.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/constants.dart'; @@ -48,7 +48,6 @@ class _LoginVerificationPageState extends State { LoginPassword user = LoginPassword.fromJson(jsonDecode(response.body)); if (user.messageStatus == 1) { userToken = user.data!.userToken ?? ""; - // navigateWithName(context, AppRoutes.loginMethodSelection, arguments: user.data!.userToken); } else { Utils.showToast(user.message ?? ""); @@ -72,6 +71,7 @@ class _LoginVerificationPageState extends State { if (verifiedUser.messageStatus == 1) { User user = User.fromJson(jsonDecode(response2.body)); AppState().setUser = user; + print(user.data!.userInfo!.providerId.toString()); navigateReplaceWithName(context, AppRoutes.dashboard); } else { Utils.showToast(verifiedUser.message ?? ""); diff --git a/lib/pages/user/login_verify_account_page.dart b/lib/pages/user/login_verify_account_page.dart index a2989f2..26fbf49 100644 --- a/lib/pages/user/login_verify_account_page.dart +++ b/lib/pages/user/login_verify_account_page.dart @@ -1,4 +1,4 @@ -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/constants.dart'; import 'package:car_customer_app/config/routes.dart'; @@ -128,7 +128,7 @@ class LoginVerifyAccountPage extends StatelessWidget { title: "Phone Number Verified", onClick: () { pop(context); - navigateWithName(context, AppRoutes.completeProfile,arguments: user); + navigateWithName(context, AppRoutes.profile1,arguments: user); }, ), ); diff --git a/lib/pages/user/login_with_password_page.dart b/lib/pages/user/login_with_password_page.dart index e5c9567..448a2ea 100644 --- a/lib/pages/user/login_with_password_page.dart +++ b/lib/pages/user/login_with_password_page.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:car_customer_app/api/shared_prefrence.dart'; -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/constants.dart'; import 'package:car_customer_app/config/routes.dart'; @@ -19,13 +19,38 @@ import 'package:car_customer_app/extensions/int_extensions.dart'; import 'package:car_customer_app/extensions/string_extensions.dart'; import 'package:car_customer_app/extensions/widget_extensions.dart'; import 'package:car_customer_app/widgets/show_fill_button.dart'; +import 'package:car_customer_app/widgets/tab/login_email_tab.dart'; import 'package:car_customer_app/widgets/txt_field.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart'; -class LoginWithPassword extends StatelessWidget { +import '../../models/user/country.dart'; +import '../../widgets/dropdown/dropdow_field.dart'; + +class LoginWithPassword extends StatefulWidget { + @override + State createState() => _LoginWithPasswordState(); +} + +class _LoginWithPasswordState extends State { int otpType = 1; + + ClassType type = ClassType.NUMBER; String phoneNum = "", password = ""; + String email = ""; + String countryCode = ""; + Country? _country; + + @override + void initState() { + super.initState(); + getCountryList(); + } + + getCountryList() async { + _country = await UserApiClent().getAllCountries(); + setState(() {}); + } @override Widget build(BuildContext context) { @@ -37,37 +62,52 @@ class LoginWithPassword extends StatelessWidget { padding: EdgeInsets.all(40), child: Column( children: [ - "Login".toText24(), - mFlex(1), - TxtField( - hint: "966501234567", - value: phoneNum, - onChanged: (v) { - phoneNum = v; + LoginEmailTab( + onSelection: (ClassType type) { + setState(() { + this.type = type; + }); }, ), - 12.height, - TxtField( - hint: "Password", - value: password, - isPasswordEnabled: true, - maxLines: 1, - onChanged: (v) { - password = v; - }, + 50.height, + (type == ClassType.NUMBER ? "Enter Phone" : "Enter Email").toText24(), + mFlex(1), + Column( + children: [ + if (type == ClassType.NUMBER) + getCountry(), + 6.height, + TxtField( + hint: type == ClassType.NUMBER ? "5********" : "Enter Email", + value: phoneNum, + onChanged: (v) { + phoneNum = v; + }, + ), + 6.height, + TxtField( + hint: "Enter Password?", + value: password, + isPasswordEnabled: true, + maxLines: 1, + onChanged: (v) { + password = v; + }, + ), + ], ), 10.height, Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "Forget Password".toText12(color: Colors.blue).onPress(() { + "Forget Password?".toText12(color: Colors.blue).onPress(() { navigateWithName(context, AppRoutes.forgetPassword); }), ], ), 50.height, ShowFillButton( - title: "Continue", + title: "Log In", width: double.infinity, onPressed: () { performBasicOtp(context); @@ -80,17 +120,37 @@ class LoginWithPassword extends StatelessWidget { ); } + Widget getCountry() { + if (_country != null) { + List dropList = []; + _country!.data?.forEach((element) { + dropList.add(new DropValue(element.id ?? 0, (element.countryName ?? "") + " " + (element.countryCode ?? ""), element.countryCode ?? "")); + }); + return Padding( + padding: const EdgeInsets.all(2.0), + child: DropdownField((DropValue value) { + countryCode = value.subValue; + }, list: dropList, hint: "Chose Country"), + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + Future performBasicOtp(BuildContext context) async { Utils.showLoading(context); - Response response = await UserApiClent().login_V1(phoneNum, password); + Response response = await UserApiClent().login_V1(type == ClassType.NUMBER ? countryCode + phoneNum : phoneNum, password); Utils.hideLoading(context); LoginPassword user = LoginPassword.fromJson(jsonDecode(response.body)); if (user.messageStatus == 1) { - SharedPrefManager.setPhoneOrEmail(phoneNum); + SharedPrefManager.setPhoneOrEmail(type == ClassType.NUMBER ? countryCode + phoneNum : phoneNum); SharedPrefManager.setUserPassword(password); navigateReplaceWithName(context, AppRoutes.loginMethodSelection, arguments: user.data!.userToken); } else { Utils.showToast(user.message ?? ""); } } + } diff --git a/lib/pages/user/register_page.dart b/lib/pages/user/register_page.dart index 3d94e1b..fd17b8f 100644 --- a/lib/pages/user/register_page.dart +++ b/lib/pages/user/register_page.dart @@ -1,10 +1,12 @@ import 'package:car_customer_app/api/api_client.dart'; -import 'package:car_customer_app/api/user_api_client.dart'; +import 'package:car_customer_app/api/client/user_api_client.dart'; import 'package:car_customer_app/classes/utils.dart'; import 'package:car_customer_app/config/routes.dart'; import 'package:car_customer_app/models/user/basic_otp.dart'; import 'package:car_customer_app/models/user/country.dart'; import 'package:car_customer_app/models/user/register_user.dart'; +import 'package:car_customer_app/models/user/role.dart'; + import 'package:car_customer_app/utils/navigator.dart'; import 'package:car_customer_app/utils/utils.dart'; import 'package:car_customer_app/widgets/app_bar.dart'; @@ -20,7 +22,8 @@ import 'package:flutter/material.dart'; class RegisterPage extends StatelessWidget { String phoneNum = "", countryCode = ""; - int role = -1, countryId = -1; + int role = 4, countryId = -1; + @override Widget build(BuildContext context) { return Scaffold( @@ -32,6 +35,23 @@ class RegisterPage extends StatelessWidget { child: Column( children: [ "Enter Phone Number".toText24(), + // 12.height, + // FutureBuilder( + // future: UserApiClent().getRoles(), + // builder: (context, snapshot) { + // if (snapshot.hasData) { + // List dropList = []; + // snapshot.data?.data?.forEach((element) { + // dropList.add(new DropValue(element.id ?? 0, element.roleName ?? "", "")); + // }); + // return DropdownField((DropValue value) { + // role = value.id; + // }, list: dropList, hint: "Chosse Role"); + // } else { + // return CircularProgressIndicator(); + // } + // }, + // ), 12.height, FutureBuilder( future: UserApiClent().getAllCountries(), @@ -44,14 +64,14 @@ class RegisterPage extends StatelessWidget { return DropdownField((DropValue value) { countryCode = value.subValue; countryId = value.id; - }, list: dropList, hint: "Chosse Country"); + }, list: dropList, hint: "Chose Country"); } else { return CircularProgressIndicator(); } }, ), TxtField( - hint: "Enter Phone number to Register", + hint: "5********", onChanged: (v) { phoneNum = v; }, @@ -61,7 +81,7 @@ class RegisterPage extends StatelessWidget { title: "Continue", width: double.infinity, onPressed: () { - performBasicOtp(context); + if (validation()) performBasicOtp(context); }, ), ], @@ -72,7 +92,7 @@ class RegisterPage extends StatelessWidget { Future performBasicOtp(BuildContext context) async { Utils.showLoading(context); - BasicOtp basicOtp = await UserApiClent().basicOtp(countryCode + phoneNum,); + BasicOtp basicOtp = await UserApiClent().basicOtp(countryCode + phoneNum, roleId: role); Utils.hideLoading(context); if (basicOtp.messageStatus == 1) { showMDialog(context, child: OtpDialog( @@ -83,25 +103,29 @@ class RegisterPage extends StatelessWidget { Utils.hideLoading(context); if (user.messageStatus == 1) { Utils.showToast(user.message ?? ""); - navigateReplaceWithName(context, AppRoutes.completeProfile,arguments: user); + navigateReplaceWithName(context, AppRoutes.completeProfile, arguments: user); } else { Utils.showToast(user.message ?? ""); } - // delay(300).then( - // (value) => showMDialog( - // context, - // child: MessageDialog( - // title: "Phone Number Verified", - // onClick: () { - // navigateWithName(context, AppRoutes.completeProfile); - // }, - // ), - // ), - // ); }, )); } else { Utils.showToast(basicOtp.message ?? ""); } } + + bool validation() { + bool isValid = true; + if (role == -1) { + Utils.showToast("Please select Provider Role"); + isValid = false; + } else if (countryCode.isEmpty) { + Utils.showToast("Please select Country Code"); + isValid = false; + } else if (phoneNum.isEmpty) { + Utils.showToast("Please add Phone No"); + isValid = false; + } + return isValid; + } } diff --git a/lib/pages/user/register_selection_page.dart b/lib/pages/user/register_selection_page.dart index 189402b..523ceea 100644 --- a/lib/pages/user/register_selection_page.dart +++ b/lib/pages/user/register_selection_page.dart @@ -23,6 +23,7 @@ class RegisterSelectionPage extends StatelessWidget { mFlex(1), ShowFillButton( title: "Log In", + // title: "Log In With Password", width: double.infinity, onPressed: () { navigateWithName(context, AppRoutes.loginWithPassword); diff --git a/lib/pages/user/vertify_password_page.dart b/lib/pages/user/vertify_password_page.dart new file mode 100644 index 0000000..1c34270 --- /dev/null +++ b/lib/pages/user/vertify_password_page.dart @@ -0,0 +1,162 @@ + + +import 'package:car_customer_app/classes/utils.dart'; +import 'package:car_customer_app/config/constants.dart'; +import 'package:car_customer_app/config/routes.dart'; +import 'package:car_customer_app/models/user/forget_password_otp_compare.dart'; +import 'package:car_customer_app/models/user/forget_password_otp_request.dart'; +import 'package:car_customer_app/utils/navigator.dart'; +import 'package:car_customer_app/utils/utils.dart'; +import 'package:car_customer_app/widgets/app_bar.dart'; +import 'package:car_customer_app/widgets/button/show_image_button.dart'; +import 'package:car_customer_app/extensions/int_extensions.dart'; +import 'package:car_customer_app/extensions/string_extensions.dart'; +import 'package:car_customer_app/extensions/widget_extensions.dart'; +import 'package:car_customer_app/widgets/dialog/dialogs.dart'; +import 'package:car_customer_app/widgets/dialog/message_dialog.dart'; +import 'package:car_customer_app/widgets/dialog/otp_dialog.dart'; +import 'package:car_customer_app/widgets/txt_field.dart'; +import 'package:flutter/material.dart'; + + + +class VerifyPasswordPage extends StatelessWidget { + int otpType = 1; + String phoneNum = ""; + String userName = ""; + String userToken = ""; + String otp = ""; + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: appBar(title: "Change Password"), + body: Container( + width: double.infinity, + height: double.infinity, + padding: EdgeInsets.all(40), + child: Column( + children: [ + "Verify New Password".toText24(), + mFlex(1), + TxtField( + hint: "966500000000", + value: phoneNum, + onChanged: (v) { + phoneNum = v; + }, + ), + mFlex(2), + Row( + children: [ + Expanded( + child: ShowImageButton( + onClick: () { + otpType = 1; + // sendPasswordOTP(context); + showMDialog(context, child: OtpDialog( + onClick: (String code) { + pop(context); + delay(300).then( + (value) => showMDialog( + context, + child: MessageDialog( + title: "Phone Number Verified", + onClick: () { + otpType=1; + navigateWithName(context, AppRoutes.confirmNewPasswordPage); + // ForgetPasswordOTP(context); + }, + ), + ), + ); + }, + )); + }, + title: 'With SMS', + icon: icons + "ic_sms.png", + ), + ), + 20.width, + Expanded( + child: ShowImageButton( + onClick: () { + otpType = 1; + // sendPasswordOTP(context); + showMDialog(context, child: OtpDialog( + onClick: (String code) { + pop(context); + delay(300).then( + (value) => showMDialog( + context, + child: MessageDialog( + title: "Phone Number Verified", + onClick: () { + otpType=2; + navigateWithName(context, AppRoutes.confirmNewPasswordPage); + // ForgetPasswordOTP(context); + }, + ), + ), + ); + }, + )); + }, + title: 'With Whatsapp', + icon: icons + "ic_whatsapp.png", + ), + ), + ], + ), + mFlex(10), + ], + ), + ), + ); + } + + // Future sendPasswordOTP(BuildContext context) async { + // Utils.showLoading(context); + // PasswordOTPCompare otpCompare = await UserApiClent().ForgetPasswordOTPCompare(userToken, otp); + // Utils.hideLoading(context); + // if (otpCompare.messageStatus == 1) { + // // navigateWithName(context, AppRoutes.confirmNewPasswordPage); + // showMDialog(context, child: OtpDialog( + // onClick: (String code) async { + // pop(context); + // Utils.showLoading(context); + // // PasswordOTPCompare otpCompare = await UserApiClent().ForgetPasswordOTPCompare(userToken, otp); + // Utils.hideLoading(context); + // }, + // )); + // } else { + // Utils.showToast(otpCompare.message ?? ""); + // } + // } + + +// User otpRequest = User.fromJson(jsonDecode(res.body)); +// SharedPrefManager.setUserToken(otpRequest.data!.accessToken ?? ""); +// SharedPrefManager.setUserId(otpRequest.data!.userInfo!.userId ?? ""); +// showMDialog(context, child: OtpDialog( +// onClick: (String code) { +// pop(context); +// delay(300).then( +// (value) => showMDialog( +// context, +// child: MessageDialog( +// title: "Phone Number Verified", +// onClick: () { +// otpType=1; +// navigateWithName(context, AppRoutes.confirmNewPasswordPage); +// // ForgetPasswordOTP(context); +// }, +// ), +// ), +// ); +// }, +// )); + + +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index fdf7639..41f75e8 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -26,6 +26,18 @@ Widget spacerVertical(double v) { width: double.infinity, ); } + +String convertFileToBase64(File file) { + List imageBytes = file.readAsBytesSync(); + return base64Encode(imageBytes); +} + +// String checkFileExt(String path) { +// String ex = p.extension(path); +// var parts = ex.split('.'); +// return parts[1] ?? "png"; // '.dart' +// } + Future delay(int millis) async { return await Future.delayed(Duration(milliseconds: millis)); } @@ -104,6 +116,7 @@ Widget mFlex(int f) { ), ); } + Widget mExp(int f) { return Expanded( flex: f, @@ -197,17 +210,19 @@ String base64String(Uint8List data) { return base64Encode(data); } -Widget overLayWidget({double? width, double? height,List? color}) { +Widget overLayWidget({double? width, double? height, List? color}) { return Container( width: width ?? double.infinity, height: height ?? 60, decoration: BoxDecoration( gradient: LinearGradient( - colors: color!=null?color:[ - Colors.black.withOpacity(0.2), - Colors.black.withOpacity(0.1), - Colors.black.withOpacity(0.004), - ], + colors: color != null + ? color + : [ + Colors.black.withOpacity(0.2), + Colors.black.withOpacity(0.1), + Colors.black.withOpacity(0.004), + ], begin: Alignment.topCenter, end: Alignment.bottomCenter, tileMode: TileMode.clamp, @@ -286,11 +301,6 @@ Decoration containerColorRadiusBorder(Color background, double radius, Color col ); } - - - - - //Decoration appGradient = BoxDecoration( // gradient: LinearGradient( // colors: [ diff --git a/lib/widgets/tab/login_email_tab.dart b/lib/widgets/tab/login_email_tab.dart new file mode 100644 index 0000000..7c29b63 --- /dev/null +++ b/lib/widgets/tab/login_email_tab.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +enum ClassType { EMAIL, NUMBER } + +class LoginEmailTab extends StatefulWidget { + Function(ClassType) onSelection; + + LoginEmailTab({required this.onSelection}); + + @override + State createState() => _LoginEmailTabState(); +} + +class _LoginEmailTabState extends State { + ClassType type = ClassType.NUMBER; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + child: Row( + children: [ + Expanded( + child: InkWell( + onTap: () { + type = ClassType.NUMBER; + widget.onSelection(ClassType.NUMBER); + }, + child: Container( + width: double.infinity, + height: 45, + color: type == ClassType.NUMBER ? Colors.blue : Colors.transparent, + child: Center( + child: Text( + "Number", + style: TextStyle( + color: type == ClassType.NUMBER ? Colors.white : Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + Expanded( + child: InkWell( + onTap: () { + type = ClassType.EMAIL; + widget.onSelection(ClassType.EMAIL); + }, + child: Container( + width: double.infinity, + height: 45, + color: type == ClassType.EMAIL ? Colors.blue : Colors.transparent, + child: Center( + child: Text( + "Email", + style: TextStyle( + color: type == ClassType.EMAIL ? Colors.white : Colors.black, + fontWeight: FontWeight.bold, + ), + )), + ), + ), + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 13c55cd..5ad0a2d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -50,6 +50,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" crypto: dependency: transitive description: @@ -116,13 +123,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" flutter_svg: dependency: "direct dev" description: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.22.0" + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -139,7 +153,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "8.0.8" + version: "8.0.9" http: dependency: "direct dev" description: @@ -154,6 +168,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + image_picker: + dependency: "direct dev" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.1" injector: dependency: "direct dev" description: @@ -189,6 +224,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -216,14 +258,14 @@ packages: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.5.1+1" + version: "1.0.0" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "0.2.1" + version: "1.0.0" path_provider: dependency: "direct dev" description: @@ -279,7 +321,21 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "7.1.0" + version: "9.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2+1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.4" permission_handler_platform_interface: dependency: transitive description: @@ -287,6 +343,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.7.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" petitparser: dependency: transitive description: @@ -431,7 +494,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.8" typed_data: dependency: transitive description: @@ -475,5 +538,5 @@ packages: source: hosted version: "5.3.1" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + dart: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9390e9e..b48ef15 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,11 +50,12 @@ dev_dependencies: provider: ^6.0.0 easy_localization: ^3.0.0 http: ^0.13.3 - permission_handler: 7.1.0 - flutter_svg: ^0.22.0 + permission_handler: ^9.2.0 + flutter_svg: ^1.0.3 sizer: ^2.0.15 - fluttertoast: ^8.0.8 + fluttertoast: ^8.0.9 shared_preferences: ^2.0.6 + image_picker: ^0.8.4+4 # For information on the generic Dart part of this file, see the