diff --git a/android/app/src/main/res/values/mapbox_access_token.xml b/android/app/src/main/res/values/mapbox_access_token.xml
new file mode 100644
index 0000000..f1daf69
--- /dev/null
+++ b/android/app/src/main/res/values/mapbox_access_token.xml
@@ -0,0 +1,3 @@
+
+ sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg
+
\ No newline at end of file
diff --git a/assets/images/svg/bulb_icon.svg b/assets/images/svg/bulb_icon.svg
new file mode 100644
index 0000000..62afc17
--- /dev/null
+++ b/assets/images/svg/bulb_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/call_for_doctor.svg b/assets/images/svg/call_for_doctor.svg
new file mode 100644
index 0000000..9644d3a
--- /dev/null
+++ b/assets/images/svg/call_for_doctor.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/call_for_vitals.svg b/assets/images/svg/call_for_vitals.svg
new file mode 100644
index 0000000..ceacdf5
--- /dev/null
+++ b/assets/images/svg/call_for_vitals.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/select_city_icon.svg b/assets/images/svg/select_city_icon.svg
new file mode 100644
index 0000000..ff66079
--- /dev/null
+++ b/assets/images/svg/select_city_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index db2ca46..b369d62 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -178,6 +178,10 @@ class AppAssets {
static const String doctor_profile_rating_icon = '$svgBasePath/doctor_profile_rating_icon.svg';
static const String doctor_profile_reviews_icon = '$svgBasePath/doctor_profile_reviews_icon.svg';
static const String waiting_appointment_icon = '$svgBasePath/waitingAppo.svg';
+ static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg';
+ static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg';
+ static const String bulb_icon = '$svgBasePath/bulb_icon.svg';
+ static const String select_city_icon = '$svgBasePath/select_city_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index b994d4e..4c17de6 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart';
@@ -119,6 +121,7 @@ class AppDependencies {
getIt.registerLazySingleton(() => ContactUsRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => HmgServicesRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => SymptomsCheckerRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => BloodDonationRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -225,6 +228,10 @@ class AppDependencies {
() => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
+ getIt.registerLazySingleton(
+ () => BloodDonationViewModel(bloodDonationRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt()),
+ );
+
// Screen-specific VMs → Factory
// getIt.registerFactory(
// () => BookAppointmentsViewModel(
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index 9a03545..857c0c2 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -329,7 +329,7 @@ class Utils {
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
- .toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
+ .toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
.paddingSymmetrical(64.w, 0.h),
SizedBox(height: 16.h),
callToActionButton
@@ -875,4 +875,54 @@ class Utils {
Uri uri = Uri.parse(url);
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
}
+
+
+ static Color getCardBorderColor(int currentQueueStatus) {
+ switch (currentQueueStatus) {
+ case 0:
+ return AppColors.ratingColorYellow;
+ case 1:
+ return AppColors.primaryRedColor;
+ case 2:
+ return AppColors.successColor;
+ }
+ return AppColors.textColor;
+ }
+
+ static Color getCardButtonColor(int currentQueueStatus) {
+ switch (currentQueueStatus) {
+ case 0:
+ return AppColors.textColor.withValues(alpha: 0.08);
+ case 1:
+ return AppColors.primaryRedColor;
+ case 2:
+ return AppColors.successColor;
+ }
+ return AppColors.primaryRedColor;
+ }
+
+ static Color getCardButtonTextColor(int currentQueueStatus) {
+ switch (currentQueueStatus) {
+ case 0:
+ return Color(0xFFA2A2A2);
+ case 1:
+ return AppColors.whiteColor;
+ case 2:
+ return AppColors.whiteColor;
+ }
+ return AppColors.primaryRedColor;
+ }
+
+ static String getCardButtonText(int currentQueueStatus) {
+ switch (currentQueueStatus) {
+ case 0:
+ return "Please wait! you will be called for vital signs".needTranslation;
+ case 1:
+ return "Please visit Room S5 for vital signs".needTranslation;
+ case 2:
+ return "Please visit Room S5 to the Doctor".needTranslation;
+ }
+ return "";
+ }
+
}
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index c2a4a87..2039fb8 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -263,7 +263,7 @@ extension EmailValidator on String {
style: TextStyle(fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
- Widget toText20({Color? color, FontWeight? weight, bool isBold = false}) => Text(
+ Widget toText20({Color? color, FontWeight? weight, bool isBold = false, }) => Text(
this,
style: TextStyle(
fontSize: 20.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart
index 6ecf4b1..c9796e8 100644
--- a/lib/features/authentication/authentication_repo.dart
+++ b/lib/features/authentication/authentication_repo.dart
@@ -101,10 +101,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
@override
- Future>> checkPatientAuthentication({
- required dynamic checkPatientAuthenticationReq,
- String? languageID,
- }) async {
+ Future>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq, String? languageID}) async {
int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1;
checkPatientAuthenticationReq.patientOutSA = isOutKsa;
try {
@@ -149,7 +146,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
sendActivationCodeReq.isDentalAllowedBackend = false;
final payload = sendActivationCodeReq.toJson();
if (isFormFamilyFile) {
-
payload.remove("MobileNo");
payload.remove("NationalID");
payload.remove("SMSSignature");
@@ -266,10 +262,10 @@ class AuthenticationRepoImp implements AuthenticationRepo {
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
//silent login case removed token and login token
- if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true) {
- newRequest.logInTokenID = null;
- newRequest.deviceToken = null;
- }
+ // if(newRequest.logInTokenID.isEmpty && newRequest.isSilentLogin == true) {
+ // newRequest.logInTokenID = null;
+ // newRequest.deviceToken = null;
+ // }
}
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index 3260ea5..fa16423 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -852,16 +852,13 @@ class AuthenticationViewModel extends ChangeNotifier {
request['isRegister'] = true;
_appState.setAppAuthToken = response['LogInTokenID'];
if (isPatientOutsideSA(request: response)) {
- print("=======OUT SA=======");
sendActivationCode(
- otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
- nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
- phoneNumber: request["PatientMobileNumber"].toString(),
- payload: request,
- isForRegister: true,
- );
+ otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
+ nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
+ phoneNumber: request["PatientMobileNumber"].toString(),
+ payload: request,
+ isForRegister: true);
} else {
- print("=======IN SA=======");
chekUserNHICData(request: request);
}
}
diff --git a/lib/features/blood_donation/blood_donation_repo.dart b/lib/features/blood_donation/blood_donation_repo.dart
new file mode 100644
index 0000000..84997b2
--- /dev/null
+++ b/lib/features/blood_donation/blood_donation_repo.dart
@@ -0,0 +1,95 @@
+import 'package:dartz/dartz.dart';
+import 'package:hmg_patient_app_new/core/api/api_client.dart';
+import 'package:hmg_patient_app_new/core/api_consts.dart';
+import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
+import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart';
+import 'package:hmg_patient_app_new/services/logger_service.dart';
+
+abstract class BloodDonationRepo {
+ Future>>> getAllCities();
+
+ Future>> getPatientBloodGroupDetails();
+}
+
+class BloodDonationRepoImp implements BloodDonationRepo {
+ final ApiClient apiClient;
+ final LoggerService loggerService;
+
+ BloodDonationRepoImp({required this.loggerService, required this.apiClient});
+
+ @override
+ Future>>> getAllCities() async {
+ Map mapDevice = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_CITIES_REQUEST,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['ListCities'];
+ final citiesList = list.map((item) => CitiesModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: citiesList,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> getPatientBloodGroupDetails() async {
+ Map mapDevice = {};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_BLOOD_REQUEST,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['List_BloodGroupDetails'][0];
+ final patientBloodGroup = List_BloodGroupDetailsModel.fromJson(list);
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: patientBloodGroup,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart
new file mode 100644
index 0000000..7d2e5df
--- /dev/null
+++ b/lib/features/blood_donation/blood_donation_view_model.dart
@@ -0,0 +1,133 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_repo.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_list_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart';
+import 'package:hmg_patient_app_new/services/dialog_service.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+import 'package:hmg_patient_app_new/services/navigation_service.dart';
+
+class BloodDonationViewModel extends ChangeNotifier {
+ final DialogService dialogService;
+ BloodDonationRepo bloodDonationRepo;
+ ErrorHandlerService errorHandlerService;
+ final NavigationService navigationService;
+ final AppState appState;
+
+ List citiesList = [];
+ List bloodGroupList = [
+ BloodGroupListModel("O+", 0),
+ BloodGroupListModel("O-", 1),
+ BloodGroupListModel("AB+", 2),
+ BloodGroupListModel("AB-", 3),
+ BloodGroupListModel("A+", 4),
+ BloodGroupListModel("A-", 5),
+ BloodGroupListModel("B+", 6),
+ BloodGroupListModel("B-", 7),
+ ];
+
+ late CitiesModel selectedCity;
+ late BloodGroupListModel selectedBloodGroup;
+ int _selectedHospitalIndex = 0;
+ int _selectedBloodTypeIndex = 0;
+ String _selectedBloodType = '';
+
+ List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel();
+
+ BloodDonationViewModel({required this.bloodDonationRepo, required this.errorHandlerService, required this.navigationService, required this.dialogService, required this.appState});
+
+ setSelectedCity(CitiesModel city) {
+ selectedCity = city;
+ notifyListeners();
+ }
+
+ Future getRegionSelectedClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ citiesList.clear();
+ selectedCity = CitiesModel();
+ notifyListeners();
+ final result = await bloodDonationRepo.getAllCities();
+
+ result.fold(
+ (failure) async {
+ onError!(failure.message);
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage ?? 'An unexpected error occurred');
+ } else if (apiResponse.messageStatus == 1) {
+ citiesList = apiResponse.data!;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ Future getPatientBloodGroupDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bloodDonationRepo.getPatientBloodGroupDetails();
+
+ result.fold(
+ (failure) async {
+ onError!(failure.message);
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage ?? 'An unexpected error occurred');
+ } else if (apiResponse.messageStatus == 1) {
+ patientBloodGroupDetailsModel = apiResponse.data!;
+
+ CitiesModel citiesModel = CitiesModel();
+ citiesModel.iD = getSelectedCityID();
+ _selectedHospitalIndex = (citiesModel.iD! - 1);
+ citiesModel.description = citiesList[_selectedHospitalIndex].description;
+ citiesModel.descriptionN = citiesList[_selectedHospitalIndex].descriptionN;
+ selectedCity = citiesModel;
+ _selectedBloodType = patientBloodGroupDetailsModel.bloodGroup!;
+ _selectedBloodTypeIndex = getBloodIndex(_selectedBloodType);
+
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ int getSelectedCityID() {
+ int cityID = 1;
+ citiesList.forEach((element) {
+ if (element.description == patientBloodGroupDetailsModel.city) {
+ cityID = element.iD!;
+ }
+ });
+ return cityID;
+ }
+
+ int getBloodIndex(String type) {
+ switch (type) {
+ case "O+":
+ return 0;
+ case "O-":
+ return 1;
+ case "AB+":
+ return 2;
+ case "AB-":
+ return 3;
+ case "A+":
+ return 4;
+ case "A-":
+ return 5;
+ case "B+":
+ return 6;
+ case "B-":
+ return 7;
+
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/lib/features/blood_donation/models/blood_group_list_model.dart b/lib/features/blood_donation/models/blood_group_list_model.dart
new file mode 100644
index 0000000..9a98cc5
--- /dev/null
+++ b/lib/features/blood_donation/models/blood_group_list_model.dart
@@ -0,0 +1,6 @@
+class BloodGroupListModel {
+ String name;
+ int value;
+
+ BloodGroupListModel(this.name, this.value);
+}
diff --git a/lib/features/blood_donation/models/blood_group_response_model.dart b/lib/features/blood_donation/models/blood_group_response_model.dart
new file mode 100644
index 0000000..ceaca78
--- /dev/null
+++ b/lib/features/blood_donation/models/blood_group_response_model.dart
@@ -0,0 +1,61 @@
+class List_BloodGroupDetailsModel {
+ int? iD;
+ int? patientID;
+ int? patientType;
+ bool? patientOutSA;
+ int? zipCode;
+ String? cellNumber;
+ String? cityCode;
+ String? city;
+ int? gender;
+ String? bloodGroup;
+ String? nationalID;
+ bool? isActive;
+
+ List_BloodGroupDetailsModel({
+ this.iD,
+ this.patientID,
+ this.patientType,
+ this.patientOutSA,
+ this.zipCode,
+ this.cellNumber,
+ this.cityCode,
+ this.city,
+ this.gender,
+ this.bloodGroup,
+ this.nationalID,
+ this.isActive,
+ });
+
+ List_BloodGroupDetailsModel.fromJson(Map json) {
+ iD = json['ID'];
+ patientID = json['PatientID'];
+ patientType = json['PatientType'];
+ patientOutSA = json['PatientOutSA'];
+ zipCode = json['ZipCode'];
+ cellNumber = json['CellNumber'];
+ cityCode = json['CityCode'];
+ city = json['City'];
+ gender = json['Gender'];
+ bloodGroup = json['BloodGroup'];
+ nationalID = json['NationalID'];
+ isActive = json['IsActive'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ID'] = this.iD;
+ data['PatientID'] = this.patientID;
+ data['PatientType'] = this.patientType;
+ data['PatientOutSA'] = this.patientOutSA;
+ data['ZipCode'] = this.zipCode;
+ data['CellNumber'] = this.cellNumber;
+ data['CityCode'] = this.cityCode;
+ data['City'] = this.city;
+ data['Gender'] = this.gender;
+ data['BloodGroup'] = this.bloodGroup;
+ data['NationalID'] = this.nationalID;
+ data['IsActive'] = this.isActive;
+ return data;
+ }
+}
diff --git a/lib/features/blood_donation/models/cities_model.dart b/lib/features/blood_donation/models/cities_model.dart
new file mode 100644
index 0000000..cf7e631
--- /dev/null
+++ b/lib/features/blood_donation/models/cities_model.dart
@@ -0,0 +1,21 @@
+class CitiesModel {
+ int? iD;
+ String? description;
+ String? descriptionN;
+
+ CitiesModel({this.iD, this.description, this.descriptionN});
+
+ CitiesModel.fromJson(Map json) {
+ iD = json['ID'];
+ description = json['Description'];
+ descriptionN = json['DescriptionN'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ID'] = this.iD;
+ data['Description'] = this.description;
+ data['DescriptionN'] = this.descriptionN;
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index 2413827..d96cb4f 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -497,8 +497,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
initialSlotDuration = apiResponse.data["InitialSlotDuration"];
freeSlotsResponse = apiResponse.data['FreeTimeSlots'];
- // isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];
- isWaitingAppointmentAvailable = true;
+ isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];
+ // isWaitingAppointmentAvailable = true;
freeSlotsResponse.forEach((element) {
// date = (isLiveCareSchedule != null && isLiveCareSchedule)
diff --git a/lib/features/book_appointments/models/resp_models/get_allergies_response_model.dart b/lib/features/book_appointments/models/resp_models/get_allergies_response_model.dart
new file mode 100644
index 0000000..cd4802f
--- /dev/null
+++ b/lib/features/book_appointments/models/resp_models/get_allergies_response_model.dart
@@ -0,0 +1,37 @@
+class GetAllergiesResponseModel {
+ int? patientID;
+ int? allergyDiseaseType;
+ int? allergyDiseaseID;
+ String? description;
+ String? descriptionN;
+ String? remarks;
+
+ GetAllergiesResponseModel({
+ this.patientID,
+ this.allergyDiseaseType,
+ this.allergyDiseaseID,
+ this.description,
+ this.descriptionN,
+ this.remarks,
+ });
+
+ GetAllergiesResponseModel.fromJson(Map json) {
+ patientID = json['PatientID'];
+ allergyDiseaseType = json['AllergyDiseaseType'];
+ allergyDiseaseID = json['AllergyDiseaseID'];
+ description = json['Description'];
+ descriptionN = json['DescriptionN'];
+ remarks = json['Remarks'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['PatientID'] = this.patientID;
+ data['AllergyDiseaseType'] = this.allergyDiseaseType;
+ data['AllergyDiseaseID'] = this.allergyDiseaseID;
+ data['Description'] = this.description;
+ data['DescriptionN'] = this.descriptionN;
+ data['Remarks'] = this.remarks;
+ return data;
+ }
+}
diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart
index ab09ca6..bf10e9e 100644
--- a/lib/features/medical_file/medical_file_repo.dart
+++ b/lib/features/medical_file/medical_file_repo.dart
@@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_allergies_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart';
@@ -38,6 +39,8 @@ abstract class MedicalFileRepo {
Future>> removeFamilyFile({required int? id});
Future>> acceptRejectFamilyFile({required int? id, required int? status});
+
+ Future>>> getPatientAllergiesList({Function(dynamic)? onSuccess, Function(String)? onError});
}
class MedicalFileRepoImp implements MedicalFileRepo {
@@ -549,4 +552,42 @@ class MedicalFileRepoImp implements MedicalFileRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future>>> getPatientAllergiesList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Map mapDevice = {"isDentalAllowedBackend": false, "OutSA": 0};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_PATIENT_ALLERGIES,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['Patient_Allergies'];
+
+ final vaccinesList = list.map((item) => GetAllergiesResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: vaccinesList,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart
index 4b5a14c..de7f067 100644
--- a/lib/features/medical_file/medical_file_view_model.dart
+++ b/lib/features/medical_file/medical_file_view_model.dart
@@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_allergies_response_model.dart';
import 'package:hmg_patient_app_new/features/common/models/family_file_request.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
@@ -27,6 +28,7 @@ class MedicalFileViewModel extends ChangeNotifier {
bool isPatientSickLeaveListLoading = false;
bool isPatientSickLeavePDFLoading = false;
bool isPatientMedicalReportsListLoading = false;
+ bool isPatientAllergiesListLoading = false;
MedicalFileRepo medicalFileRepo;
ErrorHandlerService errorHandlerService;
@@ -34,6 +36,8 @@ class MedicalFileViewModel extends ChangeNotifier {
List patientVaccineList = [];
List patientSickLeaveList = [];
+ List patientAllergiesList = [];
+
List patientMedicalReportList = [];
List patientMedicalReportRequestedList = [];
@@ -69,8 +73,10 @@ class MedicalFileViewModel extends ChangeNotifier {
initMedicalFileProvider() {
patientMedicalReportAppointmentHistoryList.clear();
+ patientAllergiesList.clear();
isPatientVaccineListLoading = true;
isPatientMedicalReportsListLoading = true;
+ isPatientAllergiesListLoading = true;
notifyListeners();
}
@@ -89,7 +95,6 @@ class MedicalFileViewModel extends ChangeNotifier {
void onFamilyFileTabChange(int index) {
setSelectedFamilyFileTabIndex = index;
- notifyListeners();
}
setIsPatientVaccineListLoading(bool isLoading) {
@@ -162,6 +167,32 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
+ Future getPatientAllergiesList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isPatientAllergiesListLoading = true;
+ patientAllergiesList.clear();
+ notifyListeners();
+ final result = await medicalFileRepo.getPatientAllergiesList();
+
+ result.fold(
+ (failure) async {
+ isPatientAllergiesListLoading = false;
+ notifyListeners();
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ patientAllergiesList = apiResponse.data!;
+ isPatientAllergiesListLoading = false;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
Future getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientSickLeaveList.clear();
final result = await medicalFileRepo.getPatientSickLeavesList();
@@ -442,9 +473,6 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
-
-
-
Future addFamilyFile({required OTPTypeEnum otpTypeEnum}) async {
LoaderBottomSheet.showLoader();
AuthenticationViewModel authVM = getIt.get();
@@ -489,6 +517,13 @@ class MedicalFileViewModel extends ChangeNotifier {
});
}
+ Future clearAuthValues() async {
+ authVM.nationalIdController.clear();
+ authVM.phoneNumberController.clear();
+ authVM.emailController.clear();
+ authVM.dobController.clear();
+ }
+
Future handleFamilyFileRequestOTPVerification() async {
LoaderBottomSheet.showLoader();
if (!_appState.getIsChildLoggedIn) {
@@ -600,7 +635,7 @@ class MedicalFileViewModel extends ChangeNotifier {
getFamilyFiles(status: 0);
getAllPendingRecordsByResponseId();
LoaderBottomSheet.hideLoader();
- onFamilyFileTabChange(0);
+ // onFamilyFileTabChange(0);
}
},
);
diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index fa1ad22..71e0312 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -37,6 +37,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
DateTime? start = null;
DateTime? end = null;
+ bool isPatientHasQueueAppointment = false;
+ int currentQueueStatus = 0;
+
List patientAppointmentsHistoryList = [];
List filteredAppointmentList = [];
@@ -85,6 +88,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isTamaraDetailsLoading = true;
isAppointmentPatientShareLoading = true;
isEyeMeasurementsAppointmentsLoading = true;
+ isPatientHasQueueAppointment = false;
+ notifyListeners();
+ }
+
+ setCurrentQueueStatus(int currentQueueStatus) {
+ this.currentQueueStatus = currentQueueStatus;
notifyListeners();
}
diff --git a/lib/main.dart b/lib/main.dart
index 9d1ec39..30714c6 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
@@ -149,6 +150,9 @@ void main() async {
),
ChangeNotifierProvider(
create: (_) => getIt.get(),
+ ),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
)
], child: MyApp()),
),
diff --git a/lib/presentation/allergies/allergies_list_page.dart b/lib/presentation/allergies/allergies_list_page.dart
new file mode 100644
index 0000000..efcdd0a
--- /dev/null
+++ b/lib/presentation/allergies/allergies_list_page.dart
@@ -0,0 +1,138 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:provider/provider.dart';
+
+class AllergiesListPage extends StatelessWidget {
+ AllergiesListPage({super.key});
+
+ late MedicalFileViewModel medicalFileViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ medicalFileViewModel = Provider.of(context, listen: false);
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: LocaleKeys.allergies.tr(),
+ child: SingleChildScrollView(
+ child: Consumer(builder: (context, medicalFileVM, child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 16.h),
+ ListView.separated(
+ scrollDirection: Axis.vertical,
+ itemCount: medicalFileVM.isPatientAllergiesListLoading
+ ? 5
+ : medicalFileVM.patientAllergiesList.isNotEmpty
+ ? medicalFileVM.patientAllergiesList.length
+ : 1,
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ padding: EdgeInsets.only(left: 24.h, right: 24.h),
+ itemBuilder: (context, index) {
+ return medicalFileVM.isPatientAllergiesListLoading
+ ? Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(14.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.allergy_info_icon, width: 36.w, height: 36.h, fit: BoxFit.contain).toShimmer2(isShow: true),
+ SizedBox(width: 16.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
+ SizedBox(height: 8.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 3.h,
+ runSpacing: 4.h,
+ children: [
+ // AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
+ // AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ )
+ : medicalFileVM.patientAllergiesList.isNotEmpty
+ ? AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 1000),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.allergy_info_icon, width: 36.w, height: 36.h, fit: BoxFit.contain),
+ SizedBox(height: 16.h),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ (medicalFileVM.patientAllergiesList[index].description).toString().toText16(isBold: true).toShimmer2(isShow: false),
+ (medicalFileVM.patientAllergiesList[index].remarks).toString().toText12(),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ : Utils.getNoDataWidget(context, noDataText: "No allergies data found...".needTranslation);
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
+ ),
+ SizedBox(height: 60.h),
+ ],
+ );
+ }),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/appointments/appointment_queue_page.dart b/lib/presentation/appointments/appointment_queue_page.dart
new file mode 100644
index 0000000..f48f1eb
--- /dev/null
+++ b/lib/presentation/appointments/appointment_queue_page.dart
@@ -0,0 +1,220 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:provider/provider.dart';
+
+class AppointmentQueuePage extends StatelessWidget {
+ AppointmentQueuePage({super.key});
+
+ AppState? appState;
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Consumer(builder: (context, myAppointmentsVM, child) {
+ return Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "Queueing".needTranslation,
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.all(24.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ side: BorderSide(color: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ AppCustomChipWidget(
+ labelText: myAppointmentsVM.currentQueueStatus == 0 ? "In Queue".needTranslation : "Your Turn".needTranslation,
+ backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20),
+ textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus),
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
+ ],
+ ),
+ SizedBox(height: 10.h),
+ "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
+ SizedBox(height: 8.h),
+ "Thank you for your patience, here is your queue number.".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 8.h),
+ "IMD W-A-5".needTranslation.toText32(isBold: true),
+ SizedBox(height: 8.h),
+ CustomButton(
+ text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus),
+ onPressed: () {},
+ backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus),
+ borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01),
+ textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus),
+ fontSize: 12.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 40.h,
+ iconColor: AppColors.whiteColor,
+ iconSize: 18.h,
+ ),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Serving Now".needTranslation.toText16(isBold: true),
+ SizedBox(height: 18.h),
+ ListView.separated(
+ padding: EdgeInsets.zero,
+ shrinkWrap: true,
+ itemCount: 3,
+ physics: NeverScrollableScrollPhysics(),
+ itemBuilder: (BuildContext context, int index) {
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "IMD W-A-2".needTranslation.toText17(isBold: true),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ "Room: S2".toText12(fontWeight: FontWeight.w500),
+ SizedBox(width: 8.w),
+ AppCustomChipWidget(
+ deleteIcon: AppAssets.call_for_vitals,
+ labelText: "Call for vital signs".needTranslation,
+ iconColor: AppColors.primaryRedColor,
+ textColor: AppColors.primaryRedColor,
+ iconSize: 14.w,
+ backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
+ labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h),
+ ),
+ ],
+ ),
+ ],
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h),
+ ),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.bulb_icon, width: 24.w, height: 24.h),
+ SizedBox(width: 8.w),
+ "Things to ask your doctor today".needTranslation.toText16(isBold: true),
+ ],
+ ),
+ SizedBox(height: 8.h),
+
+ // What can I do to improve my overall health?
+ // Are there any routine screenings I should get?
+ // What is this medication for?
+ // Are there any side effects I should know about?
+ // When should I come back for a follow-up?
+
+ "• ${"What can I do to improve my overall health?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 4.h),
+ "• ${"Are there any routine screenings I should get?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 4.h),
+ "• ${"What is this medication for?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 4.h),
+ "• ${"Are there any side effects I should know about?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+ SizedBox(height: 4.h),
+ "• ${"When should I come back for a follow-up?"}".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
+
+ SizedBox(height: 16.h),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: CustomButton(
+ text: "Go to homepage".needTranslation,
+ onPressed: () {
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 50.h,
+ icon: AppAssets.homeBottom,
+ iconColor: AppColors.whiteColor,
+ iconSize: 18.h,
+ ).paddingSymmetrical(16.h, 24.h),
+ )
+ ],
+ );
+ }),
+ );
+ }
+}
diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart
index cca227a..f5ec31b 100644
--- a/lib/presentation/appointments/widgets/appointment_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_card.dart
@@ -124,47 +124,37 @@ class AppointmentCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.center,
children: [
- Stack(
- children: [
- Image.network(
- isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : patientAppointmentHistoryResponseModel.doctorImageURL!,
- width: 63.w,
- height: 63.h,
- fit: BoxFit.cover,
- ),
- Positioned(
- bottom: 0,
- left: 0,
- right: 0,
- child: Container(
- width: 63.w,
- height: 20,
- color: AppColors.textColorLight.withValues(alpha: 0.25),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Utils.buildSvgWithAssets(
- icon: AppAssets.rating_icon,
- width: 12.w,
- height: 12.h,
- fit: BoxFit.contain,
- ),
- SizedBox(width: 4.w),
- isLoading ? "Rating".toText12() : patientAppointmentHistoryResponseModel.decimalDoctorRate.toString().toText12(),
- ],
- ),
+ Image.network(
+ isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : patientAppointmentHistoryResponseModel.doctorImageURL!,
+ width: 63.w,
+ height: 63.h,
+ fit: BoxFit.cover,
+ ).circle(100.r).toShimmer2(isShow: isLoading),
+ Transform.translate(
+ offset: Offset(0.0, -20.h),
+ child: Container(
+ width: 40.w,
+ height: 40.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ shape: BoxShape.circle, // Makes the container circular
+ border: Border.all(
+ color: AppColors.scaffoldBgColor, // Color of the border
+ width: 1.5.w, // Width of the border
),
- )
- ],
- ).circle(100).toShimmer2(isShow: isLoading),
- // SizedBox(height: 12.h),
- // AppCustomChipWidget(
- // icon: AppAssets.rating_icon,
- // iconColor: AppColors.ratingColorYellow,
- // labelText: isLoading ? "Rating" : "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}".needTranslation)
- // .toShimmer2(isShow: isLoading),
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
+ SizedBox(height: 2.h),
+ "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
+ ],
+ ),
+ ).circle(100).toShimmer2(isShow: isLoading),
+ ),
],
),
SizedBox(width: 16.h),
@@ -181,10 +171,14 @@ class AppointmentCard extends StatelessWidget {
spacing: 3.h,
runSpacing: 4.h,
children: [
- AppCustomChipWidget(labelText: isLoading ? 'Cardiology' : patientAppointmentHistoryResponseModel.clinicName!)
- .toShimmer2(isShow: isLoading),
- AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!)
- .toShimmer2(isShow: isLoading),
+ AppCustomChipWidget(
+ labelText: isLoading
+ ? 'Cardiology'
+ : (patientAppointmentHistoryResponseModel.clinicName!.length > 15
+ ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...'
+ : patientAppointmentHistoryResponseModel.clinicName!),
+ ).toShimmer2(isShow: isLoading),
+ AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: isLoading
diff --git a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart
index 74ab6b7..f430447 100644
--- a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart
+++ b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart
@@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@@ -156,7 +157,9 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
),
(r) => false);
Navigator.of(context).push(
- CustomPageRoute(page: MyAppointmentsPage()),
+ CustomPageRoute(
+ page: AppointmentQueuePage(),
+ ),
);
}, isFullScreen: false);
},
diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
index b2d74ca..96d538b 100644
--- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
@@ -50,8 +51,29 @@ class AppointmentDoctorCard extends StatelessWidget {
height: 63.h,
fit: BoxFit.cover,
).circle(100.r),
- SizedBox(height: 12.h),
- AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
+ Transform.translate(
+ offset: Offset(0.0, -20.h),
+ child: Container(
+ width: 40.w,
+ height: 40.h,
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ shape: BoxShape.circle, // Makes the container circular
+ border: Border.all(
+ color: AppColors.scaffoldBgColor, // Color of the border
+ width: 1.5.w, // Width of the border
+ ),
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
+ SizedBox(height: 2.h),
+ "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
+ ],
+ ),
+ ).circle(100),
+ ),
],
),
SizedBox(width: 16.w),
@@ -60,13 +82,16 @@ class AppointmentDoctorCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true),
+ SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 6.w,
runSpacing: 6.h,
children: [
AppCustomChipWidget(
- labelText: patientAppointmentHistoryResponseModel.clinicName!,
+ labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 15
+ ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...'
+ : patientAppointmentHistoryResponseModel.clinicName!),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
),
AppCustomChipWidget(
@@ -75,8 +100,7 @@ class AppointmentDoctorCard extends StatelessWidget {
),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
- labelText:
- "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(
+ labelText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false,
)}",
@@ -100,8 +124,7 @@ class AppointmentDoctorCard extends StatelessWidget {
),
],
),
- SizedBox(height: 16.h),
-
+ SizedBox(height: 8.h),
Visibility(
visible: renderWidgetForERDisplay == false,
child: getAppointmentActionButtons(
diff --git a/lib/presentation/appointments/widgets/appointment_queueing_screen.dart b/lib/presentation/appointments/widgets/appointment_queueing_screen.dart
deleted file mode 100644
index e69de29..0000000
diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart
index 0fa995d..28430a8 100644
--- a/lib/presentation/authentication/login.dart
+++ b/lib/presentation/authentication/login.dart
@@ -88,10 +88,10 @@ class LoginScreenState extends State {
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.h),
leadingIcon: AppAssets.student_card,
- errorMessage: "Please enter a valid national ID or file number",
+ errorMessage: "Please enter a valid national ID or file number".needTranslation,
hasError: false,
),
- SizedBox(height: 16.h), // Adjusted to sizer unit (approx 16px)
+ SizedBox(height: 16.h),
CustomButton(
height: 50.h,
text: LocaleKeys.login.tr(),
@@ -110,17 +110,12 @@ class LoginScreenState extends State {
}
},
),
- SizedBox(height: 10.h), // Adjusted to sizer unit (approx 14px)
+ SizedBox(height: 10.h),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
- style: context.dynamicTextStyle(
- color: Colors.black,
- fontSize: 14.f, // Adjusted to sizer unit
- height: 26 / 16, // This height is a ratio, may need re-evaluation
- fontWeight: FontWeight.w500,
- ),
+ style: context.dynamicTextStyle(color: Colors.black, fontSize: 14.f, height: 26 / 16, fontWeight: FontWeight.w500),
children: [
TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()),
TextSpan(text: " "),
@@ -140,9 +135,9 @@ class LoginScreenState extends State {
),
],
),
- ).withVerticalPadding(2), // Adjusted to sizer unit
+ ).withVerticalPadding(2.h),
),
- SizedBox(height: 20.h), // Adjusted to sizer unit (approx 14px)
+ SizedBox(height: 20.h),
],
),
),
diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart
index 9551ba1..f04f7dd 100644
--- a/lib/presentation/authentication/register.dart
+++ b/lib/presentation/authentication/register.dart
@@ -78,8 +78,7 @@ class _RegisterNew extends State {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Utils.showLottie(
- context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true),
+ Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.w, height: 200.h, fit: BoxFit.cover, repeat: true),
SizedBox(height: 16.h),
LocaleKeys.prepareToElevate.tr().toText32(isBold: true),
SizedBox(height: 24.h),
@@ -113,7 +112,7 @@ class _RegisterNew extends State {
Divider(height: 1),
TextInputWidget(
labelText: LocaleKeys.dob.tr(),
- hintText: "11 July, 1994",
+ hintText: "11 July, 1994".needTranslation,
controller: authVm.dobController,
focusNode: _dobFocusNode,
isEnable: true,
@@ -240,7 +239,7 @@ class _RegisterNew extends State {
),
),
),
- SizedBox(height: 30),
+ SizedBox(height: 30.h),
],
),
),
diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart
index 3466497..71cef96 100644
--- a/lib/presentation/authentication/register_step2.dart
+++ b/lib/presentation/authentication/register_step2.dart
@@ -69,7 +69,7 @@ class _RegisterNew extends State {
height: double.infinity,
child: SingleChildScrollView(
reverse: false,
- padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 0.h),
+ padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -98,7 +98,7 @@ class _RegisterNew extends State {
leadingIcon: AppAssets.user_circle,
labelColor: AppColors.textColor,
).paddingSymmetrical(0.h, 16.h),
- Divider(height: 1, color: AppColors.greyColor),
+ Divider(height: 1.h, color: AppColors.greyColor),
TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(),
hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : (appState.getNHICUserData.idNumber ?? ""),
@@ -130,7 +130,7 @@ class _RegisterNew extends State {
hasSelectionCustomIcon: true,
isAllowRadius: false,
labelColor: AppColors.textColor,
- padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.user_full,
).withVerticalPadding(8);
@@ -167,7 +167,7 @@ class _RegisterNew extends State {
hasSelectionCustomIcon: true,
isAllowRadius: false,
labelColor: AppColors.textColor,
- padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.smart_phone,
).withVerticalPadding(8);
@@ -188,7 +188,7 @@ class _RegisterNew extends State {
leadingIcon: AppAssets.smart_phone,
onChange: (value) {})
.paddingSymmetrical(0.h, 16.h),
- Divider(height: 1, color: AppColors.greyColor),
+ Divider(height: 1.h, color: AppColors.greyColor),
authVM!.isUserFromUAE()
? Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
@@ -217,10 +217,10 @@ class _RegisterNew extends State {
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
isAllowRadius: false,
- padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
- ).withVerticalPadding(8);
+ ).withVerticalPadding(8.h);
},
)
: TextInputWidget(
@@ -256,7 +256,7 @@ class _RegisterNew extends State {
leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h),
Divider(
- height: 1,
+ height: 1.h,
color: AppColors.greyColor,
),
TextInputWidget(
@@ -292,9 +292,7 @@ class _RegisterNew extends State {
iconColor: AppColors.primaryRedColor,
),
),
- SizedBox(
- width: 16,
- ),
+ SizedBox(width: 16.w),
Expanded(
child: CustomButton(
backgroundColor: AppColors.primaryRedColor,
diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart
new file mode 100644
index 0000000..8908652
--- /dev/null
+++ b/lib/presentation/blood_donation/blood_donation_page.dart
@@ -0,0 +1,172 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_city_widget.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:provider/provider.dart';
+
+class BloodDonationPage extends StatelessWidget {
+ BloodDonationPage({super.key});
+
+ late AppState appState;
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Consumer(builder: (context, bloodDonationVM, child) {
+ return Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: LocaleKeys.bloodDonation.tr(),
+ child: Padding(
+ padding: EdgeInsets.all(24.w),
+ child: SingleChildScrollView(
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.select_city_icon, width: 40.h, height: 40.h),
+ SizedBox(width: 12.w),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.city.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500),
+ (appState.isArabic()
+ ? (bloodDonationVM.selectedCity.descriptionN ?? LocaleKeys.select.tr())
+ : bloodDonationVM.selectedCity.description ?? LocaleKeys.select.tr(context: context))
+ .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ],
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
+ ],
+ ).onPress(() async {
+ LoaderBottomSheet.showLoader(loadingText: "Fetching Cities...");
+ await bloodDonationVM.getRegionSelectedClinics(onSuccess: (val) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(context,
+ title: LocaleKeys.selectCity.tr(context: context),
+ isDismissible: true,
+ child: SelectCityWidget(
+ bloodDonationViewModel: bloodDonationVM,
+ ),
+ callBackFunc: () {});
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ });
+ }),
+ SizedBox(height: 16.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h),
+ SizedBox(width: 12.w),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.gender.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500),
+ "Male".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ],
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
+ ],
+ ),
+ SizedBox(height: 16.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h),
+ SizedBox(width: 12.w),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500),
+ "AB+".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ],
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: SizedBox(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ CustomButton(
+ text: LocaleKeys.save.tr(),
+ onPressed: () {
+ // openDoctorScheduleCalendar();
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12.r,
+ padding: EdgeInsets.symmetric(horizontal: 10.w),
+ height: 50.h,
+ iconSize: 18.h,
+ ).paddingSymmetrical(16.h, 24.h),
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }),
+ );
+ }
+}
diff --git a/lib/presentation/blood_donation/widgets/city_list_item.dart b/lib/presentation/blood_donation/widgets/city_list_item.dart
new file mode 100644
index 0000000..408fb26
--- /dev/null
+++ b/lib/presentation/blood_donation/widgets/city_list_item.dart
@@ -0,0 +1,60 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+
+class CityListItem extends StatelessWidget {
+ final CitiesModel cityModel;
+
+ late AppState appState;
+
+ CityListItem({super.key, required this.cityModel});
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ return DecoratedBox(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 8.h,
+ children: [hospitalName],
+ ),
+ ),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon,
+ iconColor: AppColors.blackColor,
+ width: 40.h,
+ height: 40.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ );
+ }
+
+ Widget get hospitalName => Row(
+ children: [
+ Expanded(
+ child: (appState.isArabic() ? cityModel.descriptionN : cityModel.description)!.toText16(color: AppColors.textColor, isBold: true),
+ )
+ ],
+ );
+}
diff --git a/lib/presentation/blood_donation/widgets/select_city_widget.dart b/lib/presentation/blood_donation/widgets/select_city_widget.dart
new file mode 100644
index 0000000..a0e8477
--- /dev/null
+++ b/lib/presentation/blood_donation/widgets/select_city_widget.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/city_list_item.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+
+class SelectCityWidget extends StatelessWidget {
+ SelectCityWidget({super.key, required this.bloodDonationViewModel});
+
+ BloodDonationViewModel bloodDonationViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 8.h),
+ SizedBox(
+ height: MediaQuery.sizeOf(context).height * .4,
+ child: ListView.separated(
+ itemBuilder: (_, index) {
+ return CityListItem(
+ cityModel: bloodDonationViewModel.citiesList[index],
+ ).onPress(() {
+ bloodDonationViewModel.setSelectedCity(bloodDonationViewModel.citiesList[index]);
+ Navigator.of(context).pop();
+ });
+ },
+ separatorBuilder: (_, __) => SizedBox(
+ height: 8.h,
+ ),
+ itemCount: bloodDonationViewModel.citiesList.length),
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart
index 8a0f7e7..0ad58cc 100644
--- a/lib/presentation/book_appointment/book_appointment_page.dart
+++ b/lib/presentation/book_appointment/book_appointment_page.dart
@@ -25,6 +25,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@@ -72,6 +73,15 @@ class _BookAppointmentPageState extends State {
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
+ isLeading: true,
+ leadingCallback: () {
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
child: SingleChildScrollView(
child: Consumer(builder: (context, bookAppointmentsVM, child) {
return Column(
diff --git a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart
index ae47326..a46f79d 100644
--- a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart
+++ b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart
@@ -67,40 +67,4 @@ class HospitalListItemAdvancePayment extends StatelessWidget {
)
],
);
-
-// Widget get distanceInfo => Row(
-// children: [
-// Visibility(
-// visible: (hospitalModel.distanceInKMs != "0"),
-// child: AppCustomChipWidget(
-// labelText: "${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
-// deleteIcon: AppAssets.location_red,
-// deleteIconSize: Size(9, 12),
-// backgroundColor: AppColors.secondaryLightRedColor,
-// textColor: AppColors.errorColor,
-// ),
-// ),
-// Visibility(
-// visible: (hospitalData?.distanceInKMs == "0"),
-// child: Row(
-// children: [
-// AppCustomChipWidget(
-// labelText: "Distance not available".needTranslation,
-// textColor: AppColors.blackColor,
-// ),
-// SizedBox(
-// width: 8.h,
-// )
-// ],
-// )),
-// Visibility(
-// visible: !isLocationEnabled,
-// child: AppCustomChipWidget(
-// labelText: "Location turned off".needTranslation,
-// deleteIcon: AppAssets.location_unavailable,
-// deleteIconSize: Size(9, 12),
-// textColor: AppColors.blackColor,
-// )),
-// ],
-// );
}
diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart
index fd6e976..1b9f3cd 100644
--- a/lib/presentation/hmg_services/services_page.dart
+++ b/lib/presentation/hmg_services/services_page.dart
@@ -42,6 +42,16 @@ class ServicesPage extends StatelessWidget {
textColor: AppColors.blackColor,
route: AppRoutes.homeHealthCarePage,
),
+ HmgServicesComponentModel(
+ 3,
+ "Blood Donation".needTranslation,
+ "".needTranslation,
+ AppAssets.emergency_services_icon,
+ true,
+ bgColor: AppColors.bgGreenColor,
+ textColor: AppColors.blackColor,
+ route: AppRoutes.bloodDonationPage,
+ ),
HmgServicesComponentModel(
11,
"Virtual Tour".needTranslation,
diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart
index 5190e5b..3d774d3 100644
--- a/lib/presentation/insurance/widgets/patient_insurance_card.dart
+++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart
@@ -34,7 +34,7 @@ class PatientInsuranceCard extends StatelessWidget {
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
- borderRadius: 24,
+ borderRadius: 24.r,
),
child: Padding(
padding: EdgeInsets.all(16.h),
@@ -112,6 +112,6 @@ class PatientInsuranceCard extends StatelessWidget {
],
),
),
- ).paddingSymmetrical(24.h, 0.h);
+ ).paddingSymmetrical(0.h, 0.h);
}
}
diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index c1922d0..f8d978e 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -24,12 +24,13 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/allergies/allergies_list_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
-import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart';
@@ -54,6 +55,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
+import 'package:hmg_patient_app_new/widgets/expandable_list_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@@ -85,6 +87,7 @@ class _MedicalFilePageState extends State {
appState = getIt.get();
scheduleMicrotask(() {
if (appState.isAuthenticated) {
+ labViewModel.initLabProvider();
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
@@ -107,18 +110,22 @@ class _MedicalFilePageState extends State {
trailing: Row(
children: [
Wrap(
+ spacing: -8.h,
+ // runSpacing: 0.h,
children: [
Utils.buildImgWithAssets(
icon: AppAssets.babyGirlImg,
- height: 32.h,
- border: 1.5,
+ height: 28.h,
+ width: 28.w,
+ border: 1,
fit: BoxFit.contain,
borderRadius: 50.r,
),
Utils.buildImgWithAssets(
icon: AppAssets.femaleImg,
- height: 32.h,
- border: 1.5,
+ height: 28.h,
+ width: 28.w,
+ border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
@@ -144,24 +151,18 @@ class _MedicalFilePageState extends State {
},
profiles: medicalFileViewModel.patientFamilyFiles);
}),
- isLeading: false,
+ isLeading: true,
+ leadingCallback: () {
+ Navigator.pushAndRemoveUntil(
+ navigationService.navigatorKey.currentContext!,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ },
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- SizedBox(height: 16.h),
- TextInputWidget(
- labelText: LocaleKeys.search.tr(context: context),
- hintText: "Type any record".needTranslation,
- controller: TextEditingController(),
- keyboardType: TextInputType.number,
- isEnable: true,
- prefix: null,
- autoFocus: false,
- isBorderAllowed: false,
- isAllowLeadingIcon: true,
- padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
- leadingIcon: AppAssets.student_card,
- ).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
@@ -189,7 +190,7 @@ class _MedicalFilePageState extends State {
children: [
AppCustomChipWidget(
icon: AppAssets.file_icon,
- labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
+ labelText: "${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
labelPadding: EdgeInsetsDirectional.only(end: 6.w),
onChipTap: () {
navigationService.pushPage(
@@ -225,9 +226,10 @@ class _MedicalFilePageState extends State {
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
- labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}".needTranslation,
+ labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
iconColor: AppColors.primaryRedColor,
- labelPadding: EdgeInsetsDirectional.only(end: 4.w, start: 0.w),
+ labelPadding: EdgeInsetsDirectional.only(end: 4.w),
+ padding: EdgeInsets.zero,
),
Consumer(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
@@ -236,8 +238,7 @@ class _MedicalFilePageState extends State {
iconColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
textColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 12.w,
- backgroundColor:
- insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
+ backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
);
}),
@@ -248,27 +249,112 @@ class _MedicalFilePageState extends State {
),
).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h),
- Consumer(builder: (context, medicalFileVM, child) {
- return Column(
- children: [
- CustomTabBar(
- activeTextColor: AppColors.primaryRedColor,
- activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
- tabs: [
- CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
- CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
- CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
- CustomTabBarModel(AppAssets.more, "More".needTranslation),
- ],
- onTabChange: (index) {
- medicalFileVM.onTabChanged(index);
- },
- ).paddingSymmetrical(24.w, 0.0),
- SizedBox(height: 24.h),
- getSelectedTabData(medicalFileVM.selectedTabIndex),
- ],
- );
- }),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(context: context),
+ hintText: "Type any record".needTranslation,
+ controller: TextEditingController(),
+ keyboardType: TextInputType.number,
+ isEnable: true,
+ prefix: null,
+ autoFocus: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
+ leadingIcon: AppAssets.search_icon,
+ hintColor: AppColors.textColor,
+ ).paddingSymmetrical(24.w, 0.0),
+ SizedBox(height: 16.h),
+ // Using CustomExpandableList
+ CustomExpandableList(
+ expansionMode: ExpansionMode.exactlyOne,
+ dividerColor: Color(0xFF2B353E1A),
+ itemPadding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 14.h),
+ items: [
+ ExpandableListItem(
+ title: "Medical Services".toText18(weight: FontWeight.w600),
+ children: [
+ SizedBox(height: 10.h),
+ getSelectedTabData(0),
+ ],
+ ),
+ ExpandableListItem(
+ title: "Medical Reports".toText18(
+ weight: FontWeight.w600,
+ ),
+ expandedBackgroundColor: Colors.transparent,
+ children: [
+ SizedBox(height: 10.h),
+ getSelectedTabData(2),
+ ]),
+ ExpandableListItem(
+ title: "Insurance & Payments".toText18(weight: FontWeight.w600),
+ expandedBackgroundColor: Colors.transparent,
+ children: [
+ SizedBox(height: 10.h),
+ getSelectedTabData(1),
+ ],
+ ),
+ ExpandableListItem(
+ title: "Tracker & Others".toText18(weight: FontWeight.w600),
+ expandedBackgroundColor: Colors.transparent,
+ children: [
+ Text("Blood Report"),
+ SizedBox(height: 8),
+ Text("X-Ray Report"),
+ ],
+ ),
+ ],
+ theme: ExpandableListTheme.custom(
+ defaultTrailingIcon: Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w, iconColor: AppColors.textColor),
+ ),
+ ).paddingSymmetrical(16.w, 0.0),
+
+ // ExpansionTileList(
+ // shrinkWrap: true,
+ // trailing: Icon(Icons.arrow_drop_down),
+ // expansionMode: ExpansionMode.exactlyOne,
+ // children: [
+ // ExpansionTile(
+ // title: "Medical Services".toText20(weight: FontWeight.w600, color: AppColors.textColor),
+ // iconColor: Color(0xFF2E3039),
+ // trailing: Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w),
+ // children: [Text('Child 1')],
+ // ),
+ // ExpansionTile(
+ // title: "Medical Reports".toText20(weight: FontWeight.w600, color: AppColors.textColor),
+ // children: [Text('Child 2')],
+ // ),
+ // ExpansionTile(
+ // title: "Insurance & Payments".toText20(weight: FontWeight.w600, color: AppColors.textColor),
+ // children: [Text('Child 3')],
+ // ),
+ // ExpansionTile(
+ // title: "Tracker & Others".toText20(weight: FontWeight.w600, color: AppColors.textColor),
+ // children: [Text('Child 4')],
+ // ),
+ // ],
+ // ),
+ // Consumer(builder: (context, medicalFileVM, child) {
+ // return Column(
+ // children: [
+ // CustomTabBar(
+ // activeTextColor: AppColors.primaryRedColor,
+ // activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
+ // tabs: [
+ // CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
+ // CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
+ // CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
+ // CustomTabBarModel(AppAssets.more, "More".needTranslation),
+ // ],
+ // onTabChange: (index) {
+ // medicalFileVM.onTabChanged(index);
+ // },
+ // ).paddingSymmetrical(24.w, 0.0),
+ // SizedBox(height: 24.h),
+ // getSelectedTabData(medicalFileVM.selectedTabIndex),
+ // ],
+ // );
+ // }),
],
),
);
@@ -324,7 +410,7 @@ class _MedicalFilePageState extends State {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- "Appointments & visits".needTranslation.toText18(isBold: true),
+ "Appointments & visits".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
Row(
children: [
LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
@@ -333,7 +419,7 @@ class _MedicalFilePageState extends State {
],
),
],
- ).paddingSymmetrical(24.w, 0.h).onPress(() {
+ ).paddingSymmetrical(0.w, 0.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyAppointmentsPage(),
@@ -356,8 +442,7 @@ class _MedicalFilePageState extends State {
? Container(
padding: EdgeInsets.all(12.w),
width: MediaQuery.of(context).size.width,
- decoration:
- RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true),
child: Column(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
@@ -390,7 +475,6 @@ class _MedicalFilePageState extends State {
: ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
- padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemCount: myAppointmentsVM.patientAppointmentsHistoryList.length,
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
@@ -408,45 +492,18 @@ class _MedicalFilePageState extends State {
onRescheduleTap: () {
openDoctorScheduleCalendar(myAppointmentsVM.patientAppointmentsHistoryList[index]);
},
- onAskDoctorTap: () async {
- LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation);
- await myAppointmentsViewModel.isDoctorAvailable(
- projectID: myAppointmentsVM.patientAppointmentsHistoryList[index].projectID,
- doctorId: myAppointmentsVM.patientAppointmentsHistoryList[index].doctorID,
- clinicId: myAppointmentsVM.patientAppointmentsHistoryList[index].clinicID,
- onSuccess: (value) async {
- if (value) {
- await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
- LoaderBottomSheet.hideLoader();
- showCommonBottomSheetWithoutHeight(
- context,
- title: LocaleKeys.askDoctor.tr(context: context),
- child: AskDoctorRequestTypeSelect(
- askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList,
- myAppointmentsViewModel: myAppointmentsViewModel,
- patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
- ),
- callBackFunc: () {},
- isFullScreen: false,
- isCloseButtonVisible: true,
- );
- });
- } else {
- print("Doctor is not available");
- }
- });
- },
+ onAskDoctorTap: () {},
)),
),
),
);
},
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 12.h),
),
- );
+ ).paddingSymmetrical(0.w, 0.h);
}),
- SizedBox(height: 24.h),
- "Lab & Radiology".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
+ SizedBox(height: 10.h),
+ "Lab & Radiology".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
Row(
children: [
@@ -458,7 +515,6 @@ class _MedicalFilePageState extends State {
labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests,
isLoading: labViewModel.isLabOrdersLoading,
).onPress(() {
- labViewModel.initLabProvider();
Navigator.of(context).push(
CustomPageRoute(
page: LabOrdersPage(),
@@ -466,11 +522,12 @@ class _MedicalFilePageState extends State {
);
}),
),
- SizedBox(width: 16.h),
+ SizedBox(width: 8.w),
Expanded(
child: LabRadCard(
icon: AppAssets.radiology_icon,
- labelText: LocaleKeys.radiology.tr(context: context),
+ labelText: "${LocaleKeys.radiology.tr(context: context)} Results".needTranslation,
+ // labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar",
// labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"],
labOrderTests: [],
isLoading: false,
@@ -483,19 +540,16 @@ class _MedicalFilePageState extends State {
}),
),
],
- ).paddingSymmetrical(24.w, 0.h),
+ ).paddingSymmetrical(0.w, 0.h),
SizedBox(height: 24.h),
- "Active Medications & Prescriptions".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
+ "Active Medications & Prescriptions".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
Consumer(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsOrdersLoading
- ? const CommonShimmerWidget().paddingSymmetrical(24.w, 0.h)
+ ? const CommonShimmerWidget().paddingSymmetrical(0.w, 0.h)
: prescriptionVM.patientPrescriptionOrders.isNotEmpty
? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: Colors.white,
- borderRadius: 12.r,
- ),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r, hasShadow: false),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
@@ -532,13 +586,11 @@ class _MedicalFilePageState extends State {
spacing: 3.w,
runSpacing: 4.w,
children: [
- AppCustomChipWidget(
- labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
+ AppCustomChipWidget(labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(
- DateUtil.convertStringToDate(
- prescriptionVM.patientPrescriptionOrders[index].appointmentDate),
+ DateUtil.convertStringToDate(prescriptionVM.patientPrescriptionOrders[index].appointmentDate),
false,
),
),
@@ -551,19 +603,13 @@ class _MedicalFilePageState extends State {
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
- icon: AppAssets.forward_arrow_icon_small,
- width: 15.w,
- height: 15.h,
- fit: BoxFit.contain,
- iconColor: AppColors.textColor)),
+ icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
CustomPageRoute(
- page: PrescriptionDetailPage(
- isFromAppointments: false,
- prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
+ page: PrescriptionDetailPage(isFromAppointments: false, prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
),
);
}),
@@ -573,9 +619,9 @@ class _MedicalFilePageState extends State {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
- SizedBox(height: 8.h),
+ SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
- SizedBox(height: 8.h),
+ SizedBox(height: 16.h),
Row(
children: [
Expanded(
@@ -600,7 +646,7 @@ class _MedicalFilePageState extends State {
iconSize: 16.w,
),
),
- SizedBox(width: 10.w),
+ SizedBox(width: 6.w),
Expanded(
child: CustomButton(
text: "All Medications".needTranslation,
@@ -622,7 +668,7 @@ class _MedicalFilePageState extends State {
],
),
),
- ).paddingSymmetrical(24.w, 0.h)
+ ).paddingSymmetrical(0.w, 0.h)
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@@ -636,14 +682,14 @@ class _MedicalFilePageState extends State {
width: 62.w,
height: 62.h,
),
- ).paddingSymmetrical(24.w, 0.h);
+ ).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 24.h),
//My Doctor Section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- LocaleKeys.myDoctor.tr(context: context).toText18(isBold: true),
+ LocaleKeys.myDoctor.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2),
Row(
children: [
LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
@@ -659,7 +705,7 @@ class _MedicalFilePageState extends State {
);
}),
],
- ).paddingSymmetrical(24.w, 0.h),
+ ).paddingSymmetrical(0.w, 0.h),
SizedBox(height: 16.h),
Consumer(builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isPatientMyDoctorsLoading
@@ -673,10 +719,7 @@ class _MedicalFilePageState extends State {
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true, radius: 50.r),
SizedBox(height: 8.h),
- ("Dr. John Smith Smith Smith")
- .toString()
- .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
- .toShimmer2(isShow: true),
+ ("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true),
],
)
: myAppointmentsVM.patientMyDoctorsList.isEmpty
@@ -694,14 +737,13 @@ class _MedicalFilePageState extends State {
width: 62.w,
height: 62.h,
),
- ).paddingSymmetrical(24.w, 0.h)
+ ).paddingSymmetrical(0.w, 0.h)
: SizedBox(
- height: 110.h,
+ height: 100.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.patientMyDoctorsList.length,
shrinkWrap: true,
- padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
@@ -721,8 +763,7 @@ class _MedicalFilePageState extends State {
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false, radius: 50.r),
SizedBox(height: 8.h),
- SizedBox(
- width: 80.w,
+ Expanded(
child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName)
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
@@ -761,10 +802,10 @@ class _MedicalFilePageState extends State {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
),
- );
+ ).paddingSymmetrical(0.w, 0);
}),
SizedBox(height: 24.h),
- "Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
+ "Others".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
@@ -801,7 +842,14 @@ class _MedicalFilePageState extends State {
svgIcon: AppAssets.allergy_info_icon,
isLargeText: true,
iconSize: 36.w,
- ),
+ ).onPress(() {
+ medicalFileViewModel.getPatientAllergiesList();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: AllergiesListPage(),
+ ),
+ );
+ }),
MedicalFileCard(
label: "Vaccine Info".needTranslation,
textColor: AppColors.blackColor,
@@ -817,7 +865,7 @@ class _MedicalFilePageState extends State {
);
}),
],
- ).paddingSymmetrical(24.w, 0.0),
+ ).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 24.h),
],
);
@@ -832,7 +880,7 @@ class _MedicalFilePageState extends State {
labOrder: null,
index: index,
isLoading: true,
- ).paddingSymmetrical(24.w, 0.0)
+ ).paddingSymmetrical(0.w, 0.0)
: insuranceVM.patientInsuranceList.isNotEmpty
? PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
@@ -859,14 +907,9 @@ class _MedicalFilePageState extends State {
text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
onPressed: () {
insuranceViewModel.setIsInsuranceUpdateDetailsLoading(true);
- insuranceViewModel.getPatientInsuranceDetailsForUpdate(appState.getAuthenticatedUser()!.patientId.toString(),
- appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
- showCommonBottomSheetWithoutHeight(context,
- child: PatientInsuranceCardUpdateCard(),
- callBackFunc: () {},
- title: "",
- isCloseButtonVisible: false,
- isFullScreen: false);
+ insuranceViewModel.getPatientInsuranceDetailsForUpdate(
+ appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
+ showCommonBottomSheetWithoutHeight(context, child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false);
},
backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
borderColor: AppColors.bgGreenColor.withOpacity(0.0),
@@ -878,7 +921,7 @@ class _MedicalFilePageState extends State {
height: isFoldable ? 50.h : 40.h,
).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h),
),
- ).paddingSymmetrical(24.w, 0.h);
+ ).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 10.h),
GridView(
@@ -933,7 +976,7 @@ class _MedicalFilePageState extends State {
iconSize: 36.w,
),
],
- ).paddingSymmetrical(24.w, 0.0),
+ ).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 16.h),
],
);
@@ -946,12 +989,12 @@ class _MedicalFilePageState extends State {
? PatientSickLeaveCard(
patientSickLeavesResponseModel: PatientSickLeavesResponseModel(),
isLoading: true,
- ).paddingSymmetrical(24.w, 0.0)
+ ).paddingSymmetrical(0.w, 0.0)
: medicalFileVM.patientSickLeaveList.isNotEmpty
? PatientSickLeaveCard(
patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
isLoading: false,
- ).paddingSymmetrical(24.w, 0.0)
+ ).paddingSymmetrical(0.w, 0.0)
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@@ -965,7 +1008,7 @@ class _MedicalFilePageState extends State {
width: 62.w,
height: 62.h,
),
- ).paddingSymmetrical(24.w, 0.h);
+ ).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 16.h),
GridView(
@@ -1018,7 +1061,7 @@ class _MedicalFilePageState extends State {
);
}),
],
- ).paddingSymmetrical(24.w, 0.0),
+ ).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 24.h),
],
);
diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart
index 696b332..eef73eb 100644
--- a/lib/presentation/medical_file/widgets/lab_rad_card.dart
+++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart
@@ -27,20 +27,27 @@ class LabRadCard extends StatelessWidget {
Widget build(BuildContext context) {
AppState appState = getIt.get();
return Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: false),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 18.r, hasShadow: false),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
+ mainAxisAlignment: MainAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(
icon: icon,
width: 40.w,
height: 40.h,
- fit: BoxFit.contain,
+ fit: BoxFit.cover,
).toShimmer2(isShow: false, radius: 12.r),
SizedBox(width: 8.w),
- Flexible(child: labelText.toText14(isBold: true).toShimmer2(isShow: false, radius: 6.r, height: 32.h)),
+ Flexible(
+ child: labelText.toText12(isBold: true, maxLine: 2),
+ ),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 10.w, height: 10.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
+ ),
],
),
// SizedBox(height: 16.h),
@@ -61,21 +68,21 @@ class LabRadCard extends StatelessWidget {
// )
// : "You don't have any records yet".needTranslation.toText13(
// color: AppColors.greyTextColor, isCenter: true),
- SizedBox(height: 16.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- "View All".toText12(isBold: true),
- Transform.flip(
- flipX: appState.isArabic(),
- child: Utils.buildSvgWithAssets(
- icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
- .toShimmer2(isShow: false, radius: 12.r),
- ),
- ],
- )
+ // SizedBox(height: 16.h),
+ // Row(
+ // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // children: [
+ // SizedBox.shrink(),
+ // Transform.flip(
+ // flipX: appState.isArabic(),
+ // child: Utils.buildSvgWithAssets(
+ // icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
+ // .toShimmer2(isShow: false, radius: 12.r),
+ // ),
+ // ],
+ // )
],
- ).paddingAll(16.w),
+ ).paddingAll(12.w),
);
}
}
diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart
index 5020b82..781624a 100644
--- a/lib/presentation/my_family/my_family.dart
+++ b/lib/presentation/my_family/my_family.dart
@@ -46,6 +46,9 @@ class _FamilyMedicalScreenState extends State {
void initState() {
super.initState();
medicalVM = context.read();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ medicalVM?.onFamilyFileTabChange(0);
+ });
}
@override
@@ -65,6 +68,7 @@ class _FamilyMedicalScreenState extends State {
text: "Add a new family member".needTranslation,
onPressed: () {
DialogService dialogService = getIt.get();
+ medicalVM!.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
@@ -83,13 +87,17 @@ class _FamilyMedicalScreenState extends State {
children: [
appState.isChildLoggedIn
? SizedBox()
- : CustomTabBar(
- activeBackgroundColor: AppColors.secondaryLightRedColor,
- activeTextColor: AppColors.primaryRedColor,
- tabs: [CustomTabBarModel(null, LocaleKeys.family.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())],
- onTabChange: (index) {
- medicalVM!.onFamilyFileTabChange(index);
- },
+ : Selector(
+ selector: (_, model) => model.getSelectedFamilyFileTabIndex,
+ builder: (context, selectedIndex, child) => CustomTabBar(
+ activeBackgroundColor: AppColors.secondaryLightRedColor,
+ activeTextColor: AppColors.primaryRedColor,
+ // selectedIndex: selectedIndex,
+ tabs: [CustomTabBarModel(null, LocaleKeys.family.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())],
+ onTabChange: (index) {
+ medicalVM!.onFamilyFileTabChange(index);
+ },
+ ),
),
appState.isChildLoggedIn ? SizedBox() : SizedBox(height: 25.h),
Selector(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)),
@@ -117,7 +125,7 @@ class _FamilyMedicalScreenState extends State {
case 1:
return FamilyCards(
profiles: medicalVM!.pendingFamilyFiles,
- isRequestDesign: true,
+ isRequestDesign: medicalVM!.getSelectedFamilyFileTabIndex == 1,
onSelect: (FamilyFileResponseModelLists profile) {
medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 3);
},
diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart
index e40dbd0..f73eba3 100644
--- a/lib/routes/app_routes.dart
+++ b/lib/routes/app_routes.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
+import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
@@ -27,6 +28,7 @@ class AppRoutes {
static const String comprehensiveCheckupPage = '/comprehensiveCheckupPage';
static const String homeHealthCarePage = '/homeHealthCarePage';
static const String zoomCallPage = '/zoomCallPage';
+ static const String bloodDonationPage = '/bloodDonationPage';
// Symptoms Checker
static const String organSelectorPage = '/organSelectorPage';
@@ -53,5 +55,7 @@ class AppRoutes {
suggestionsScreen: (context) => SuggestionsScreen(),
possibleConditionsScreen: (context) => PossibleConditionsScreen(),
triageScreen: (context) => TriageScreen()
+ zoomCallPage: (context) => CallScreen(),
+ bloodDonationPage: (context) => BloodDonationPage()
};
}
diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart
index b91e093..497a009 100644
--- a/lib/services/dialog_service.dart
+++ b/lib/services/dialog_service.dart
@@ -129,13 +129,21 @@ class DialogServiceImp implements DialogService {
if (context == null) return;
showCommonBottomSheetWithoutHeight(context,
title: label ?? "",
- child: FamilyCards(
- profiles: profiles,
- onSelect: (FamilyFileResponseModelLists profile) {
- onSwitchPress(profile);
- },
- onRemove: (FamilyFileResponseModelLists profile) {},
- isShowDetails: false,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ (message).toText16(isBold: false, color: AppColors.textColor),
+ SizedBox(height: 24.h),
+ FamilyCards(
+ profiles: profiles,
+ onSelect: (FamilyFileResponseModelLists profile) {
+ onSwitchPress(profile);
+ },
+ onRemove: (FamilyFileResponseModelLists profile) {},
+ isShowDetails: false,
+ ),
+ ],
),
callBackFunc: () {});
}
diff --git a/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart b/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart
index 73a4a47..b18bd58 100644
--- a/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart
+++ b/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart
@@ -10,7 +10,7 @@ class ExpandableBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
- print("the currently selected item is ${bottomSheetType}");
+ print("the currently selected item is $bottomSheetType");
return AnimatedCrossFade(
duration: const Duration(milliseconds: 600),
firstChild:children[BottomSheetType.FIXED] ?? SizedBox.shrink(),
diff --git a/lib/widgets/expandable_list_widget.dart b/lib/widgets/expandable_list_widget.dart
new file mode 100644
index 0000000..b948299
--- /dev/null
+++ b/lib/widgets/expandable_list_widget.dart
@@ -0,0 +1,301 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+
+// ==================== MAIN CUSTOM WIDGET ====================
+
+class CustomExpandableList extends StatefulWidget {
+ final List items;
+ final ExpansionMode expansionMode;
+ final EdgeInsetsGeometry? itemPadding;
+ final EdgeInsetsGeometry? contentPadding;
+ final Color? dividerColor;
+ final double dividerHeight;
+ final bool showDividers;
+ final Duration animationDuration;
+ final Curve animationCurve;
+ final ExpandableListTheme? theme;
+ final Function(int index, bool isExpanded)? onItemToggled;
+
+ const CustomExpandableList({
+ Key? key,
+ required this.items,
+ this.expansionMode = ExpansionMode.multiple,
+ this.itemPadding,
+ this.contentPadding,
+ this.dividerColor,
+ this.dividerHeight = 1.0,
+ this.showDividers = true,
+ this.animationDuration = const Duration(milliseconds: 300),
+ this.animationCurve = Curves.easeInOut,
+ this.theme,
+ this.onItemToggled,
+ }) : super(key: key);
+
+ @override
+ _CustomExpandableListState createState() => _CustomExpandableListState();
+}
+
+class _CustomExpandableListState extends State {
+ late List _expandedStates;
+
+ @override
+ void initState() {
+ super.initState();
+ _expandedStates = List.generate(
+ widget.items.length,
+ (index) => widget.items[index].initiallyExpanded,
+ );
+ }
+
+ void _toggleItem(int index) {
+ setState(() {
+ if (widget.expansionMode == ExpansionMode.exactlyOne) {
+ // Close all others, open this one
+ for (int i = 0; i < _expandedStates.length; i++) {
+ _expandedStates[i] = (i == index) ? !_expandedStates[i] : false;
+ }
+ } else if (widget.expansionMode == ExpansionMode.atMostOne) {
+ // Toggle this one, close if opening another
+ bool wasExpanded = _expandedStates[index];
+ for (int i = 0; i < _expandedStates.length; i++) {
+ if (i == index) {
+ _expandedStates[i] = !wasExpanded;
+ } else if (!wasExpanded) {
+ _expandedStates[i] = false;
+ }
+ }
+ } else {
+ // Multiple - just toggle the clicked item
+ _expandedStates[index] = !_expandedStates[index];
+ }
+ });
+
+ widget.onItemToggled?.call(index, _expandedStates[index]);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = widget.theme ?? ExpandableListTheme.defaultTheme(context);
+
+ return Column(
+ children: List.generate(widget.items.length, (index) {
+ final item = widget.items[index];
+ final isExpanded = _expandedStates[index];
+ final isLast = index == widget.items.length - 1;
+
+ return Column(
+ children: [
+ _ExpandableListItemWidget(
+ title: item.title,
+ isExpanded: isExpanded,
+ onTap: () => _toggleItem(index),
+ padding: widget.itemPadding,
+ contentPadding: widget.contentPadding,
+ theme: theme,
+ leading: item.leading,
+ trailing: item.trailing,
+ backgroundColor: item.backgroundColor,
+ expandedBackgroundColor: item.expandedBackgroundColor,
+ animationDuration: widget.animationDuration,
+ animationCurve: widget.animationCurve,
+ children: item.children,
+ ),
+ if (widget.showDividers && !isLast)
+ Divider(
+ height: widget.dividerHeight,
+ color: widget.dividerColor ?? theme.dividerColor,
+ ).paddingSymmetrical(12.h,0),
+ ],
+ );
+ }),
+ );
+ }
+}
+
+// ==================== SUPPORTING WIDGETS ====================
+
+class _ExpandableListItemWidget extends StatelessWidget {
+ final Widget title;
+ final List children;
+ final bool isExpanded;
+ final VoidCallback onTap;
+ final EdgeInsetsGeometry? padding;
+ final EdgeInsetsGeometry? contentPadding;
+ final ExpandableListTheme theme;
+ final Widget? leading;
+ final Widget? trailing;
+ final Color? backgroundColor;
+ final Color? expandedBackgroundColor;
+ final Duration animationDuration;
+ final Curve animationCurve;
+
+ const _ExpandableListItemWidget({
+ super.key,
+ required this.title,
+ required this.children,
+ required this.isExpanded,
+ required this.onTap,
+ this.padding,
+ this.contentPadding,
+ required this.theme,
+ this.leading,
+ this.trailing,
+ this.backgroundColor,
+ this.expandedBackgroundColor,
+ required this.animationDuration,
+ required this.animationCurve,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ color: isExpanded
+ ? (expandedBackgroundColor ?? theme.expandedBackgroundColor)
+ : (backgroundColor ?? theme.backgroundColor),
+ child: Column(
+ children: [
+ // Header
+ InkWell(
+ onTap: onTap,
+ child: Padding(
+ padding: padding ?? theme.itemPadding,
+ child: Row(
+ children: [
+ if (leading != null) ...[
+ leading!,
+ SizedBox(width: theme.leadingSpacing),
+ ],
+ Expanded(child: title),
+ if (trailing != null)
+ trailing!
+ else
+ AnimatedRotation(
+ turns: isExpanded ? 0.5 : 0.0,
+ duration: animationDuration,
+ curve: animationCurve,
+ child: theme.defaultTrailingIcon,
+ ),
+ ],
+ ),
+ ),
+ ),
+
+ // Content
+ AnimatedSize(
+ duration: animationDuration,
+ curve: animationCurve,
+ child: Container(
+ constraints: BoxConstraints(
+ minHeight: isExpanded ? 0.0 : 0.0,
+ maxHeight: isExpanded ? double.infinity : 0.0,
+ ),
+ child: isExpanded
+ ? Padding(
+ padding: contentPadding ?? theme.contentPadding,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: children,
+ ),
+ )
+ : SizedBox.shrink(),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// ==================== DATA MODELS ====================
+
+class ExpandableListItem {
+ final Widget title;
+ final List children;
+ final Widget? leading;
+ final Widget? trailing;
+ final bool initiallyExpanded;
+ final Color? backgroundColor;
+ final Color? expandedBackgroundColor;
+
+ const ExpandableListItem({
+ required this.title,
+ required this.children,
+ this.leading,
+ this.trailing,
+ this.initiallyExpanded = false,
+ this.backgroundColor,
+ this.expandedBackgroundColor,
+ });
+}
+
+// ==================== THEME ====================
+
+class ExpandableListTheme {
+ final Color backgroundColor;
+ final Color expandedBackgroundColor;
+ final Color dividerColor;
+ final Widget defaultTrailingIcon;
+ final EdgeInsetsGeometry itemPadding;
+ final EdgeInsetsGeometry contentPadding;
+ final double leadingSpacing;
+
+ const ExpandableListTheme({
+ required this.backgroundColor,
+ required this.expandedBackgroundColor,
+ required this.dividerColor,
+ required this.defaultTrailingIcon,
+ required this.itemPadding,
+ required this.contentPadding,
+ this.leadingSpacing = 12.0,
+ });
+
+ factory ExpandableListTheme.defaultTheme(BuildContext context) {
+ final textColor = Theme.of(context).textTheme.bodyLarge?.color ?? Colors.black;
+
+ return ExpandableListTheme(
+ backgroundColor: Colors.transparent,
+ expandedBackgroundColor: Colors.transparent,
+ dividerColor: Colors.grey.shade300,
+ defaultTrailingIcon: Icon(
+ Icons.keyboard_arrow_down,
+ color: textColor,
+ size: 24,
+ ),
+ itemPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
+ contentPadding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0),
+ );
+ }
+
+ factory ExpandableListTheme.custom({
+ Color? backgroundColor,
+ Color? expandedBackgroundColor,
+ Color? dividerColor,
+ Widget? defaultTrailingIcon,
+ EdgeInsetsGeometry? itemPadding,
+ EdgeInsetsGeometry? contentPadding,
+ double? leadingSpacing,
+ }) {
+ return ExpandableListTheme(
+ backgroundColor: backgroundColor ?? Colors.transparent,
+ expandedBackgroundColor: expandedBackgroundColor ?? Colors.grey.shade50,
+ dividerColor: dividerColor ?? Colors.grey.shade300,
+ defaultTrailingIcon: defaultTrailingIcon ??
+ Icon(Icons.keyboard_arrow_down, color: Colors.black, size: 24),
+ itemPadding: itemPadding ?? const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
+ contentPadding: contentPadding ?? const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0),
+ leadingSpacing: leadingSpacing ?? 12.0,
+ );
+ }
+}
+
+// ==================== ENUMS ====================
+
+enum ExpansionMode {
+ multiple, // Multiple items can be expanded
+ exactlyOne, // Exactly one item expanded at a time
+ atMostOne, // Zero or one item expanded at a time
+}
+
+
+
diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart
index 4b3f091..c1a38ab 100644
--- a/lib/widgets/input_widget.dart
+++ b/lib/widgets/input_widget.dart
@@ -47,38 +47,40 @@ class TextInputWidget extends StatelessWidget {
final bool isMultiline;
final int minLines;
final int maxLines;
+ final Color? hintColor;
// final List countryList;
// final Function(Country)? onCountryChange;
- TextInputWidget(
- {super.key,
- required this.labelText,
- required this.hintText,
- this.controller,
- this.onChange,
- this.onCalendarTypeChanged,
- this.prefix,
- this.isEnable = true,
- this.isBorderAllowed = true,
- this.isAllowRadius = true,
- this.isReadOnly = false,
- this.keyboardType = TextInputType.number,
- this.focusNode,
- this.autoFocus = false,
- this.padding,
- this.isAllowLeadingIcon = false,
- this.leadingIcon,
- this.isCountryDropDown = false,
- this.hasError = false,
- this.errorMessage,
- this.onCountryChange,
- this.selectionType,
- this.fontSize,
- this.isWalletAmountInput = false,
- this.suffix,
- this.labelColor,
+ TextInputWidget({
+ super.key,
+ required this.labelText,
+ required this.hintText,
+ this.controller,
+ this.onChange,
+ this.onCalendarTypeChanged,
+ this.prefix,
+ this.isEnable = true,
+ this.isBorderAllowed = true,
+ this.isAllowRadius = true,
+ this.isReadOnly = false,
+ this.keyboardType = TextInputType.number,
+ this.focusNode,
+ this.autoFocus = false,
+ this.padding,
+ this.isAllowLeadingIcon = false,
+ this.leadingIcon,
+ this.isCountryDropDown = false,
+ this.hasError = false,
+ this.errorMessage,
+ this.onCountryChange,
+ this.selectionType,
+ this.fontSize,
+ this.isWalletAmountInput = false,
+ this.suffix,
+ this.labelColor,
this.onSubmitted,
+ this.hintColor,
// multiline defaults
this.isMultiline = false,
this.minLines = 3,
@@ -274,7 +276,7 @@ class TextInputWidget extends StatelessWidget {
decoration: InputDecoration(
isDense: true,
hintText: hintText,
- hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.75),
+ hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w500, color: hintColor != null ? AppColors.textColor : Color(0xff898A8D), letterSpacing: -0.75),
prefixIconConstraints: BoxConstraints(minWidth: 30.h),
prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, weight: FontWeight.w500),
contentPadding: EdgeInsets.zero,
diff --git a/lib/widgets/radio/custom_radio_button.dart b/lib/widgets/radio/custom_radio_button.dart
index 080fcb3..a530102 100644
--- a/lib/widgets/radio/custom_radio_button.dart
+++ b/lib/widgets/radio/custom_radio_button.dart
@@ -11,14 +11,13 @@ class CustomRadioOption extends StatelessWidget {
// final Widget child; // The content of your radio option (e.g., Text, Image)
- const CustomRadioOption({
- super.key,
- required this.value,
- required this.groupValue,
- required this.onChanged,
- // required this.child,
- required this.text,
- });
+ const CustomRadioOption(
+ {super.key,
+ required this.value,
+ required this.groupValue,
+ required this.onChanged,
+ // required this.child,
+ required this.text});
@override
Widget build(BuildContext context) {
@@ -31,13 +30,12 @@ class CustomRadioOption extends StatelessWidget {
child: Row(
children: [
Container(
- width: 20.h,
- height: 20.h,
+ width: 18.h,
+ height: 18.h,
decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
- border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h),
- ),
+ shape: BoxShape.circle,
+ color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
+ border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h)),
),
SizedBox(width: 8.h),
text.toText16(weight: FontWeight.w500), // The provided content