From b228e88a94ecbd5a140ecc1910944bf5e008cafd Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" <> Date: Tue, 2 Jul 2024 15:59:58 +0300 Subject: [PATCH] user profile & settings & api's --- lib/config/routes.dart | 8 + lib/models/user_models/user.dart | 45 +- lib/repositories/user_repo.dart | 2 +- lib/theme/app_theme.dart | 1 + lib/utils/app_permission_handler.dart | 23 +- lib/view_models/ad_view_model.dart | 2 +- lib/view_models/service_view_model.dart | 615 ++++++++++++++++++ lib/view_models/user_view_model.dart | 24 +- lib/views/profile/profile_view.dart | 384 +++++------ .../provider_license_page.dart | 220 +++++++ .../setting_options/setting_option_help.dart | 89 +++ .../setting_options_language.dart | 169 ++++- .../widgets/custom_setting_options_tile.dart | 48 +- lib/views/user/change_password_page.dart | 2 +- lib/views/user/edit_account_page.dart | 87 ++- lib/views/user/login_with_password_page.dart | 2 +- lib/widgets/common_widgets/app_bar.dart | 161 ++--- 17 files changed, 1482 insertions(+), 400 deletions(-) create mode 100644 lib/view_models/service_view_model.dart create mode 100644 lib/views/setting_options/provider_license_page.dart create mode 100644 lib/views/setting_options/setting_option_help.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 6ea75a3..ae8892a 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -4,6 +4,9 @@ import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/views/profile/profile_view.dart'; +import 'package:mc_common_app/views/setting_options/provider_license_page.dart'; +import 'package:mc_common_app/views/setting_options/setting_option_help.dart'; +import 'package:mc_common_app/views/setting_options/setting_options_language.dart'; import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart'; @@ -89,10 +92,12 @@ class AppRoutes { static const String settingOptionsFaqs = "/settingOptionsFaqs"; static const String settingOptionsLanguages = "/settingOptionsLanguages"; static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; + static const String settingOptionsHelp = "/settingOptionsHelp"; //Profile Screen static const String profileView = "/profileView"; + static const providerLicensePage = "/providerLicensePage"; //Chat static const String chatView = "/chatView"; @@ -135,6 +140,9 @@ class AppRoutes { changeEmailPage: (context) => const ChangeEmailPage(), editAccountPage: (context) => const EditAccountPage(), profileView: (context) => const ProfileScreen(), + settingOptionsLanguages: (context) => const SettingOptionsLanguage(), + settingOptionsHelp: (context) => const SettingOptionsHelp(), + providerLicensePage: (context) => ProviderLicensePage(), }; } diff --git a/lib/models/user_models/user.dart b/lib/models/user_models/user.dart index 2d2b83c..27e38c0 100644 --- a/lib/models/user_models/user.dart +++ b/lib/models/user_models/user.dart @@ -3,6 +3,7 @@ // final user = userFromJson(jsonString); import 'dart:convert'; +import 'dart:io'; import '../../classes/app_state.dart'; import '../../utils/enums.dart'; @@ -68,27 +69,27 @@ class UserData { } class UserInfo { - UserInfo({ - this.id, - this.userId, - this.firstName, - this.lastName, - this.mobileNo, - this.email, - this.userImageUrl, - this.roleId, - this.roleName, - this.isEmailVerified, - this.serviceProviderBranch, - this.isVerified, - this.userRoles, - this.isCustomer, - this.isProviderDealership, - this.isDealershipUser, - this.providerId, - this.customerId, - this.dealershipId, - }); + UserInfo( + {this.id, + this.userId, + this.firstName, + this.lastName, + this.mobileNo, + this.email, + this.userImageUrl, + this.roleId, + this.roleName, + this.isEmailVerified, + this.serviceProviderBranch, + this.isVerified, + this.userRoles, + this.isCustomer, + this.isProviderDealership, + this.isDealershipUser, + this.providerId, + this.customerId, + this.dealershipId, + this.userLocalImage}); int? id; String? userId; @@ -109,6 +110,7 @@ class UserInfo { dynamic providerId; int? customerId; dynamic dealershipId; + File? userLocalImage; UserInfo.fromJson(Map json) { if (json["roleID"] == 5) { @@ -137,6 +139,7 @@ class UserInfo { providerId = json["providerID"]; customerId = json["customerID"]; dealershipId = json["dealershipID"]; + userLocalImage = null; } // factory UserInfo.fromJson(Map json) => UserInfo( diff --git a/lib/repositories/user_repo.dart b/lib/repositories/user_repo.dart index 4e6756e..e423e4f 100644 --- a/lib/repositories/user_repo.dart +++ b/lib/repositories/user_repo.dart @@ -275,7 +275,7 @@ class UserRepoImp implements UserRepo { String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token $t"); - return await injector.get().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t); + return await injector.get().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t,); } @override diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index 70a101b..1fa9447 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -9,6 +9,7 @@ class AppTheme { return ThemeData( fontFamily: !isArabic ? MyFonts.poppinsFont : null, primaryColor: primaryColor, + useMaterial3: false, primaryTextTheme: const TextTheme( titleLarge: TextStyle(color: Colors.white), ), colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.orange).copyWith(background: Colors.white), diff --git a/lib/utils/app_permission_handler.dart b/lib/utils/app_permission_handler.dart index b9e2e87..a4e6150 100644 --- a/lib/utils/app_permission_handler.dart +++ b/lib/utils/app_permission_handler.dart @@ -73,16 +73,18 @@ class AppPermissions { ); } - Future getAndroidDeviceInfo() async { - AndroidDeviceInfo deviceInfo = await DeviceInfoPlugin().androidInfo; - return deviceInfo.version.sdkInt; + static Future getAndroidDeviceInfo() async { + if (Platform.isAndroid) { + AndroidDeviceInfo deviceInfo = await DeviceInfoPlugin().androidInfo; + return deviceInfo.version.sdkInt; + } else { + return 0; + } } static Future checkStoragePermissions(BuildContext context) async { - bool permissionStatus; - final deviceInfo = await DeviceInfoPlugin().androidInfo; - - if (deviceInfo.version.sdkInt > 32) { + bool permissionStatus = false; + if (await getAndroidDeviceInfo() > 32) { permissionStatus = await Permission.photos.request().isGranted; if (permissionStatus) { return true; @@ -91,7 +93,12 @@ class AppPermissions { return false; } } else { - permissionStatus = await Permission.storage.request().isGranted; + if (Platform.isAndroid) { + permissionStatus = await Permission.storage.request().isGranted; + } else if (Platform.isIOS) { + print("Ios"); + permissionStatus = await Permission.photos.request().isGranted; + } if (permissionStatus) { return true; } else { diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 7973bad..ae9bebe 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -1643,7 +1643,7 @@ class AdVM extends BaseVM { logger.d("Step 3"); if (vehicleBrands.isNotEmpty) { for (var vBrands in vehicleBrands) { - if (vBrands.id == previousDetails.vehicle.) { + if (vBrands.id == previousDetails.vehicle!.id) { // BrandsId need to be matched here. vBrands.isSelected = true; break; diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..44fe97b --- /dev/null +++ b/lib/view_models/service_view_model.dart @@ -0,0 +1,615 @@ +// import 'dart:convert'; +// import 'dart:io'; +// +// import 'package:car_provider_app/repositories/branch_repo.dart'; +// import 'package:mc_common_app/models/m_response.dart'; +// +// import 'package:mc_common_app/models/model/provider_model.dart'; +// import 'package:mc_common_app/models/profile/categroy.dart'; +// import 'package:mc_common_app/models/profile/document.dart'; +// import 'package:mc_common_app/models/profile/services.dart'; +// import 'package:mc_common_app/models/services/branch_model.dart'; +// import 'package:mc_common_app/models/services/item_model.dart'; +// import 'package:mc_common_app/models/services/service_model.dart'; +// import 'package:mc_common_app/models/user/cities.dart'; +// import 'package:mc_common_app/models/user/country.dart'; +// import 'package:mc_common_app/repositories/common_repo.dart'; +// import 'package:mc_common_app/services/common_services.dart'; +// import 'package:mc_common_app/utils/enums.dart'; +// import 'package:mc_common_app/utils/utils.dart'; +// import 'package:mc_common_app/view_models/base_view_model.dart'; +// import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; +// import 'package:file_picker/file_picker.dart'; +// +// class ServiceVM extends BaseVM { +// final BranchRepo branchRepo; +// final CommonAppServices commonServices; +// final CommonRepo commonRepo; +// +// ServiceVM({required this.branchRepo, required this.commonServices, required this.commonRepo}); +// +// //Documents & Branches +// int selectedBranchStatus = 3; +// Document? document; +// ProviderModel? branchs; +// Country? country; +// Cities? cities; +// DropValue? countryValue; +// DropValue? cityValue; +// List countryDropList = []; +// List citiesDropList = []; +// double latitude = 0, longitude = 0; +// int role = -1, countryId = -1, cityId = -1; +// String countryCode = "", address = "", branchName = "", branchDescription = ""; +// +// getServiceProviderDocument(int providerId) async { +// setState(ViewState.busy); +// document = await branchRepo.getServiceProviderDocument(providerId); +// setState(ViewState.idle); +// } +// +// selectFile(int index) async { +// File? file = await commonServices.pickFile(fileType: FileType.custom, allowedExtensions: ['png', 'pdf', 'jpeg']); +// +// if (file != null) { +// int sizeInBytes = file.lengthSync(); +// // double sizeInMb = sizeInBytes / (1024 * 1024); +// if (sizeInBytes > 1000) { +// Utils.showToast("File is larger then 1KB"); +// } else { +// document!.data![index].document = Utils.convertFileToBase64(file); +// document!.data![index].fileExt = Utils.checkFileExt(file.path); +// document!.data![index].documentUrl = file.path; +// setState(ViewState.idle); +// } +// } else { +// // User canceled the picker +// } +// } +// +// Future updateDocument(List? data) async { +// return await branchRepo.serviceProviderDocumentsUpdate(data); +// } +// +// //Create new branch +// getBranchAndServices() async { +// setState(ViewState.busy); +// branchs = await branchRepo.getBranchAndServices(); +// setState(ViewState.idle); +// } +// +// getAllCountriesList(BranchModel? branchData, String countryCode) async { +// setState(ViewState.busy); +// resetValues(); +// country = await commonRepo.getAllCountries(); +// country!.data?.forEach((element) { +// if (branchData != null) if (branchData.id != null) { +// if (element.id == branchData.countryID) { +// countryValue = DropValue(element.id ?? 0, countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), element.countryCode ?? ""); +// } +// } +// countryDropList.add(DropValue(element.id ?? 0, countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), element.countryCode ?? "")); +// }); +// if (branchData != null) if (branchData.id != null) getAllCities(branchData, countryCode); +// setState(ViewState.idle); +// } +// +// getAllCities(BranchModel? branchData, String countryCode) async { +// setState(ViewState.busy); +// citiesDropList.clear(); +// cities = null; +// cityId = -1; +// cities = await commonRepo.getAllCites(countryId.toString()); +// cities!.data?.forEach((element) { +// if (branchData != null && branchData.id != null) { +// if (element.id == branchData.cityId) { +// address = branchData.address!; +// branchName = branchData.branchName!; +// branchDescription = branchData.branchDescription!; +// latitude = double.parse(branchData.latitude ?? ""); +// longitude = double.parse(branchData.longitude ?? ""); +// countryId = branchData.countryID!; +// cityId = branchData.cityId!; +// cityValue = DropValue(element.id ?? 0, countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), element.id.toString() ?? ""); +// } +// } +// citiesDropList.add(DropValue(element.id ?? 0, countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), element.id.toString() ?? "")); +// }); +// setState(ViewState.idle); +// } +// +// Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude) async { +// return await branchRepo.createBranch(branchName, branchDescription, cityId.toString(), address, latitude.toString(), longitude.toString()); +// } +// +// Future updateBranch(int id, String branchName, String branchDescription, String cityId, String address, String latitude, String longitude, {bool isNeedToDelete = true}) async { +// return await branchRepo.updateBranch(id ?? 0, branchName, branchDescription, cityId.toString(), address, latitude.toString(), longitude.toString()); +// } +// +// resetValues() { +// countryCode = ""; +// address = ""; +// branchName = ""; +// branchDescription = ""; +// latitude = 0; +// longitude = 0; +// role = -1; +// countryId = -1; +// cityId = -1; +// countryDropList.clear(); +// countryId = -1; +// cityId = -1; +// cities = null; +// categoryDropList = []; +// servicesDropList = []; +// services = null; +// } +// +// //Create Services +// Services? services; +// List categoryDropList = []; +// List servicesDropList = []; +// +// fetchBranchCategory(String countryCode) async { +// categoryDropList.clear(); +// servicesDropList = []; +// services = null; +// setState(ViewState.busy); +// Category? category = await branchRepo.fetchBranchCategory(); +// category.data?.forEach((element) { +// categoryDropList.add(DropValue( +// element.id ?? 0, +// ((element.categoryName!.isEmpty +// ? "N/A" +// : countryCode == "SA" +// ? element.categoryNameN +// : element.categoryName) ?? +// "N/A"), +// "")); +// }); +// setState(ViewState.idle); +// } +// +// fetchServicesByCategoryId(String categoryId) async { +// servicesDropList = []; +// setState(ViewState.busy); +// services = await branchRepo.fetchServicesByCategoryId(categoryId); +// +// for (var element in services!.data!) { +// servicesDropList.add(DropValue(element.id ?? 0, element.description ?? "N/aA", "")); +// } +// setState(ViewState.idle); +// } +// +// Future createService(List> map) async { +// return await branchRepo.createService(map); +// } +// +// Future updateServices(List> map) async { +// return await branchRepo.updateService(map); +// } +// +// updateSelectedBranchType(int status) { +// selectedBranchStatus = status; +// notifyListeners(); +// } +// +// List? matchedServices; +// bool isAllSelected = false; +// +// getAllMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { +// matchedServices = null; +// MResponse response = await branchRepo.getMatchedServices(oldBranchId, newBranchId, categoryId); +// matchedServices = []; +// if (response.messageStatus == 1) { +// matchedServices = List.from(response.data.map((x) => ServiceModel.fromJson(x))); +// } +// notifyListeners(); +// } +// +// selectAllServices(bool value) { +// for (var element in matchedServices!) { +// element.isExpandedOrSelected = value; +// for (var item in element.serviceItems!) { +// item.isUpdateOrSelected = value; +// } +// } +// isAllSelected = value; +// notifyListeners(); +// } +// +// updateServiceItem(int serviceIndex, bool value) { +// for (var element in matchedServices![serviceIndex].serviceItems!) { +// element.isUpdateOrSelected = value; +// } +// notifyListeners(); +// } +// +// copyItems(int index, List copiedItems) { +// matchedServices![index].serviceItems = copiedItems; +// notifyListeners(); +// } +// +// Future duplicateItems(Map map) async { +// return await branchRepo.duplicateItems(map); +// } +// } + +import 'dart:io'; +import 'package:mc_common_app/repositories/branch_repo.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/models/general_models/m_response.dart'; +import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; +import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart'; +import 'package:mc_common_app/models/provider_branches_models/profile/document.dart'; +import 'package:mc_common_app/models/provider_branches_models/profile/services.dart'; +import 'package:mc_common_app/models/provider_branches_models/provider_model.dart'; +import 'package:mc_common_app/models/services_models/item_model.dart'; +import 'package:mc_common_app/models/services_models/service_model.dart'; +import 'package:mc_common_app/models/user_models/branch_user.dart'; +import 'package:mc_common_app/models/user_models/cities.dart'; +import 'package:mc_common_app/models/user_models/country.dart'; +import 'package:mc_common_app/repositories/common_repo.dart'; +import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; + +class ServiceVM extends BaseVM { + final BranchRepo branchRepo; + final CommonAppServices commonServices; + final CommonRepo commonRepo; + + ServiceVM({ + required this.branchRepo, + required this.commonServices, + required this.commonRepo, + }); + + // Documents & Branches + int selectedBranchStatus = 3; + Document? document; + ProviderModel? branches; + Country? country; + Cities? cities; + DropValue? countryValue; + DropValue? cityValue; + List countryDropList = []; + List citiesDropList = []; + double latitude = 0; + double longitude = 0; + int role = -1; + int countryId = -1; + int cityId = -1; + String countryCode = ""; + String address = ""; + String branchName = ""; + String branchDescription = ""; + + Future getServiceProviderDocument(int providerId) async { + setState(ViewState.busy); + document = await branchRepo.getServiceProviderDocument(providerId); + setState(ViewState.idle); + } + + Future selectFile(BuildContext context, int index) async { + final status = await AppPermissions.checkStoragePermissions(context); + if (status) { + final File? file = await commonServices.pickFile( + context, + fileType: FileType.custom, + allowedExtensions: ['png', 'pdf', 'jpeg'], + ); + + // if (result != null) { + // final File file = File(result.files.single.path!); + // final int sizeInBytes = file.lengthSync(); + // + // if (sizeInBytes > 1000) { + // Utils.showToast("File is larger than 1KB"); + // } else { + // document!.data![index].document = Utils.convertFileToBase64(file); + // document!.data![index].fileExt = Utils.checkFileExt(file.path); + // document!.data![index].documentUrl = file.path; + // setState(ViewState.idle); + // } + // } else { + // // User canceled the picker + // } + + if (file != null) { + int sizeInBytes = file.lengthSync(); + // double sizeInMb = sizeInBytes / (1024 * 1024); + if (sizeInBytes > 1000000) { + Utils.showToast("File is larger then 1KB"); + } else { + document!.data![index].document = Utils.convertFileToBase64(file); + document!.data![index].fileExt = Utils.checkFileExt(file.path); + document!.data![index].documentUrl = file.path; + setState(ViewState.idle); + } + } else { + // User canceled the picker + } + } + } + + Future updateDocument(List? data) async { + return await branchRepo.serviceProviderDocumentsUpdate(data); + } + + // Create new branch + Future getBranchAndServices() async { + setState(ViewState.busy); + branches = await branchRepo.getBranchAndServices(); + setState(ViewState.idle); + } + + Future getAllCountriesList(BranchDetailModel? branchData, String countryCode) async { + cities = null; + country = null; + setState(ViewState.busy); + resetValues(); + country = await commonRepo.getAllCountries(); + country!.data?.forEach((element) { + if (branchData != null && branchData.id != null) { + if (element.id == branchData.countryID) { + countryId = element.id ?? -1; + countryValue = DropValue( + element.id ?? 0, + countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), + element.countryCode ?? "", + ); + } + } + countryDropList.add( + DropValue( + element.id ?? 0, + countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), + element.countryCode ?? "", + ), + ); + }); + if (branchData != null && branchData.id != null) { + await getAllCities(branchData, countryCode); + } + setState(ViewState.idle); + } + + Future getAllCities(BranchDetailModel? branchData, String countryCode) async { + setState(ViewState.busy); + citiesDropList = []; + cities = null; + cityId = -1; + cityValue = null; + cities = await commonRepo.getAllCites(countryId.toString()); + cities!.data?.forEach((element) { + if (branchData != null && branchData.id != null) { + if (element.id == branchData.cityId) { + address = branchData.address!; + branchName = branchData.branchName!; + branchDescription = branchData.branchDescription!; + latitude = double.parse(branchData.latitude ?? ""); + longitude = double.parse(branchData.longitude ?? ""); + countryId = branchData.countryID!; + cityId = branchData.cityId!; + cityValue = DropValue( + element.id ?? 0, + countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), + element.id.toString() ?? "", + ); + } + } + citiesDropList.add( + DropValue( + element.id ?? 0, + countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), + element.id.toString() ?? "", + ), + ); + }); + setState(ViewState.idle); + } + + Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude) async { + return await branchRepo.createBranch(branchName, branchDescription, cityId.toString(), address, latitude.toString(), longitude.toString()); + } + + Future updateBranch( + int id, + String branchName, + String branchDescription, + String cityId, + String address, + String latitude, + String longitude, { + bool isNeedToDelete = true, + }) async { + return await branchRepo.updateBranch( + id ?? 0, + branchName, + branchDescription, + cityId.toString(), + address, + latitude.toString(), + longitude.toString(), + ); + } + + void resetValues() { + countryCode = ""; + address = ""; + branchName = ""; + branchDescription = ""; + latitude = 0; + longitude = 0; + role = -1; + countryId = -1; + cityId = -1; + countryDropList.clear(); + countryId = -1; + cityId = -1; + cities = null; + categoryDropList = []; + servicesDropList = []; + services = null; + } + + // Create Services + Services? services; + List categoryDropList = []; + List servicesDropList = []; + + Future fetchBranchCategory(String countryCode) async { + categoryDropList.clear(); + servicesDropList = []; + services = null; + setOnlyState(ViewState.busy); + Category? category = await branchRepo.fetchBranchCategory(); + category.data?.forEach((element) { + categoryDropList.add( + DropValue( + element.id ?? 0, + ((element.categoryName!.isEmpty + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? + "N/A"), + "", + ), + ); + }); + setState(ViewState.idle); + } + + Future fetchServicesByCategoryId(int categoryId) async { + servicesDropList = []; + setState(ViewState.busy); + services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: categoryId); // to get all the services + for (var element in services!.data!) { + servicesDropList.add( + DropValue( + element.id ?? 0, + element.description ?? "N/A", + "", + ), + ); + } + setState(ViewState.idle); + } + + Future fetchProviderServices(String branchID, String categoryId) async { + servicesDropList = []; + services = null; + setState(ViewState.busy); + services = await branchRepo.fetchProviderServices(branchID, categoryId); + + for (var element in services!.data!) { + servicesDropList.add( + DropValue( + element.id ?? 0, + element.description ?? "N/A", + "", + ), + ); + } + setState(ViewState.idle); + } + + Future createService(List> map) async { + return await branchRepo.createService(map); + } + + Future updateServices(List> map) async { + return await branchRepo.updateService(map); + } + + void updateSelectedBranchType(int status) { + selectedBranchStatus = status; + notifyListeners(); + } + + List? matchedServices; + bool isAllSelected = false; + + Future getAllMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { + matchedServices = null; + final MResponse response = await branchRepo.getMatchedServices(oldBranchId, newBranchId, categoryId); + matchedServices = []; + + if (response.messageStatus == 1) { + matchedServices = List.from(response.data.map((x) => ServiceModel.fromJson(x))); + } + notifyListeners(); + } + + void selectAllServices(bool value) { + for (var element in matchedServices!) { + element.isExpandedOrSelected = value; + for (var item in element.serviceItems!) { + item.isUpdateOrSelected = value; + } + } + isAllSelected = value; + notifyListeners(); + } + + void updateServiceItem(int serviceIndex, bool value) { + for (var element in matchedServices![serviceIndex].serviceItems!) { + element.isUpdateOrSelected = value; + } + notifyListeners(); + } + + void copyItems(int index, List copiedItems) { + matchedServices![index].serviceItems = copiedItems; + notifyListeners(); + } + + Future duplicateItems(Map map) async { + return await branchRepo.duplicateItems(map); + } + + // Branch Users + List allProviderDealersList = []; + List branchUserList = []; + + getAllProviderDealers(Map map) async { + setState(ViewState.busy); + MResponse response = await branchRepo.getAllProviderDealers(map); + if (response.messageStatus == 1) { + allProviderDealersList = []; + allProviderDealersList = List.from(response.data.map((x) => BranchUser.fromJson(x))); + } + setState(ViewState.idle); + } + + Future getBranchUsers(Map map) async { + setState(ViewState.busy); + MResponse response = await branchRepo.getBranchUsers(map); + if (response.messageStatus == 1) { + branchUserList = []; + branchUserList = List.from(response.data.map((x) => BranchUser.fromJson(x))); + } + setState(ViewState.idle); + } + + Future assignDealerToBranch(Map map) async { + MResponse response = await branchRepo.assignDealerToBranch(map); + return response; + } + + Future removeDealerFromBranch(Map map) async { + MResponse response = await branchRepo.removeDealerFromBranch(map); + return response; + } + + Future addNewServiceInAppointment(Map map) async { + MResponse response = await branchRepo.addNewServicesInAppointment(map); + return response; + } +} diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 8df39d9..ef18a5d 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -1,11 +1,14 @@ import 'dart:convert'; +import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:http/http.dart'; import 'package:huawei_fido/huawei_fido.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:local_auth/local_auth.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -22,12 +25,14 @@ import 'package:mc_common_app/models/user_models/confirm_password.dart'; import 'package:mc_common_app/models/user_models/country.dart'; import 'package:mc_common_app/models/user_models/forget_password_otp_compare.dart'; import 'package:mc_common_app/models/user_models/forget_password_otp_request.dart'; +import 'package:mc_common_app/models/user_models/image_response.dart'; import 'package:mc_common_app/models/user_models/login_password.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/models/user_models/user.dart'; import 'package:mc_common_app/models/user_models/verify_email.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_auth_service.dart'; +import 'package:mc_common_app/services/common_services.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/shared_prefrence.dart'; @@ -40,8 +45,9 @@ import 'package:mc_common_app/widgets/tab/login_email_tab.dart'; class UserVM extends BaseVM { final UserRepo userRepo; + final CommonAppServices commanServices; - UserVM({required this.userRepo}); + UserVM({required this.userRepo, required this.commanServices}); bool completeProfilePageCheckbox = false; @@ -582,4 +588,20 @@ class UserVM extends BaseVM { return true; } } + + Future updateUserImage(BuildContext context) async { + File? myPick = await commanServices.pickFile(context, fileType: FileType.image); + if (myPick != null) { + userRepo.updateUserImage(encodeBase64Image(myPick)).whenComplete(() { + AppState().getUser.data!.userInfo!.userLocalImage = myPick; + }); + } + notifyListeners(); + } + + String encodeBase64Image(File file) { + List imageBytes = file.readAsBytesSync(); + print(imageBytes); + return base64Encode(imageBytes); + } } diff --git a/lib/views/profile/profile_view.dart b/lib/views/profile/profile_view.dart index b7a72d6..4f27aba 100644 --- a/lib/views/profile/profile_view.dart +++ b/lib/views/profile/profile_view.dart @@ -1,264 +1,184 @@ +import 'dart:io'; import 'dart:ui'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; +import 'package:mc_common_app/view_models/service_view_model.dart'; +import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; +import 'package:mc_common_app/views/user/change_password_page.dart'; +import 'package:mc_common_app/views/user/change_password_page.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; -class ProfileScreen extends StatelessWidget { +class ProfileScreen extends StatefulWidget { const ProfileScreen({Key? key}) : super(key: key); - // final ImagePicker _picker = ImagePicker(); + @override + State createState() => _ProfileScreenState(); +} - Widget showItem({required String icon, required String title, required VoidCallback onTap, String? subTitle}) { - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 16, - height: 16, - child: SvgPicture.asset( - icon, - color: subTitle == null ? MyColors.black : MyColors.primaryColor, - ), - ), - 12.width, - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - title.toText(isBold: true, fontSize: 14), - if (subTitle != null) subTitle.toText(fontSize: 14, color: MyColors.primaryColor), - ], - ), - ), - const Icon( - Icons.arrow_forward, - size: 16, - ), - ], - ), - ), - ); +class _ProfileScreenState extends State { + late UserVM userVM; + + @override + void initState() { + // TODO: implement initState + super.initState(); + userVM = Provider.of(context, listen: false); } @override Widget build(BuildContext context) { - return Scaffold( extendBody: true, backgroundColor: const Color(0xffefefef), - body: Stack( - children: [ - Column( - children: [ - Expanded( - flex: 3, - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage(MyAssets.icLogoWhitePng), - fit: BoxFit.cover, + body: Consumer(builder: (_, model, __) { + return Stack( + // fit: StackFit.loose, + children: [ + Column( + children: [ + Expanded( + flex: 3, + child: Container( + decoration: BoxDecoration( + image: AppState().getUser.data!.userInfo!.userLocalImage != null + ? DecorationImage( + image: FileImage(AppState().getUser.data!.userInfo!.userLocalImage!), + fit: BoxFit.cover, + ) + : AppState().getUser.data!.userInfo!.userImageUrl != null + ? DecorationImage( + image: CachedNetworkImageProvider( + AppState().getUser.data!.userInfo!.userImageUrl, + ), + fit: BoxFit.cover, + ) + : DecorationImage( + image: AssetImage( + MyAssets.icLogoWhitePng, + ), + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 7.0, sigmaY: 7.0), + child: Container( + // height:, + color: Colors.white.withOpacity(0.0), + ), ), ), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 7.0, sigmaY: 7.0), - child: Container( - // height:, - color: Colors.white.withOpacity(0.0), + ), + Expanded( + flex: 8, + child: Container( + width: double.infinity, + color: Colors.white, + child: ListView( + children: [ + 20.height, + "${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName ?? ""}".toText(fontSize: 20).paddingOnly(left: 25), + Column( + children: [ + CustomProfileOptionsTile( + titleText: "Country", + subtitleText: "Saudi Arabia", + needBorderBelow: true, + onTap: () {}, + ), + CustomProfileOptionsTile( + titleText: "Email", + subtitleText: "${AppState().getUser.data!.userInfo!.email}", + needBorderBelow: true, + onTap: () { + Navigator.pushNamed(context, AppRoutes.changeEmailPage); + }, + ), + CustomProfileOptionsTile( + titleText: "Phone Number", + subtitleText: "${AppState().getUser.data!.userInfo!.email}", + needBorderBelow: true, + onTap: () { + Navigator.pushNamed(context, AppRoutes.changeMobilePage); + }, + ), + CustomProfileOptionsTile( + titleText: "Password", + subtitleText: "************", + onTap: () { + Navigator.pushNamed(context, AppRoutes.changePassword); + }, + ), + ], + ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 20, 24, 0), borderRadius: 0), + ], ), ), ), - ), - Expanded( - flex: 8, - child: Container( - width: double.infinity, - color: Colors.white, - child: ListView( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - height: 90, - alignment: Alignment.centerLeft, - child: ClipOval( - child: Image.asset( - MyAssets.carBanner, - width: 90, - height: 90, - fit: BoxFit.fill, - ), - )), - Container( - height: 35, - width: 35, - decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)), - child: const Icon(Icons.edit_note, color: MyColors.darkIconColor, size: 27).paddingOnly(left: 5), - ).onPress(() {}) - ], - ).horPaddingMain(), - 10.height, - "${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName ?? ""}".toText(fontSize: 20).paddingOnly(left: 25), - Column( - children: [ - CustomProfileOptionsTile( - titleText: "Country", - subtitleText: "Saudi Arabia", - needBorderBelow: true, - onTap: () {}, - ), - CustomProfileOptionsTile( - titleText: "Email", - subtitleText: "${AppState().getUser.data!.userInfo!.email}", - needBorderBelow: true, - onTap: () {}, - ), - CustomProfileOptionsTile( - titleText: "Phone Number", - subtitleText: "${AppState().getUser.data!.userInfo!.email}", - needBorderBelow: true, - onTap: () {}, - ), - CustomProfileOptionsTile( - titleText: "Password", - subtitleText: "************", - onTap: () {}, + ], + ), + Positioned( + top: MediaQuery.of(context).size.height * 0.25, + child: Container( + height: 90, + alignment: Alignment.centerLeft, + child: ClipOval( + child: AppState().getUser.data!.userInfo!.userLocalImage != null + ? Image.file( + AppState().getUser.data!.userInfo!.userLocalImage!, + width: 90, + height: 90, + fit: BoxFit.fill, + ) + : CachedNetworkImage( + imageUrl: "${AppState().getUser.data!.userInfo!.userImageUrl}", + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, + fit: BoxFit.cover, + ), + ), ), - ], - ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), - ], - ), + placeholder: (context, url) => const Center(child: CircularProgressIndicator()), + errorWidget: (context, url, error) => const Icon(Icons.error), + width: 90, + height: 90, + fit: BoxFit.fill, + fadeInCurve: Curves.easeIn, + fadeInDuration: Duration(milliseconds: 1000), + useOldImageOnUrlChange: false, + ), ), + ).horPaddingMain(), + ), + Positioned( + top: MediaQuery.of(context).size.height * 0.30, + right: MediaQuery.of(context).size.height * 0.036, + child: Container( + height: 35, + width: 35, + decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)), + child: const Icon(Icons.edit_note, color: MyColors.darkIconColor, size: 27).paddingOnly(left: 5), + ).onPress( + () async { + model.updateUserImage(context); + }, ), - ], - ), - - CircleAvatar( - radius: 20, - backgroundColor: Colors.white, - child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4), - ).onPress(() { - Navigator.pop(context); - }).paddingOnly(left: 21, right: 21, top: 50), - - // SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // CircleAvatar( - // radius: 20, - // backgroundColor: Colors.white, - // child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4), - // ).onPress(() { - // Navigator.pop(context); - // }), - // ], - // ).paddingOnly(left: 21, right: 21, top: 50), - // Stack( - // children: [ - // Column( - // children: [ - // showItem( - // icon: MyAssets.icEmail, - // title: "Country", - // onTap: () { - // navigateWithName(context, AppRoutes.changeEmailPage); - // }, - // ), - // const Divider( - // height: 1, - // ), - // showItem( - // icon: MyAssets.icPassword, - // title: "Country", - // onTap: () { - // navigateWithName(context, AppRoutes.changePassword); - // }, - // ), - // const Divider( - // height: 1, - // ), - // showItem( - // icon: MyAssets.icPhoneNumber, - // title: "Country", - // onTap: () { - // navigateWithName(context, AppRoutes.changeMobilePage); - // }, - // ), - // const Divider( - // height: 1, - // ), - // ], - // ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.symmetric(horizontal: 21)), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Container( - // height: 68, - // alignment: Alignment.centerLeft, - // child: ClipOval( - // child: Image.asset( - // MyAssets.carBanner, - // width: 68, - // height: 68, - // fit: BoxFit.fill, - // ), - // )), - // InkWell( - // onTap: () {}, - // child: Container( - // padding: const EdgeInsets.only(left: 17, right: 17, top: 8, bottom: 8), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(30), - // color: Colors.black.withOpacity(.21), - // ), - // child: Row( - // children: [ - // const Icon(Icons.photo, color: Colors.white, size: 16), - // 4.width, - // "edit".toText(fontSize: 12, color: Colors.white), - // ], - // ), - // ), - // ), - // ], - // ), - // ], - // ).horPaddingMain(), - // ], - // ), - // ), - // Container( - // height: MediaQuery.of(context).size.height, - // width: MediaQuery.of(context).size.width, - // color: Colors.white, - // child: Column( - // children: [ - // CustomSettingOptionsTile( - // leadingWidget: const Icon(Icons.person, size: 20), titleText: "Invite Friends", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.myRequestsPage)), - // CustomSettingOptionsTile( - // leadingWidget: const Icon(Icons.help, size: 20), titleText: "Help", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs)), - // CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () => navigateWithName(context, AppRoutes.profileView)), - // ], - // ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, borderRadius: 0), - // ), - ], - ), + ), + ], + ); + }), ); } } diff --git a/lib/views/setting_options/provider_license_page.dart b/lib/views/setting_options/provider_license_page.dart new file mode 100644 index 0000000..89724c5 --- /dev/null +++ b/lib/views/setting_options/provider_license_page.dart @@ -0,0 +1,220 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/general_models/m_response.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; + +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/service_view_model.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; + +import 'package:mc_common_app/widgets/txt_field.dart'; + +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; + +class ProviderLicensePage extends StatefulWidget { + @override + State createState() => _ProviderLicensePageState(); +} + +class _ProviderLicensePageState extends State { + late ServiceVM branchVM; + + @override + void initState() { + // TODO: implement initState + super.initState(); + branchVM = Provider.of(context, listen: false); + branchVM.getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.defineLicences.tr(), + isRemoveBackButton: false, + ), + body: Consumer(builder: (_, model, __) { + return Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + // LocaleKeys.defineLicences.tr().toText20(isBold: true), + // 12.height, + // LocaleKeys.defineLicenese.tr().toText14(color: MyColors.lightTextColor), + 20.height, + showWidget(model), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(12.0), + child: ShowFillButton( + title: LocaleKeys.continu.tr(), + maxWidth: double.infinity, + onPressed: () { + if (AppState().getUser.data!.userInfo!.roleId == 5) { + if (validation(model)) { + updateDocument(model); + } else { + Utils.showToast("All document's are mandatory for Dealership Provider"); + } + } else { + updateDocument(model); + } + }, + ), + ), + ], + ); + }), + ); + } + + validation(ServiceVM model) { + bool valid = true; + model.document!.data!.forEach((element) { + if (element.documentUrl == null) { + valid = false; + } + }); + return valid; + } + + updateDocument(ServiceVM model) async { + Utils.showLoading(context); + MResponse res = await model.updateDocument(model.document!.data); + Utils.hideLoading(context); + if (res.messageStatus == 1) { + Utils.showToast("Documents uploaded successfully"); + } else { + Utils.showToast(res.message ?? ""); + } + } + + Widget showWidget(ServiceVM model) { + if (model.state == ViewState.idle) { + return model.document!.data!.isEmpty + ? Text("LocaleKeys.somethingWrong.tr()") + : ListView.separated( + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + model.document?.data![index].documentName ?? "", + style: const TextStyle( + fontSize: 16, + ), + ), + Padding( + padding: const EdgeInsets.only(left: 20, right: 20, top: 4, bottom: 8), + child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor, textAlign: TextAlign.center), + ), + TxtField( + hint: LocaleKeys.description.tr(), + maxLines: 3, + isBackgroundEnabled: true, + ), + if ((model.document?.data![index].documentUrl ?? "").toString().isNotEmpty) + Column( + children: [ + 8.height, + (model.document?.data![index].documentUrl ?? "").toString().toText( + fontSize: 14, + color: MyColors.lightTextColor, + ), + ], + ), + 8.height, + InkWell( + onTap: () async { + model.selectFile(context, index); + }, + child: Container( + width: double.infinity, + height: 45, + decoration: BoxDecoration( + color: Colors.transparent, + border: Border.all(color: MyColors.greyACColor, width: 2), + borderRadius: const BorderRadius.all(Radius.circular(0)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon( + Icons.attach_file, + size: 18, + color: MyColors.darkPrimaryColor, + ), + 8.width, + Text( + LocaleKeys.attachFile.tr(), + style: const TextStyle( + color: MyColors.darkPrimaryColor, + ), + ), + const Icon( + Icons.attach_file, + size: 18, + color: Colors.transparent, + ), + ], + ), + ), + ), + ], + ); + }, + separatorBuilder: (context, index) { + return 20.height; + }, + itemCount: model.document!.data!.length, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + } + +// selectFile(int index) async { +// FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['png', 'pdf', 'jpeg']); +// +// if (result != null) { +// File file = File(result.files.single.path ?? ""); +// int sizeInBytes = file.lengthSync(); +// // double sizeInMb = sizeInBytes / (1024 * 1024); +// if (sizeInBytes > 1000) { +// Utils.showToast("File is larger then 1KB"); +// } else { +// document!.data![index].document = Utils.convertFileToBase64(file); +// document!.data![index].fileExt = Utils.checkFileExt(file.path); +// setState(() { +// document!.data![index].documentUrl = result.files.single.path ?? ""; +// }); +// } +// } else { +// // User canceled the picker +// } +// } +} diff --git a/lib/views/setting_options/setting_option_help.dart b/lib/views/setting_options/setting_option_help.dart new file mode 100644 index 0000000..f875f05 --- /dev/null +++ b/lib/views/setting_options/setting_option_help.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class SettingOptionsHelp extends StatelessWidget { + const SettingOptionsHelp({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Help", + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Column( + children: [ + Expanded( + child: ListView( + children: [ + Column( + children: [ + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.info_rounded, size: 20), + titleText: "FAQs", + needBorderBelow: true, + onTap: () {}, + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.mail, size: 20), + titleText: "Contact Us", + needBorderBelow: true, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.lock, size: 20), + titleText: "Term & Privacy", + onTap: () => navigateWithName(context, AppRoutes.profileView), + ), + ], + ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), + ], + ), + ), + ], + ), + ); + } +} + +class CustomSettingOptionsTile extends StatelessWidget { + final Widget leadingWidget; + final String titleText; + final bool needBorderBelow; + final bool isForLanguage; + final Function() onTap; + + const CustomSettingOptionsTile({super.key, required this.leadingWidget, required this.onTap, required this.titleText, this.needBorderBelow = false, this.isForLanguage = false}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + leadingWidget, + 12.width, + titleText.toText(fontSize: 16), + ], + ), + isForLanguage ? const Icon(Icons.language, size: 18) : const Icon(Icons.arrow_forward, size: 18) + ], + ).onPress(onTap), + 5.height, + if (needBorderBelow) ...[ + const Divider(thickness: 1), + ], + ], + ); + } +} diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index f79546c..0e4f413 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -1,6 +1,12 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; @@ -23,28 +29,145 @@ class SettingOptionsLanguage extends StatelessWidget { ), body: Column( children: [ + // (AppState().currentAppType == AppType.provider) + // ? Expanded( - child: ListView( - children: [ - Column( - children: [ - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.person, size: 20), - titleText: "My Requests", - needBorderBelow: true, - onTap: () { - context.read().onNavbarTapped(4); - Navigator.pop(context); - }), - CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}), - CustomSettingOptionsTile(leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)), - ], - ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), - CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {}) - .toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0), - ], - ), - ), + child: ListView( + shrinkWrap: true, + padding: EdgeInsets.zero, + children: [ + Column( + children: [ + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.quickreply_outlined, size: 20), + titleText: "Requests", + needBorderBelow: true, + onTap: () { + context.read().onNavbarTapped(4); + Navigator.pop(context); + }), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.settings, size: 20), + titleText: "Settings", + needBorderBelow: true, + onTap: () {}, + //navigateWithName(context, AppRoutes.settingOptionsInviteFriends), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), + titleText: "Help", + needBorderBelow: true, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.person, size: 20), + titleText: "Account", + needBorderBelow: false, + onTap: () { + navigateWithName(context, AppRoutes.profileView); + // context.read().onNavbarTapped(4); + // Navigator.pop(context); + }), + ], + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.translate, size: 20), + titleText: "Language", + isForLanguage: true, + onTap: () { + if (EasyLocalization.of(context)?.currentLocale?.countryCode == "SA") { + context.setLocale(const Locale("en", "US")); + } else { + context.setLocale(const Locale('ar', 'SA')); + } + }, + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + (AppState().currentAppType == AppType.provider) + ? Column( + children: [ + CustomSettingOptionsTile( + leadingWidget: SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + MyAssets.icStar, + color: MyColors.primaryColor, + ), + ), + titleText: "My Subscriptions", + subTitle: "Silver", + isForLanguage: false, + needBorderBelow: true, + onTap: () { + navigateWithName(context, AppRoutes.mySubscriptionsPage); + }, + ), + CustomSettingOptionsTile( + leadingWidget: SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + MyAssets.icGroupStar, + // color: MyColors.primaryColor, + ), + ), + titleText: "Subscriptions", + subTitle: null, + isForLanguage: false, + needBorderBelow: true, + onTap: () { + navigateWithName(context, AppRoutes.subscriptionsPage); + }, + ), + CustomSettingOptionsTile( + leadingWidget: SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + MyAssets.icGroupStar, + // color: MyColors.primaryColor, + ), + ), + titleText: "Define Licenses", + subTitle: null, + isForLanguage: false, + needBorderBelow: false, + onTap: () { + navigateWithName(context, AppRoutes.providerLicensePage); + }, + ) + ], + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0) + : SizedBox(), + (AppState().currentAppType == AppType.provider) ? 10.height : SizedBox(), + ], + )), + // : Expanded( + // child: ListView( + // children: [ + // Column( + // children: [ + // CustomSettingOptionsTile( + // leadingWidget: const Icon(Icons.person, size: 20), + // titleText: "My Requests", + // needBorderBelow: true, + // onTap: () { + // context.read().onNavbarTapped(4); + // Navigator.pop(context); + // }), + // CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}), + // CustomSettingOptionsTile( + // leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)), + // ], + // ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), + // CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {}) + // .toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0), + // ], + // ), + // ), + (AppState().currentAppType == AppType.provider) ? const Text("Provider") : const Text("Customer"), Row( children: [ Expanded( @@ -59,9 +182,9 @@ class SettingOptionsLanguage extends StatelessWidget { ), ), ], - ).paddingAll(21), + ).paddingAll(0), ], - ), + ).paddingAll(21), ); } } diff --git a/lib/views/setting_options/widgets/custom_setting_options_tile.dart b/lib/views/setting_options/widgets/custom_setting_options_tile.dart index 2553d44..2a80cd8 100644 --- a/lib/views/setting_options/widgets/custom_setting_options_tile.dart +++ b/lib/views/setting_options/widgets/custom_setting_options_tile.dart @@ -1,8 +1,11 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:sizer/sizer.dart'; class CustomSettingOptionsTile extends StatelessWidget { final Widget leadingWidget; @@ -10,8 +13,17 @@ class CustomSettingOptionsTile extends StatelessWidget { final bool needBorderBelow; final bool isForLanguage; final Function() onTap; + final String? subTitle; - const CustomSettingOptionsTile({super.key, required this.leadingWidget, required this.onTap, required this.titleText, this.needBorderBelow = false, this.isForLanguage = false}); + const CustomSettingOptionsTile({ + super.key, + required this.leadingWidget, + required this.onTap, + required this.titleText, + this.needBorderBelow = false, + this.isForLanguage = false, + this.subTitle, + }); @override Widget build(BuildContext context) { @@ -20,17 +32,39 @@ class CustomSettingOptionsTile extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - leadingWidget, - 12.width, - titleText.toText(fontSize: 16), + Row( + children: [ + leadingWidget, + 12.width, + titleText.toText(fontSize: 16), + ], + ), + if (subTitle != null) + Row( + children: [ + 28.width, + subTitle!.toText(fontSize: 14, color: MyColors.primaryColor), + ], + ), ], ), - isForLanguage ? const Icon(Icons.language, size: 18) : const Icon(Icons.arrow_forward, size: 18) + isForLanguage + ? LocaleKeys.english.tr().toText(fontSize: 12, isUnderLine: true, color: MyColors.primaryColor) + + // const Icon( + // Icons.language, + // size: 18, + // color: MyColors.primaryColor, + // ) + // + + : const Icon(Icons.arrow_forward, size: 18) ], ).onPress(onTap), - 5.height, + subTitle != null ? 3.height : 5.height, if (needBorderBelow) ...[ const Divider(thickness: 1), ], diff --git a/lib/views/user/change_password_page.dart b/lib/views/user/change_password_page.dart index 45c8223..d35e3f5 100644 --- a/lib/views/user/change_password_page.dart +++ b/lib/views/user/change_password_page.dart @@ -36,7 +36,7 @@ class _ChangePasswordPageState extends State { child: Container( // width: double.infinity, // height: double.infinity, - padding: EdgeInsets.all(40), + padding: EdgeInsets.all(20), child: Column( children: [ LocaleKeys.enterNewPassword.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,), diff --git a/lib/views/user/edit_account_page.dart b/lib/views/user/edit_account_page.dart index bc02461..a2231b8 100644 --- a/lib/views/user/edit_account_page.dart +++ b/lib/views/user/edit_account_page.dart @@ -4,8 +4,11 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -70,29 +73,87 @@ class _EditAccountPageState extends State { ), ], ).toWhiteContainer(width: double.infinity, pading: EdgeInsets.symmetric(horizontal: 12)), - 21.height, + Column( children: [ - showItem( - icon: MyAssets.icStar, - title: "My Subscriptions", + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.quickreply_outlined, size: 20), + titleText: "Requests", + needBorderBelow: true, + onTap: () { + context.read().onNavbarTapped(4); + Navigator.pop(context); + }), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.settings, size: 20), + titleText: "Settings", + needBorderBelow: true, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), + titleText: "Help", + needBorderBelow: true, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.person, size: 20), + titleText: "Account", + needBorderBelow: false, + onTap: () { + context.read().onNavbarTapped(4); + Navigator.pop(context); + }), + ], + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.translate, size: 20), + titleText: "Language", + isForLanguage: true, + onTap: () {}, + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + Column( + children: [ + CustomSettingOptionsTile( + leadingWidget: SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + MyAssets.icStar, + color: MyColors.primaryColor, + ), + ), + titleText: "My Subscriptions", subTitle: "Silver", + isForLanguage: false, + needBorderBelow: true, onTap: () { navigateWithName(context, AppRoutes.mySubscriptionsPage); }, ), - const Divider( - height: 1, - ), - showItem( - icon: MyAssets.icGroupStar, - title: "Subscriptions", + CustomSettingOptionsTile( + leadingWidget: SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + MyAssets.icGroupStar, + // color: MyColors.primaryColor, + ), + ), + titleText: "Subscriptions", + subTitle: null, + isForLanguage: false, + needBorderBelow: false, onTap: () { navigateWithName(context, AppRoutes.subscriptionsPage); }, - ), + ) ], - ).toWhiteContainer(width: double.infinity, pading: EdgeInsets.symmetric(horizontal: 12)), + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + (AppState().currentAppType == AppType.provider) ? const Text("Provider") : const Text("Customer"), ], ), ), @@ -121,7 +182,7 @@ class _EditAccountPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - title.toText(isBold: true, fontSize: 14), + title.toText(isBold: false, fontSize: 16), if (subTitle != null) subTitle.toText(fontSize: 14, color: MyColors.primaryColor), ], ), diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index a5b189a..ec6189a 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -49,7 +49,7 @@ class _LoginWithPasswordState extends State { // phoneNum = "966580816976"; // password = "123@Shf"; phoneNum = "966530896018"; - password = "Amir@123"; + password = "Amir@1234"; } scheduleMicrotask(() { userVM = Provider.of(context, listen: false); diff --git a/lib/widgets/common_widgets/app_bar.dart b/lib/widgets/common_widgets/app_bar.dart index 8c1c8ac..340bfa1 100644 --- a/lib/widgets/common_widgets/app_bar.dart +++ b/lib/widgets/common_widgets/app_bar.dart @@ -1,65 +1,13 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -//import 'package:flutter/material.dart'; -// import 'package:flutter_svg/flutter_svg.dart'; -// import 'package:mc_common_app/classes/consts.dart'; -// import 'package:mc_common_app/extensions/int_extensions.dart'; -// import 'package:mc_common_app/extensions/string_extensions.dart'; -// import 'package:mc_common_app/theme/colors.dart'; -// import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -// import 'package:sizer/sizer.dart'; -// -// class CustomAppBar extends StatelessWidget with PreferredSizeWidget { -// final Color? backgroundColor; -// final double? elevation; -// final String? title; -// final Color? titleColor; -// final bool? isTitleCenter; -// final Color? backIconColor; -// final List? actions; -// final bool isRemoveBackButton; -// -// const CustomAppBar({ -// Key? key, -// this.title, -// required this.isRemoveBackButton, -// this.backgroundColor, -// this.actions, -// this.backIconColor, -// this.elevation, -// this.isTitleCenter, -// this.titleColor, -// }) : super(key: key); -// -// @override -// Widget build(BuildContext context) { -// return AppBar( -// backgroundColor: backgroundColor ?? appBackgroundColor, -// elevation: elevation ?? 0, -// centerTitle: isTitleCenter ?? true, -// leading: isRemoveBackButton -// ? null -// : IconButton( -// icon: const Icon(Icons.arrow_back_ios, color: Colors.black), -// onPressed: () => Navigator.of(context).pop(), -// ), -// iconTheme: IconThemeData( -// color: backIconColor ?? Colors.black, //change your color here -// ), -// actions: actions, -// title: (title ?? "").toText(fontSize: 20, isBold: true), -// ); -// } -// -// @override -// Size get preferredSize => const Size.fromHeight(kToolbarHeight); -// } class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final Color? backgroundColor; final double? elevation; @@ -104,45 +52,76 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { centerTitle: isTitleCenter ?? true, leading: isDrawerEnabled ? InkWell( - onTap: onTap, - child: Row( - children: [ - Image.asset( - profileImageUrl, - width: 34, - height: 34, - fit: BoxFit.fill, - ).toCircle(borderRadius: 100), - 10.width, - SvgPicture.asset(MyAssets.dashboardDrawerIcon), - ], - ).paddingOnly(left: 21), - ) - : isRemoveBackButton - ? null - : Row( - children: [ - 21.width, - IconButton( - icon: const Icon( - Icons.arrow_back_ios, - color: Colors.black, - size: 16, + onTap: onTap, + child: Row( + children: [ + profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null + ? Image.file( + AppState().getUser.data!.userInfo!.userLocalImage!, + width: 34, + height: 34, + fit: BoxFit.fill, + ).toCircle(borderRadius: 100) + : profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null + ? CachedNetworkImage( + imageUrl: AppState().getUser.data!.userInfo!.userImageUrl, + imageBuilder: (context, imageProvider) => + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, + fit: BoxFit.cover, ), - onPressed: onBackButtonTapped ?? - () { - Navigator.pop(context); - }, - ).toContainer( - paddingAll: 0, - borderRadius: 100, - borderColor: MyColors.lightGreyEFColor, - isEnabledBorder: true, - height: 40, - width: 40, ), - ], - ), + ), + placeholder: (context, url) => const Center(child: CircularProgressIndicator()), + errorWidget: (context, url, error) => const Icon(Icons.error), + fadeInCurve: Curves.easeIn, + width: 34, + height: 34, + fit: BoxFit.fill, + + fadeInDuration: Duration(milliseconds: 1000), + useOldImageOnUrlChange: false, + + + ).toCircle(borderRadius: 100) + : Image.asset( + MyAssets.carBanner, + width: 34, + height: 34, + fit: BoxFit.fill, + ).toCircle(borderRadius: 100), + 10.width, + SvgPicture.asset(MyAssets.dashboardDrawerIcon), + ], + ).paddingOnly(left: 21), + ) + : isRemoveBackButton + ? null + : Row( + children: [ + 21.width, + IconButton( + icon: const Icon( + Icons.arrow_back_ios, + color: Colors.black, + size: 16, + ), + onPressed: onBackButtonTapped ?? + () { + Navigator.pop(context); + }, + ).toContainer( + paddingAll: 0, + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + height: 40, + width: 40, + ), + ], + ), iconTheme: IconThemeData( color: backIconColor ?? Colors.black, //change your color here ),