Completed the health Tracker Module, Only the email part is left.

faiz_dev
faizatflutter 4 days ago
parent 17f32e34d3
commit 3f5bd0a759

@ -0,0 +1,4 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 10C2 5.58172 5.58172 2 10 2C14.4183 2 18 5.58172 18 10C18 10.5523 18.4477 11 19 11C19.5523 11 20 10.5523 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 10.5523 0.447715 11 1 11C1.55228 11 2 10.5523 2 10Z" fill="white"/>
<path d="M10 6C10.5523 6 11 6.44772 11 7L11 12.1707C12.1652 12.5825 13 13.6938 13 15C13 16.6569 11.6569 18 10 18C8.34315 18 7 16.6569 7 15C7 13.6938 7.83481 12.5825 9 12.1707L9 7C9 6.44772 9.44772 6 10 6Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 568 B

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 6.75C4 3.02208 7.02208 0 10.75 0C14.4779 0 17.5 3.02208 17.5 6.75C17.5 6.81385 17.5 6.84578 17.4946 6.87394C17.4654 7.02516 17.324 7.13066 17.1708 7.11566C17.1422 7.11287 17.1041 7.10157 17.028 7.07899C16.306 6.86492 15.5414 6.75 14.75 6.75C11.4211 6.75 8.56706 8.78325 7.36206 11.6756C7.21403 12.031 7.14001 12.2086 6.99327 12.2449C6.84654 12.2812 6.71484 12.1723 6.45145 11.9546L6.45144 11.9546C4.95411 10.7165 4 8.84466 4 6.75ZM8 14.75C8 11.0221 11.0221 8 14.75 8C18.4779 8 21.5 11.0221 21.5 14.75C21.5 18.4779 18.4779 21.5 14.75 21.5C11.0221 21.5 8 18.4779 8 14.75ZM6.77538 14.11C6.75857 14.3217 6.75 14.5358 6.75 14.7519C6.75 16.87 7.57313 18.7958 8.91695 20.227L8.91696 20.227C9.22203 20.5519 9.37457 20.7143 9.33165 20.8754C9.28873 21.0365 9.09982 21.0941 8.72199 21.2093C8.09819 21.3996 7.43605 21.5019 6.75 21.5019C3.02208 21.5019 0 18.4798 0 14.7519C0 12.6572 0.954182 10.7853 2.45161 9.54723C2.715 9.32946 2.8467 9.22057 2.99343 9.25688C3.14016 9.29319 3.21416 9.4708 3.36215 9.82601C3.9893 11.3312 5.0627 12.6033 6.41512 13.4766L6.4968 13.5294C6.64871 13.6275 6.72467 13.6765 6.76134 13.7507C6.79802 13.8249 6.79047 13.9199 6.77538 14.11Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.0625 16.125C3.6097 16.125 0 12.5153 0 8.0625C0 3.6097 3.6097 0 8.0625 0C12.5153 0 16.125 3.6097 16.125 8.0625C16.125 12.5153 12.5153 16.125 8.0625 16.125ZM8.0625 4.3125C8.47671 4.3125 8.8125 4.64829 8.8125 5.0625V8.43749H8.93401C9.15181 8.43742 9.38226 8.43734 9.56413 8.45912C9.69673 8.47499 10.1846 8.53481 10.4097 8.99153C10.635 9.4486 10.3763 9.85561 10.3061 9.96553C10.2094 10.1169 10.0646 10.2914 9.92764 10.4564L9.90413 10.4848C9.68853 10.7449 9.43178 11.0409 9.1743 11.2764C9.04572 11.394 8.89699 11.5151 8.73553 11.6109C8.58584 11.6996 8.34945 11.8125 8.0625 11.8125C7.77556 11.8125 7.53916 11.6996 7.38947 11.6109C7.22801 11.5151 7.07929 11.394 6.95071 11.2764C6.69322 11.0409 6.43647 10.7449 6.22087 10.4848L6.19736 10.4564C6.06041 10.2914 5.91565 10.1169 5.81894 9.96552C5.74871 9.85561 5.49001 9.4486 5.71529 8.99153C5.94038 8.53481 6.42827 8.47499 6.56087 8.45912C6.74274 8.43734 6.9732 8.43742 7.191 8.43749H7.3125L7.3125 5.0625C7.3125 4.64829 7.64829 4.3125 8.0625 4.3125Z" fill="#FF9E15"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,7 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="3" rx="1.5" fill="#18C273"/>
<rect y="4" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.7"/>
<rect y="8" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.5"/>
<rect y="12" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.35"/>
<rect y="16" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 476 B

@ -0,0 +1,3 @@
<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.52965 0.0727261C9.98692 -0.024143 11.5101 -0.024341 12.9704 0.0727261C17.537 0.376286 21.1603 4.06258 21.4582 8.67235C21.5139 9.53477 21.5139 10.4268 21.4582 11.2892C21.1603 15.899 17.537 19.5853 12.9704 19.8889C11.5101 19.9859 9.98692 19.9857 8.52965 19.8889C7.96472 19.8513 7.34978 19.7177 6.80839 19.4948C6.5703 19.3967 6.40868 19.3304 6.29041 19.287C6.20909 19.3429 6.10108 19.4223 5.9438 19.5383C5.15136 20.1226 4.15092 20.5325 2.73117 20.498L2.68544 20.4969C2.41155 20.4903 2.11961 20.4834 1.88152 20.4373C1.59475 20.3818 1.23996 20.2431 1.01791 19.8645C0.776229 19.4525 0.873128 19.0358 0.966883 18.7734C1.05536 18.5257 1.20874 18.2352 1.36542 17.9385L1.3869 17.8978C1.85323 17.0141 1.98314 16.2919 1.73381 15.8104C0.901479 14.554 0.152725 13.0056 0.0418013 11.2892C-0.0139338 10.4268 -0.0139338 9.53477 0.0418013 8.67235C0.339716 4.06258 3.963 0.376286 8.52965 0.0727261ZM6.5 7.75C6.5 8.16421 6.83579 8.5 7.25 8.5H10.75C11.1642 8.5 11.5 8.16421 11.5 7.75C11.5 7.33579 11.1642 7 10.75 7L7.25 7C6.83579 7 6.5 7.33579 6.5 7.75ZM6.5 12.75C6.5 13.1642 6.83579 13.5 7.25 13.5L14.25 13.5C14.6642 13.5 15 13.1642 15 12.75C15 12.3358 14.6642 12 14.25 12L7.25 12C6.83579 12 6.5 12.3358 6.5 12.75Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.39392 1.28104e-06L11.1061 1.28104e-06C12.009 -2.95155e-05 12.7711 -5.55068e-05 13.3749 0.0704231C14.005 0.143956 14.6207 0.308473 15.1206 0.759151C15.3458 0.962221 15.5373 1.19899 15.6876 1.46113C15.9826 1.97573 16.0312 2.52334 15.9847 3.06609C16.9603 3.1787 17.7696 3.43604 18.4021 4.06966C19.0038 4.67238 19.2625 5.43113 19.3835 6.33241C19.5 7.2007 19.5 8.30477 19.5 9.67486V14.793C19.5 16.1631 19.5 17.2672 19.3835 18.1355C19.2625 19.0367 19.0038 19.7955 18.4021 20.3982C17.8002 21.0011 17.0423 21.2605 16.1419 21.3818C15.2749 21.4986 14.1725 21.4986 12.805 21.4985L6.69504 21.4985C5.32752 21.4986 4.22513 21.4986 3.35808 21.3818C2.45774 21.2605 1.69976 21.0011 1.0979 20.3982C0.496209 19.7955 0.237469 19.0367 0.116503 18.1355C-3.37213e-05 17.2672 -1.84605e-05 16.1631 4.81163e-07 14.7931L4.81163e-07 9.67484C-1.84605e-05 8.30478 -3.37213e-05 7.20069 0.116503 6.33241C0.237469 5.43113 0.496209 4.67238 1.0979 4.06966C1.73043 3.43604 2.53975 3.1787 3.51534 3.06608C3.46879 2.52334 3.51736 1.97573 3.81239 1.46113C3.96268 1.19899 4.15422 0.962221 4.37944 0.759151C4.8793 0.308473 5.49503 0.143956 6.12506 0.0704231C6.72893 -5.54994e-05 7.49097 -2.95155e-05 8.39392 1.28104e-06ZM6.35067 1.9573C5.88641 2.01149 5.74252 2.10241 5.67575 2.1626C5.5993 2.23154 5.53534 2.31095 5.48568 2.39756C5.44637 2.46613 5.39027 2.6108 5.44746 3.05137C5.50673 3.50797 5.66092 4.11081 5.89823 5.02796C6.08725 5.75848 6.21246 6.23783 6.34555 6.59684C6.47171 6.93716 6.57647 7.08584 6.6787 7.18308C6.75978 7.26021 6.85016 7.32847 6.9482 7.38631C7.07461 7.46091 7.2556 7.52465 7.63389 7.56094C7.88784 7.58531 8.19058 7.59448 8.58009 7.59793L9.30138 5.43404C9.47603 4.9101 10.0424 4.62694 10.5663 4.80159C11.0902 4.97624 11.3734 5.54256 11.1988 6.0665L10.6878 7.59935C11.1924 7.59743 11.5638 7.58994 11.8661 7.56094C12.2444 7.52465 12.4254 7.46091 12.5518 7.38631C12.6498 7.32847 12.7402 7.26021 12.8213 7.18308C12.9235 7.08584 13.0283 6.93716 13.1545 6.59684C13.2875 6.23783 13.4128 5.75848 13.6018 5.02796C13.8391 4.11081 13.9933 3.50797 14.0525 3.05137C14.1097 2.6108 14.0536 2.46613 14.0143 2.39756C13.9647 2.31095 13.9007 2.23153 13.8242 2.1626C13.7575 2.10241 13.6136 2.01149 13.1493 1.9573C12.6718 1.90157 12.0247 1.9 11.0479 1.9L8.45213 1.9C7.47532 1.9 6.82816 1.90157 6.35067 1.9573ZM7.75 15.75C7.19772 15.75 6.75 16.1977 6.75 16.75C6.75 17.3023 7.19772 17.75 7.75 17.75H11.75C12.3023 17.75 12.75 17.3023 12.75 16.75C12.75 16.1977 12.3023 15.75 11.75 15.75L7.75 15.75Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -210,7 +210,7 @@ class ApiClientImp implements ApiClient {
final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
// log("response.body: ${response.body}");
log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);

@ -398,19 +398,6 @@ var GET_COVID_DRIVETHRU_PROCEDURES_LIST = 'Services/Doctors.svc/REST/COVID19_Get
var GET_PATIENT_LAST_RECORD = 'Services/Patients.svc/REST/Med_GetPatientLastRecord';
var INSERT_PATIENT_HEALTH_DATA = 'Services/Patients.svc/REST/Med_InsertTransactions';
///My Trackers
var GET_DIABETIC_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
var GET_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
var ADD_DIABTEC_RESULT = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
var GET_BLOOD_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
var GET_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
var ADD_BLOOD_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
var GET_WEIGHT_PRESSURE_RESULT_AVERAGE = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
var GET_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
var ADD_WEIGHT_PRESSURE_RESULT = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
var ADD_ACTIVE_PRESCRIPTIONS_REPORT_BY_PATIENT_ID = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
var GET_CALL_INFO_HOURS_RESULT = 'Services/Doctors.svc/REST/GetCallInfoHoursResult';
@ -420,7 +407,6 @@ var GET_QUESTION_TYPES = 'Services/OUTPs.svc/REST/getQuestionsTypes';
var UPDATE_DIABETIC_RESULT = 'Services/Patients.svc/REST/Patient_UpdateDiabeticResult';
var SEND_AVERAGE_BLOOD_SUGAR_REPORT = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
var DEACTIVATE_DIABETIC_STATUS = 'services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
var DEACTIVATE_BLOOD_PRESSURES_STATUS = 'services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
@ -826,7 +812,6 @@ class ApiConsts {
static final String addHHCOrder = 'api/HHC/add';
// SYMPTOMS CHECKER API
static final String symptomsUserLogin = '$symptomsCheckerApi/user_login';
static final String getBodySymptomsByName = '$symptomsCheckerApi/GetBodySymptomsByName';
static final String getRiskFactors = '$symptomsCheckerApi/GetRiskFactors';
@ -850,6 +835,31 @@ class ApiConsts {
static String h2oUpdateUserDetail = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New";
static String h2oUndoUserActivity = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
// HEALTH TRACKERS
// Blood Sugar (Diabetic)
static String getDiabeticResultAverage = 'Services/Patients.svc/REST/Patient_GetDiabeticResultAverage';
static String getDiabeticResult = 'Services/Patients.svc/REST/Patient_GetDiabtecResults';
static String addDiabeticResult = 'Services/Patients.svc/REST/Patient_AddDiabtecResult';
static String updateDiabeticResult = 'Services/Patients.svc/REST/Patient_UpdateDiabtecResult';
static String deactivateDiabeticStatus = 'Services/Patients.svc/REST/Patient_DeactivateDiabeticStatus';
static String sendAverageBloodSugarReport = 'Services/Notifications.svc/REST/SendAverageBloodSugarReport';
// Blood Pressure
static String getBloodPressureResultAverage = 'Services/Patients.svc/REST/Patient_GetBloodPressureResultAverage';
static String getBloodPressureResult = 'Services/Patients.svc/REST/Patient_GetBloodPressureResult';
static String addBloodPressureResult = 'Services/Patients.svc/REST/Patient_AddBloodPressureResult';
static String updateBloodPressureResult = 'Services/Patients.svc/REST/Patient_UpdateBloodPressureResult';
static String deactivateBloodPressureStatus = 'Services/Patients.svc/REST/Patient_DeactivateBloodPressureStatus';
static String sendAverageBloodPressureReport = 'Services/Notifications.svc/REST/SendAverageBloodPressureReport';
// Weight Measurement
static String getWeightMeasurementResultAverage = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResultAverage';
static String getWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_GetWeightMeasurementResult';
static String addWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_AddWeightMeasurementResult';
static String updateWeightMeasurementResult = 'Services/Patients.svc/REST/Patient_UpdateWeightMeasurementResult';
static String deactivateWeightMeasurementStatus = 'Services/Patients.svc/REST/Patient_DeactivateWeightMeasurementStatus';
static String sendAverageBodyWeightReport = 'Services/Notifications.svc/REST/SendAverageBodyWeightReport';
// ************ static values for Api ****************
static final double appVersionID = 50.3;
static final int appChannelId = 3;

@ -267,16 +267,21 @@ class AppAssets {
static const String yellowArrowDownIcon = '$svgBasePath/yellow_arrow_down_icon.svg';
static const String greenTickIcon = '$svgBasePath/green_tick_icon.svg';
// PNGS
static const String bloodSugar = '$svgBasePath/bloodsugar.svg';
static const String bloodCholestrol = '$svgBasePath/bloodcholestrol.svg';
static const String triglycerides = '$svgBasePath/triglycerides.svg';
static const String bulb = '$svgBasePath/bulb.svg';
static const String switchBtn = '$svgBasePath/switch.svg';
//Health Trackers
static const String bloodPressureIcon = '$svgBasePath/blood_pressure_icon.svg';
static const String bloodSugarOnlyIcon = '$svgBasePath/blood_sugar_only_icon.svg';
static const String weightIcon = '$svgBasePath/weight_icon.svg';
static const String normalStatusGreenIcon = '$svgBasePath/normal_status_green_icon.svg';
static const String sendEmailIcon = '$svgBasePath/send_email_icon.svg';
static const String lowIndicatorIcon = '$svgBasePath/low_indicator_icon.svg';
// Health Calculators
static const String ovulationAccordion = '$svgBasePath/approximate_ovulation_accordion.svg';
static const String nextPeriodAccordion = '$svgBasePath/next_period_accordion.svg';
static const String fertileAccordion = '$svgBasePath/fertile_window_accordion.svg';
@ -285,9 +290,7 @@ class AppAssets {
static const String covid19icon = '$svgBasePath/covid_19.svg';
//vital sign
static const String heartRate = '$svgBasePath/heart_rate.svg';
static const String respRate = '$svgBasePath/resp_rate.svg';
static const String weightVital = '$svgBasePath/weight_2.svg';
@ -296,8 +299,6 @@ class AppAssets {
static const String bloodPressure = '$svgBasePath/blood_pressure.svg';
static const String temperature = '$svgBasePath/temperature.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';
static const String liveCareService = '$pngBasePath/livecare_service.png';
@ -324,8 +325,6 @@ class AppAssets {
static const String fullBodyFront = '$pngBasePath/full_body_front.png';
static const String fullBodyBack = '$pngBasePath/full_body_back.png';
static const String bmiFullBody = '$pngBasePath/bmi_image_1.png';
}
class AppAnimations {
@ -346,6 +345,4 @@ class AppAnimations {
static const String ambulance = '$lottieBasePath/ambulance.json';
static const String ambulanceAlert = '$lottieBasePath/ambulance_alert.json';
static const String rrtAmbulance = '$lottieBasePath/rrt_ambulance.json';
}

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@ -50,6 +51,7 @@ import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -141,6 +143,7 @@ class AppDependencies {
getIt.registerLazySingleton<BloodDonationRepo>(() => BloodDonationRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<WaterMonitorRepo>(() => WaterMonitorRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MyInvoicesRepo>(() => MyInvoicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<HealthTrackersRepo>(() => HealthTrackersRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
@ -167,10 +170,7 @@ class AppDependencies {
);
getIt.registerLazySingleton<HabibWalletViewModel>(
() => HabibWalletViewModel(
habibWalletRepo: getIt(),
errorHandlerService: getIt()
),
() => HabibWalletViewModel(habibWalletRepo: getIt(), errorHandlerService: getIt()),
);
getIt.registerLazySingleton<MedicalFileViewModel>(
@ -273,8 +273,13 @@ class AppDependencies {
getIt.registerLazySingleton<HealthProvider>(() => HealthProvider());
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt()));
getIt.registerLazySingleton<WaterMonitorViewModel>(() => WaterMonitorViewModel(waterMonitorRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(myInvoicesRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton<MyInvoicesViewModel>(() => MyInvoicesViewModel(
myInvoicesRepo: getIt(),
errorHandlerService: getIt(),
navServices: getIt(),
));
getIt.registerLazySingleton<HealthTrackersViewModel>(() => HealthTrackersViewModel(healthTrackersRepo: getIt(), errorHandlerService: getIt()));
}
}

@ -16,7 +16,7 @@ enum CountryEnum { saudiArabia, unitedArabEmirates }
enum CalenderEnum { gregorian, hijri }
enum SelectionTypeEnum { dropdown, calendar, search }
enum SelectionTypeEnum { dropdown, calendar, search, time }
enum GenderTypeEnum { male, female }
@ -38,7 +38,19 @@ enum HealthCalConEnum { calculator, converter }
enum HealthCalculatorEnum { general, women }
enum HealthCalculatorsTypeEnum { bmi, calories, bmr, idealBodyWeight, bodyFat, crabsProteinFat, ovulation, deliveryDueDate, bloodSugar, bloodCholesterol, triglycerides }
enum HealthCalculatorsTypeEnum {
bmi,
calories,
bmr,
idealBodyWeight,
bodyFat,
crabsProteinFat,
ovulation,
deliveryDueDate,
bloodSugar,
bloodCholesterol,
triglycerides
}
extension HealthCalculatorExtenshion on HealthCalculatorsTypeEnum {
String get displayName {
@ -297,3 +309,5 @@ extension ServiceTypeEnumExt on ServiceTypeEnum {
// SymptomsChecker
enum PossibleConditionsSeverityEnum { seekMedicalAdvice, monitorOnly, emergency }
enum HealthTrackerTypeEnum { bloodSugar, bloodPressure, weightTracker }

@ -7,16 +7,14 @@ import 'package:flutter/material.dart'; // These are the Viewport values of your
const num figmaDesignWidth = 375; // iPhone X / 12 base width
const num figmaDesignHeight = 812; // iPhone X / 12 base height
extension ConstrainedResponsive on num {
/// Width with max cap for tablets
double get wCapped => isTablet ? math.min( w, this * 1.3) : w;
double get wCapped => isTablet ? math.min(w, this * 1.3) : w;
/// Height with max cap for tablets
double get hCapped => isTablet ? math.min(h, this * 1.3) : h;
}
extension ResponsiveExtension on num {
double get _screenWidth => SizeUtils.width;

@ -0,0 +1,752 @@
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/services/logger_service.dart';
/// Progress types to request different ranges from the progress API.
enum ProgressType { today, week, month }
abstract class HealthTrackersRepo {
// ==================== BLOOD SUGAR (DIABETIC) ====================
/// Get blood sugar result averages (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResultAverage();
/// Get blood sugar results (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResults();
/// Add new blood sugar result.
Future<Either<Failure, GenericApiModel<dynamic>>> addDiabeticResult({
required String bloodSugarDateChart,
required String bloodSugarResult,
required String diabeticUnit,
required int measuredTime,
});
/// Update existing blood sugar result.
Future<Either<Failure, GenericApiModel<dynamic>>> updateDiabeticResult({
required DateTime month,
required DateTime hour,
required String bloodSugarResult,
required String diabeticUnit,
required int measuredTime,
required int lineItemNo,
});
/// Deactivate blood sugar record.
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateDiabeticStatus({
required int lineItemNo,
});
// ==================== BLOOD PRESSURE ====================
/// Get blood pressure result averages (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResultAverage();
/// Get blood pressure results (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResults();
/// Add new blood pressure result.
Future<Either<Failure, GenericApiModel<dynamic>>> addBloodPressureResult({
required String bloodPressureDate,
required String diastolicPressure,
required String systolicePressure,
required int measuredArm,
});
/// Update existing blood pressure result.
Future<Either<Failure, GenericApiModel<dynamic>>> updateBloodPressureResult({
required String bloodPressureDate,
required String diastolicPressure,
required String systolicePressure,
required int measuredArm,
required int lineItemNo,
});
/// Deactivate blood pressure record.
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateBloodPressureStatus({
required int lineItemNo,
});
// ==================== WEIGHT MEASUREMENT ====================
/// Get weight measurement result averages (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResultAverage();
/// Get weight measurement results (week, month, year).
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResults();
/// Add new weight measurement result.
Future<Either<Failure, GenericApiModel<dynamic>>> addWeightMeasurementResult({
required String weightDate,
required String weightMeasured,
required int weightUnit,
});
/// Update existing weight measurement result.
Future<Either<Failure, GenericApiModel<dynamic>>> updateWeightMeasurementResult({
required int lineItemNo,
required int weightUnit,
required String weightMeasured,
required String weightDate,
});
/// Deactivate weight measurement record.
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateWeightMeasurementStatus({
required int lineItemNo,
});
}
class HealthTrackersRepoImp implements HealthTrackersRepo {
final ApiClient apiClient;
final LoggerService loggerService;
HealthTrackersRepoImp({required this.loggerService, required this.apiClient});
// ==================== BLOOD SUGAR (DIABETIC) METHODS ====================
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResultAverage() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getDiabeticResultAverage,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract average lists
extracted = {
'monthAverageList': response['List_MonthDiabtectResultAverage'] ?? [],
'weekAverageList': response['List_WeekDiabtectResultAverage'] ?? [],
'yearAverageList': response['List_YearDiabtecResultAverage'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResults() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getDiabeticResult,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract patient result lists
extracted = {
'monthResultList': response['List_MonthDiabtecPatientResult'] ?? [],
'weekResultList': response['List_WeekDiabtecPatientResult'] ?? [],
'yearResultList': response['List_YearDiabtecPatientResult'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> addDiabeticResult({
required String bloodSugarDateChart,
required String bloodSugarResult,
required String diabeticUnit,
required int measuredTime,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'BloodSugerDateChart': bloodSugarDateChart,
'BloodSugerResult': bloodSugarResult,
'DiabtecUnit': diabeticUnit,
'MeasuredTime': measuredTime + 1, // Add 1 as per old service
};
await apiClient.post(
ApiConsts.addDiabeticResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> updateDiabeticResult({
required DateTime month,
required DateTime hour,
required String bloodSugarResult,
required String diabeticUnit,
required int measuredTime,
required int lineItemNo,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
// Format: 'YYYY-MM-DD HH:MM:SS' as per old service
String formattedDate = '${month.year}-${month.month}-${month.day} ${hour.hour}:${hour.minute}:00';
Map<String, dynamic> body = {
'BloodSugerDateChart': formattedDate,
'BloodSugerResult': bloodSugarResult,
'DiabtecUnit': diabeticUnit,
'MeasuredTime': measuredTime + 1, // Add 1 as per old service
'LineItemNo': lineItemNo,
};
await apiClient.post(
ApiConsts.updateDiabeticResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> deactivateDiabeticStatus({
required int lineItemNo,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'LineItemNo': lineItemNo,
};
await apiClient.post(
ApiConsts.deactivateDiabeticStatus,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
// ==================== BLOOD PRESSURE METHODS ====================
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResultAverage() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getBloodPressureResultAverage,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract all three list types
extracted = {
'monthList': response['List_MonthBloodPressureResultAverage'] ?? [],
'weekList': response['List_WeekBloodPressureResultAverage'] ?? [],
'yearList': response['List_YearBloodPressureResultAverage'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResults() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getBloodPressureResult,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract all three list types
extracted = {
'weekList': response['List_WeekBloodPressureResult'] ?? [],
'monthList': response['List_MonthBloodPressureResult'] ?? [],
'yearList': response['List_YearBloodPressureResult'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> addBloodPressureResult({
required String bloodPressureDate,
required String diastolicPressure,
required String systolicePressure,
required int measuredArm,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'BloodPressureDate': bloodPressureDate,
'DiastolicPressure': diastolicPressure,
'SystolicePressure': systolicePressure,
'MeasuredArm': measuredArm,
};
await apiClient.post(
ApiConsts.addBloodPressureResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> updateBloodPressureResult({
required String bloodPressureDate,
required String diastolicPressure,
required String systolicePressure,
required int measuredArm,
required int lineItemNo,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'BloodPressureDate': bloodPressureDate,
'DiastolicPressure': diastolicPressure,
'SystolicePressure': systolicePressure,
'MeasuredArm': measuredArm,
'LineItemNo': lineItemNo,
};
await apiClient.post(
ApiConsts.updateBloodPressureResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> deactivateBloodPressureStatus({
required int lineItemNo,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'LineItemNo': lineItemNo,
};
await apiClient.post(
ApiConsts.deactivateBloodPressureStatus,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
// ==================== WEIGHT MEASUREMENT METHODS ====================
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResultAverage() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getWeightMeasurementResultAverage,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract average lists
extracted = {
'monthAverageList': response['List_MonthWeightMeasurementResultAverage'] ?? [],
'weekAverageList': response['List_WeekWeightMeasurementResultAverage'] ?? [],
'yearAverageList': response['List_YearWeightMeasurementResultAverage'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResults() async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getWeightMeasurementResult,
body: {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
dynamic extracted;
if (response is Map) {
// Extract result lists
extracted = {
'weekResultList': response['List_WeekWeightMeasurementResult'] ?? [],
'monthResultList': response['List_MonthWeightMeasurementResult'] ?? [],
'yearResultList': response['List_YearWeightMeasurementResult'] ?? [],
};
} else {
extracted = response;
}
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: extracted,
);
} 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<Either<Failure, GenericApiModel<dynamic>>> addWeightMeasurementResult({
required String weightDate,
required String weightMeasured,
required int weightUnit,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'WeightDate': weightDate,
'WeightMeasured': weightMeasured,
'weightUnit': weightUnit,
};
await apiClient.post(
ApiConsts.addWeightMeasurementResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> updateWeightMeasurementResult({
required int lineItemNo,
required int weightUnit,
required String weightMeasured,
required String weightDate,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'LineItemNo': lineItemNo,
'weightUnit': '$weightUnit', // Convert to string as per old service
'WeightMeasured': weightMeasured,
'WeightDate': weightDate,
};
await apiClient.post(
ApiConsts.updateWeightMeasurementResult,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
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<Either<Failure, GenericApiModel<dynamic>>> deactivateWeightMeasurementStatus({
required int lineItemNo,
}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
Map<String, dynamic> body = {
'LineItemNo': lineItemNo,
};
await apiClient.post(
ApiConsts.deactivateWeightMeasurementStatus,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,87 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class BloodPressureResult {
int? patientID;
int? lineItemNo;
DateTime? bloodPressureDate;
int? measuredArm;
int? systolicePressure;
int? diastolicPressure;
dynamic remark;
bool? isActive;
int? chartYear;
String? chartMonth;
dynamic yearSystolicePressureAverageResult;
dynamic monthSystolicePressureResult;
dynamic weekSystolicePressureResult;
int? yearDiastolicPressureAverageResult;
dynamic monthDiastolicPressureResult;
dynamic weekDiastolicPressureResult;
String? measuredArmDesc;
dynamic weekDesc;
BloodPressureResult(
{this.patientID,
this.lineItemNo,
this.bloodPressureDate,
this.measuredArm,
this.systolicePressure,
this.diastolicPressure,
this.remark,
this.isActive,
this.chartYear,
this.chartMonth,
this.yearSystolicePressureAverageResult,
this.monthSystolicePressureResult,
this.weekSystolicePressureResult,
this.yearDiastolicPressureAverageResult,
this.monthDiastolicPressureResult,
this.weekDiastolicPressureResult,
this.measuredArmDesc,
this.weekDesc});
BloodPressureResult.fromJson(Map<String, dynamic> json) {
patientID = json['PatientID'];
lineItemNo = json['LineItemNo'];
bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
measuredArm = json['MeasuredArm'];
systolicePressure = json['SystolicePressure'];
diastolicPressure = json['DiastolicPressure'];
remark = json['Remark'];
isActive = json['IsActive'];
chartYear = json['ChartYear'];
chartMonth = json['ChartMonth'];
yearSystolicePressureAverageResult = json['YearSystolicePressureAverageResult'];
monthSystolicePressureResult = json['MonthSystolicePressureResult'];
weekSystolicePressureResult = json['WeekSystolicePressureResult'];
yearDiastolicPressureAverageResult = json['YearDiastolicPressureAverageResult'];
monthDiastolicPressureResult = json['MonthDiastolicPressureResult'];
weekDiastolicPressureResult = json['WeekDiastolicPressureResult'];
measuredArmDesc = json['MeasuredArmDesc'];
weekDesc = json['WeekDesc'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['PatientID'] = patientID;
data['LineItemNo'] = lineItemNo;
data['BloodPressureDate'] = bloodPressureDate;
data['MeasuredArm'] = measuredArm;
data['SystolicePressure'] = systolicePressure;
data['DiastolicPressure'] = diastolicPressure;
data['Remark'] = remark;
data['IsActive'] = isActive;
data['ChartYear'] = chartYear;
data['ChartMonth'] = chartMonth;
data['YearSystolicePressureAverageResult'] = yearSystolicePressureAverageResult;
data['MonthSystolicePressureResult'] = monthSystolicePressureResult;
data['WeekSystolicePressureResult'] = weekSystolicePressureResult;
data['YearDiastolicPressureAverageResult'] = yearDiastolicPressureAverageResult;
data['MonthDiastolicPressureResult'] = monthDiastolicPressureResult;
data['WeekDiastolicPressureResult'] = weekDiastolicPressureResult;
data['MeasuredArmDesc'] = measuredArmDesc;
data['WeekDesc'] = weekDesc;
return data;
}
}

@ -0,0 +1,58 @@
class MonthBloodPressureResultAverage {
dynamic weekfourSystolicePressureAverageResult;
dynamic weekfourDiastolicPressureAverageResult;
dynamic weekthreeSystolicePressureAverageResult;
dynamic weekthreeDiastolicPressureAverageResult;
dynamic weektwoSystolicePressureAverageResult;
dynamic weektwoDiastolicPressureAverageResult;
dynamic weekoneSystolicePressureAverageResult;
dynamic weekoneDiastolicPressureAverageResult;
String? weekDesc;
int? weekDiastolicPressureAverageResult;
int? weekSystolicePressureAverageResult;
MonthBloodPressureResultAverage({
this.weekfourSystolicePressureAverageResult,
this.weekfourDiastolicPressureAverageResult,
this.weekthreeSystolicePressureAverageResult,
this.weekthreeDiastolicPressureAverageResult,
this.weektwoSystolicePressureAverageResult,
this.weektwoDiastolicPressureAverageResult,
this.weekoneSystolicePressureAverageResult,
this.weekoneDiastolicPressureAverageResult,
this.weekDesc,
this.weekDiastolicPressureAverageResult,
this.weekSystolicePressureAverageResult,
});
MonthBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
weekfourSystolicePressureAverageResult = json['weekfourSystolicePressureAverageResult'];
weekfourDiastolicPressureAverageResult = json['weekfourDiastolicPressureAverageResult'];
weekthreeSystolicePressureAverageResult = json['weekthreeSystolicePressureAverageResult'];
weekthreeDiastolicPressureAverageResult = json['weekthreeDiastolicPressureAverageResult'];
weektwoSystolicePressureAverageResult = json['weektwoSystolicePressureAverageResult'];
weektwoDiastolicPressureAverageResult = json['weektwoDiastolicPressureAverageResult'];
weekoneSystolicePressureAverageResult = json['weekoneSystolicePressureAverageResult'];
weekoneDiastolicPressureAverageResult = json['weekoneDiastolicPressureAverageResult'];
weekDesc = json['WeekDesc'];
weekDiastolicPressureAverageResult = json['WeekDiastolicPressureAverageResult'];
weekSystolicePressureAverageResult = json['WeekSystolicePressureAverageResult'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['weekfourSystolicePressureAverageResult'] = weekfourSystolicePressureAverageResult;
data['weekfourDiastolicPressureAverageResult'] = weekfourDiastolicPressureAverageResult;
data['weekthreeSystolicePressureAverageResult'] = weekthreeSystolicePressureAverageResult;
data['weekthreeDiastolicPressureAverageResult'] = weekthreeDiastolicPressureAverageResult;
data['weektwoSystolicePressureAverageResult'] = weektwoSystolicePressureAverageResult;
data['weektwoDiastolicPressureAverageResult'] = weektwoDiastolicPressureAverageResult;
data['weekoneSystolicePressureAverageResult'] = weekoneSystolicePressureAverageResult;
data['weekoneDiastolicPressureAverageResult'] = weekoneDiastolicPressureAverageResult;
data['WeekDesc'] = weekDesc;
data['WeekDiastolicPressureAverageResult'] = weekDiastolicPressureAverageResult;
data['WeekSystolicePressureAverageResult'] = weekSystolicePressureAverageResult;
return data;
}
}

@ -0,0 +1,24 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class WeekBloodPressureResultAverage {
int? dailySystolicePressureAverageResult;
int? dailyDiastolicPressureAverageResult;
DateTime? bloodPressureDate;
WeekBloodPressureResultAverage({this.dailySystolicePressureAverageResult, this.dailyDiastolicPressureAverageResult, this.bloodPressureDate});
WeekBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
dailySystolicePressureAverageResult = json['DailySystolicePressureAverageResult'];
dailyDiastolicPressureAverageResult = json['DailyDiastolicPressureAverageResult'];
bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['DailySystolicePressureAverageResult'] = dailySystolicePressureAverageResult;
data['DailyDiastolicPressureAverageResult'] = dailyDiastolicPressureAverageResult;
data['BloodPressureDate'] = bloodPressureDate;
return data;
}
}

@ -0,0 +1,37 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class YearBloodPressureResultAverage {
int? monthSystolicePressureAverageResult;
int? monthDiastolicPressureAverageResult;
dynamic monthNumber;
String? monthName;
String? yearName;
DateTime? date;
YearBloodPressureResultAverage({
this.monthSystolicePressureAverageResult,
this.monthDiastolicPressureAverageResult,
this.monthNumber,
this.monthName,
this.yearName,
});
YearBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
monthSystolicePressureAverageResult = json['monthSystolicePressureAverageResult'];
monthDiastolicPressureAverageResult = json['monthDiastolicPressureAverageResult'];
monthNumber = json['monthNumber'];
monthName = json['monthName'];
yearName = json['yearName'];
date = DateUtil.getMonthDateTime(monthName!, yearName);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['monthSystolicePressureAverageResult'] = monthSystolicePressureAverageResult;
data['monthDiastolicPressureAverageResult'] = monthDiastolicPressureAverageResult;
data['monthNumber'] = monthNumber;
data['monthName'] = monthName;
data['yearName'] = yearName;
return data;
}
}

@ -0,0 +1,99 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class DiabeticPatientResult {
String? chartMonth;
var chartYear;
DateTime? dateChart;
var description;
var descriptionN;
int? diabtecAvarage;
bool? isActive;
int? lineItemNo;
var listMonth;
var listWeek;
int? measured;
String? measuredDesc;
var monthAverageResult;
int? patientID;
var remark;
var resultDesc;
dynamic resultValue;
String? unit;
var weekAverageResult;
String? weekDesc;
var yearAverageResult;
DiabeticPatientResult(
{this.chartMonth,
this.chartYear,
this.dateChart,
this.description,
this.descriptionN,
this.diabtecAvarage,
this.isActive,
this.lineItemNo,
this.listMonth,
this.listWeek,
this.measured,
this.measuredDesc,
this.monthAverageResult,
this.patientID,
this.remark,
this.resultDesc,
this.resultValue,
this.unit,
this.weekAverageResult,
this.weekDesc,
this.yearAverageResult});
DiabeticPatientResult.fromJson(Map<String, dynamic> json) {
chartMonth = json['ChartMonth'];
chartYear = json['ChartYear'];
dateChart = DateUtil.convertStringToDate(json['DateChart']);
description = json['Description'];
descriptionN = json['DescriptionN'];
diabtecAvarage = json['DiabtecAvarage'];
isActive = json['IsActive'];
lineItemNo = json['LineItemNo'];
listMonth = json['List_Month'];
listWeek = json['List_Week'];
measured = json['Measured'];
measuredDesc = json['MeasuredDesc'];
monthAverageResult = json['MonthAverageResult'];
patientID = json['PatientID'];
remark = json['Remark'];
resultDesc = json['ResultDesc'];
resultValue = json['ResultValue'];
unit = json['Unit'];
weekAverageResult = json['WeekAverageResult'];
weekDesc = json['WeekDesc'];
yearAverageResult = json['YearAverageResult'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['ChartMonth'] = chartMonth;
data['ChartYear'] = chartYear;
data['DateChart'] = DateUtil.convertDateToString(dateChart!);
data['Description'] = description;
data['DescriptionN'] = descriptionN;
data['DiabtecAvarage'] = diabtecAvarage;
data['IsActive'] = isActive;
data['LineItemNo'] = lineItemNo;
data['List_Month'] = listMonth;
data['List_Week'] = listWeek;
data['Measured'] = measured;
data['MeasuredDesc'] = measuredDesc;
data['MonthAverageResult'] = monthAverageResult;
data['PatientID'] = patientID;
data['Remark'] = remark;
data['ResultDesc'] = resultDesc;
data['ResultValue'] = resultValue;
data['Unit'] = unit;
data['WeekAverageResult'] = weekAverageResult;
data['WeekDesc'] = weekDesc;
data['YearAverageResult'] = yearAverageResult;
return data;
}
}

@ -0,0 +1,37 @@
class MonthDiabeticResultAverage {
var weekfourAverageResult;
var weekthreeAverageResult;
var weektwoAverageResult;
var weekoneAverageResult;
dynamic weekAverageResult;
String? weekDesc;
MonthDiabeticResultAverage(
{this.weekfourAverageResult,
this.weekthreeAverageResult,
this.weektwoAverageResult,
this.weekoneAverageResult,
this.weekAverageResult,
this.weekDesc});
MonthDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
weekfourAverageResult = json['weekfourAverageResult'];
weekthreeAverageResult = json['weekthreeAverageResult'];
weektwoAverageResult = json['weektwoAverageResult'];
weekoneAverageResult = json['weekoneAverageResult'];
weekAverageResult = json['WeekAverageResult'];
weekDesc = json['WeekDesc'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['weekfourAverageResult'] = weekfourAverageResult;
data['weekthreeAverageResult'] = weekthreeAverageResult;
data['weektwoAverageResult'] = weektwoAverageResult;
data['weekoneAverageResult'] = weekoneAverageResult;
data['WeekAverageResult'] = weekAverageResult;
data['WeekDesc'] = weekDesc;
return data;
}
}

@ -0,0 +1,21 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class WeekDiabeticResultAverage {
dynamic dailyAverageResult;
DateTime? dateChart;
WeekDiabeticResultAverage({this.dailyAverageResult, this.dateChart});
WeekDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
dailyAverageResult = json['DailyAverageResult'];
dateChart = DateUtil.convertStringToDate(json['DateChart']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['DailyAverageResult'] = dailyAverageResult;
data['DateChart'] = DateUtil.convertDateToString(dateChart!);
return data;
}
}

@ -0,0 +1,35 @@
import 'dart:developer';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class YearDiabeticResultAverage {
dynamic monthAverageResult;
var monthNumber;
String? monthName;
String? yearName;
DateTime? date;
YearDiabeticResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
YearDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
try {
monthAverageResult = json['monthAverageResult'];
monthNumber = json['monthNumber'];
monthName = json['monthName'];
yearName = json['yearName'];
date = DateUtil.getMonthDateTime(monthName!, yearName);
} catch (e) {
log(e.toString());
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['monthAverageResult'] = monthAverageResult;
data['monthNumber'] = monthNumber;
data['monthName'] = monthName;
data['yearName'] = yearName;
return data;
}
}

@ -0,0 +1,37 @@
class MonthWeightMeasurementResultAverage {
dynamic weekfourAverageResult;
dynamic weekthreeAverageResult;
dynamic weektwoAverageResult;
dynamic weekoneAverageResult;
dynamic weekAverageResult;
String? weekDesc;
MonthWeightMeasurementResultAverage(
{this.weekfourAverageResult,
this.weekthreeAverageResult,
this.weektwoAverageResult,
this.weekoneAverageResult,
this.weekAverageResult,
this.weekDesc});
MonthWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
weekfourAverageResult = json['weekfourAverageResult'];
weekthreeAverageResult = json['weekthreeAverageResult'];
weektwoAverageResult = json['weektwoAverageResult'];
weekoneAverageResult = json['weekoneAverageResult'];
weekAverageResult = json['WeekAverageResult'];
weekDesc = json['WeekDesc'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['weekfourAverageResult'] = weekfourAverageResult;
data['weekthreeAverageResult'] = weekthreeAverageResult;
data['weektwoAverageResult'] = weektwoAverageResult;
data['weekoneAverageResult'] = weekoneAverageResult;
data['WeekAverageResult'] = weekAverageResult;
data['WeekDesc'] = weekDesc;
return data;
}
}

@ -0,0 +1,21 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class WeekWeightMeasurementResultAverage {
dynamic dailyAverageResult;
DateTime? weightDate;
WeekWeightMeasurementResultAverage({this.dailyAverageResult, this.weightDate});
WeekWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
dailyAverageResult = json['DailyAverageResult'];
weightDate = DateUtil.convertStringToDate(json['WeightDate']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['DailyAverageResult'] = dailyAverageResult;
data['WeightDate'] = weightDate;
return data;
}
}

@ -0,0 +1,77 @@
import 'dart:developer';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class WeightMeasurementResult {
int? patientID;
int? lineItemNo;
int? weightMeasured;
DateTime? weightDate;
dynamic remark;
bool? isActive;
int? measured;
dynamic unit;
int? chartYear;
dynamic chartMonth;
double? yearAverageResult;
dynamic monthAverageResult;
dynamic weekAverageResult;
dynamic weekDesc;
WeightMeasurementResult(
{this.patientID,
this.lineItemNo,
this.weightMeasured,
this.weightDate,
this.remark,
this.isActive,
this.measured,
this.unit,
this.chartYear,
this.chartMonth,
this.yearAverageResult,
this.monthAverageResult,
this.weekAverageResult,
this.weekDesc});
WeightMeasurementResult.fromJson(Map<String, dynamic> json) {
try {
patientID = json['PatientID'];
lineItemNo = json['LineItemNo'];
weightMeasured = json['WeightMeasured'];
weightDate = DateUtil.convertStringToDate(json['WeightDate']);
remark = json['Remark'];
isActive = json['IsActive'];
measured = json['Measured'];
unit = json['Unit'];
chartYear = json['ChartYear'];
chartMonth = json['ChartMonth'];
// Convert to double safely since API may return int
yearAverageResult = json['YearAverageResult'] != null ? (json['YearAverageResult'] as num).toDouble() : null;
monthAverageResult = json['MonthAverageResult'];
weekAverageResult = json['WeekAverageResult'];
weekDesc = json['WeekDesc'];
} catch (e) {
log(e.toString());
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['PatientID'] = patientID;
data['LineItemNo'] = lineItemNo;
data['WeightMeasured'] = weightMeasured;
data['WeightDate'] = weightDate;
data['Remark'] = remark;
data['IsActive'] = isActive;
data['Measured'] = measured;
data['Unit'] = unit;
data['ChartYear'] = chartYear;
data['ChartMonth'] = chartMonth;
data['YearAverageResult'] = yearAverageResult;
data['MonthAverageResult'] = monthAverageResult;
data['WeekAverageResult'] = weekAverageResult;
data['WeekDesc'] = weekDesc;
return data;
}
}

@ -0,0 +1,29 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class YearWeightMeasurementResultAverage {
dynamic monthAverageResult;
int? monthNumber;
String? monthName;
String? yearName;
DateTime? date;
YearWeightMeasurementResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
YearWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
monthAverageResult = json['monthAverageResult'];
monthNumber = json['monthNumber'];
monthName = json['monthName'];
yearName = json['yearName'];
date = DateUtil.getMonthDateTime(monthName!, yearName);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['monthAverageResult'] = monthAverageResult;
data['monthNumber'] = monthNumber;
data['monthName'] = monthName;
data['yearName'] = yearName;
return data;
}
}

@ -16,14 +16,19 @@ import 'package:hmg_patient_app_new/features/water_monitor/models/water_cup_mode
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_repo.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class WaterMonitorViewModel extends ChangeNotifier {
WaterMonitorRepo waterMonitorRepo;
ErrorHandlerService errorHandlerService;
WaterMonitorViewModel({required this.waterMonitorRepo});
WaterMonitorViewModel({
required this.waterMonitorRepo,
required this.errorHandlerService,
});
// Controllers
final TextEditingController nameController = TextEditingController();
@ -190,9 +195,9 @@ class WaterMonitorViewModel extends ChangeNotifier {
identificationNo: identification,
);
result.fold((failure) {
log('Error fetching user progress: ${failure.message}');
}, (apiModel) {
result.fold(
(failure) => errorHandlerService.handleError(failure: failure),
(apiModel) {
log("User Progress Data ($_selectedDuration): ${apiModel.data.toString()}");
// Parse the response based on progress type
@ -300,10 +305,13 @@ class WaterMonitorViewModel extends ChangeNotifier {
identificationNo: identification,
);
result.fold((failure) {
result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
_userDetailData = null;
if (onError != null) onError(failure.message);
}, (apiModel) {
},
(apiModel) {
_userDetailData = apiModel.data;
// Populate form fields from the fetched data
@ -322,7 +330,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
if (_userDetailData == null) {
try {
_navigationService.pushAndReplace(AppRoutes.waterMonitorSettingsScreen);
_navigationService.pushAndReplace(AppRoutes.waterMonitorSettingsPage);
} catch (navErr) {
log('Navigation to water monitor settings failed: $navErr');
}
@ -686,6 +694,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
_validationError = failure.message;
_isLoading = false;
notifyListeners();
@ -1060,6 +1069,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
log('Error inserting user activity: ${failure.message}');
_isLoading = false;
notifyListeners();
@ -1134,6 +1144,7 @@ class WaterMonitorViewModel extends ChangeNotifier {
return result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
log('Error undoing user activity: ${failure.message}');
_isLoading = false;
notifyListeners();

@ -35,6 +35,7 @@ import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_v
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -173,6 +174,9 @@ void main() async {
),
ChangeNotifierProvider<MyInvoicesViewModel>(
create: (_) => getIt.get<MyInvoicesViewModel>(),
),
ChangeNotifierProvider<HealthTrackersViewModel>(
create: (_) => getIt.get<HealthTrackersViewModel>(),
)
], child: MyApp()),
),

@ -1,4 +1,9 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.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/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/bf.dart';
@ -12,17 +17,12 @@ import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ibw.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ovulation.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/triglycerides.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.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/services/dialog_service.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/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class HealthCalculatorDetailedPage extends StatefulWidget {
HealthCalculatorsTypeEnum calculatorType;
@ -50,8 +50,8 @@ class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedP
widget.calculatorType == HealthCalculatorsTypeEnum.triglycerides
? SizedBox()
: Container(
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))),
padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h),
child: CustomButton(
text: widget.calculatorType == HealthCalculatorsTypeEnum.bloodSugar ||
@ -172,58 +172,69 @@ class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedP
switch (widget.calculatorType) {
case HealthCalculatorsTypeEnum.bmi:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BMIWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.calories:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: CaloriesWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.bmr:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BMRWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.idealBodyWeight:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: IdealBodyWeightWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.bodyFat:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BodyFatWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.crabsProteinFat:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: CrabsWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.ovulation:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: OvulationWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.deliveryDueDate:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: DeliveryDueDWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.bloodSugar:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BloodSugarWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.bloodCholesterol:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BloodCholesterolWidget(onChange: onCalculate),
);
case HealthCalculatorsTypeEnum.triglycerides:
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: TriglyceridesWidget(onChange: onCalculate),
);
}

@ -282,7 +282,12 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
}
// IBW
void calculateIBW({required String heightText, required String weightText, required String heightUnit, required String weightUnit, required String bodyFrameSize}) {
void calculateIBW(
{required String heightText,
required String weightText,
required String heightUnit,
required String weightUnit,
required String bodyFrameSize}) {
if (heightText.trim().isEmpty || weightText.trim().isEmpty) {
ibwResult = null;
weightDifference = null;
@ -609,7 +614,20 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
}
String _getMonthName(int month) {
const List<String> months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const List<String> months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
return months[month - 1];
}
@ -828,7 +846,8 @@ class HealthCalcualtorViewModel extends ChangeNotifier {
// expose map-like results for widgets to forward to parent
Map<String, dynamic>? get bmiResultMap => bmiResult == null ? null : {'bmiResult': bmiResult, 'bmiCategory': bmiCategory};
Map<String, dynamic>? get caloriesResultMap => caloriesResult == null ? null : {'calories': caloriesResult, 'bmr': bmrResult, 'calorieRange': calorieRange, 'bmrRange': bmrRange};
Map<String, dynamic>? get caloriesResultMap =>
caloriesResult == null ? null : {'calories': caloriesResult, 'bmr': bmrResult, 'calorieRange': calorieRange, 'bmrRange': bmrRange};
Map<String, dynamic>? get ibwResultMap => ibwResult == null ? null : {'ibw': ibwResult, 'difference': weightDifference, 'status': weightStatus};

@ -0,0 +1,567 @@
import 'dart:developer';
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/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.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/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/services/dialog_service.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/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class AddHealthTrackerEntryPage extends StatefulWidget {
final HealthTrackerTypeEnum trackerType;
const AddHealthTrackerEntryPage({
super.key,
required this.trackerType,
});
@override
State<AddHealthTrackerEntryPage> createState() => _AddHealthTrackerEntryPageState();
}
class _AddHealthTrackerEntryPageState extends State<AddHealthTrackerEntryPage> {
late DialogService dialogService;
// Controllers for date and time
final TextEditingController dateController = TextEditingController();
final TextEditingController timeController = TextEditingController();
@override
void initState() {
super.initState();
dialogService = getIt.get<DialogService>();
}
@override
void dispose() {
dateController.dispose();
timeController.dispose();
super.dispose();
}
/// Get page title based on tracker type
String _getPageTitle() {
switch (widget.trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
return "Add Blood Sugar".needTranslation;
case HealthTrackerTypeEnum.bloodPressure:
return "Add Blood Pressure".needTranslation;
case HealthTrackerTypeEnum.weightTracker:
return "Add Weight".needTranslation;
}
}
/// Get success message based on tracker type
String _getSuccessMessage() {
switch (widget.trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
return "Blood Sugar Data saved successfully".needTranslation;
case HealthTrackerTypeEnum.bloodPressure:
return "Blood Pressure Data saved successfully".needTranslation;
case HealthTrackerTypeEnum.weightTracker:
return "Weight Data saved successfully".needTranslation;
}
}
/// Save entry based on tracker type
Future<void> _saveEntry(HealthTrackersViewModel viewModel) async {
switch (widget.trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
await _saveBloodSugarEntry(viewModel);
break;
case HealthTrackerTypeEnum.bloodPressure:
await _saveBloodPressureEntry(viewModel);
break;
case HealthTrackerTypeEnum.weightTracker:
await _saveWeightEntry(viewModel);
break;
}
}
// Save Blood Sugar entry
Future<void> _saveBloodSugarEntry(HealthTrackersViewModel viewModel) async {
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
// Combine date and time
final dateTime = "${dateController.text} ${timeController.text}";
// Call ViewModel method with callbacks
await viewModel.saveBloodSugarEntry(
dateTime: dateTime,
measureTime: viewModel.selectedBloodSugarMeasureTime,
onSuccess: () {
LoaderBottomSheet.hideLoader();
_showSuccessAndPop();
},
onFailure: (error) {
LoaderBottomSheet.hideLoader();
dialogService.showErrorBottomSheet(message: error);
},
);
}
// Save Weight entry
Future<void> _saveWeightEntry(HealthTrackersViewModel viewModel) async {
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
// Combine date and time
final dateTime = "${dateController.text} ${timeController.text}";
// Call ViewModel method with callbacks
await viewModel.saveWeightEntry(
dateTime: dateTime,
onSuccess: () {
LoaderBottomSheet.hideLoader();
_showSuccessAndPop();
},
onFailure: (error) {
LoaderBottomSheet.hideLoader();
dialogService.showErrorBottomSheet(message: error);
},
);
}
// Save Blood Pressure entry
Future<void> _saveBloodPressureEntry(HealthTrackersViewModel viewModel) async {
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
// Combine date and time
final dateTime = "${dateController.text} ${timeController.text}";
// Call ViewModel method with callbacks
await viewModel.saveBloodPressureEntry(
dateTime: dateTime,
onSuccess: () {
LoaderBottomSheet.hideLoader();
_showSuccessAndPop();
},
onFailure: (error) {
LoaderBottomSheet.hideLoader();
dialogService.showErrorBottomSheet(message: error);
},
);
}
// Show success message and pop back
void _showSuccessAndPop() {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(
loadingText: _getSuccessMessage(),
),
callBackFunc: () {
Navigator.pop(context);
},
isCloseButtonVisible: false,
isDismissible: true,
isFullScreen: false,
);
}
// Reusable method to build selection row widget
Widget _buildSelectionRow({
required String value,
required String groupValue,
required VoidCallback onTap,
bool useUpperCase = false,
}) {
return SizedBox(
height: 70.h,
child: Row(
spacing: 8.h,
children: [
Radio<String>(
value: value,
groupValue: groupValue,
activeColor: AppColors.errorColor,
onChanged: (_) => onTap(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
(useUpperCase ? value.toUpperCase() : value.toCamelCase)
.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1)
.expanded,
],
).onPress(onTap),
);
}
// Reusable method to show selection bottom sheet
void _showSelectionBottomSheet({
required BuildContext context,
required String title,
required List<String> items,
required String selectedValue,
required Function(String) onSelected,
bool useUpperCase = false,
}) {
dialogService.showFamilyBottomSheetWithoutHWithChild(
label: title.needTranslation,
message: "",
child: Container(
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.7),
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
child: ListView.separated(
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return _buildSelectionRow(
value: item,
groupValue: selectedValue,
useUpperCase: useUpperCase,
onTap: () {
onSelected(item);
Navigator.pop(context);
},
);
},
separatorBuilder: (_, __) => Divider(height: 1, color: AppColors.dividerColor),
),
),
onOkPressed: () {},
);
}
// Blood Sugar unit selection
void _showBloodSugarUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
FocusScope.of(context).unfocus();
_showSelectionBottomSheet(
context: context,
title: "Select Unit".needTranslation,
items: viewModel.bloodSugarUnit,
selectedValue: viewModel.selectedBloodSugarUnit,
onSelected: viewModel.setBloodSugarUnit,
useUpperCase: false,
);
}
// Blood Sugar measure time selection
void _showBloodSugarEntryTimeBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
FocusScope.of(context).unfocus();
_showSelectionBottomSheet(
context: context,
title: "Select Measure Time".needTranslation,
items: viewModel.bloodSugarMeasureTimeEnList,
selectedValue: viewModel.selectedBloodSugarMeasureTime,
onSelected: viewModel.setBloodSugarMeasureTime,
useUpperCase: false,
);
}
// Weight unit selection
void _showWeightUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
FocusScope.of(context).unfocus();
_showSelectionBottomSheet(
context: context,
title: "Select Unit".needTranslation,
items: viewModel.weightUnits,
selectedValue: viewModel.selectedWeightUnit,
onSelected: viewModel.setWeightUnit,
useUpperCase: false,
);
}
// Blood Pressure measured arm selection
void _showMeasuredArmSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
FocusScope.of(context).unfocus();
_showSelectionBottomSheet(
context: context,
title: "Select Arm".needTranslation,
items: viewModel.measuredArmList,
selectedValue: viewModel.selectedMeasuredArm,
onSelected: viewModel.setMeasuredArm,
useUpperCase: false,
);
}
// Reusable method to build text field
Widget _buildTextField(TextEditingController controller, String hintText, {TextInputType keyboardType = TextInputType.name}) {
return TextField(
controller: controller,
keyboardType: keyboardType,
maxLines: 1,
cursorHeight: 14.h,
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isCollapsed: true,
hintText: hintText,
hintStyle: const TextStyle(color: Colors.grey),
),
style: TextStyle(
fontSize: 14.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
);
}
// Reusable method to build settings row
Widget _buildSettingsRow({
required String icon,
required String label,
String? value,
Widget? inputField,
String? unit,
VoidCallback? onUnitTap,
VoidCallback? onRowTap,
Color? iconColor,
bool showDivider = true,
}) {
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 40.w,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.greyColor,
borderRadius: 10.r,
hasShadow: false,
),
child: Center(child: Utils.buildSvgWithAssets(icon: icon, height: 22.w, width: 22.w, iconColor: iconColor)),
),
SizedBox(width: 12.w),
Expanded(
flex: unit != null ? 2 : 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
label.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
if (inputField != null)
Container(
height: 20.w,
alignment: Alignment.centerLeft,
child: inputField,
)
else if (value != null && value.isNotEmpty)
value.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
],
),
),
if (unit != null) ...[
Container(
width: 1.w,
height: 30.w,
color: AppColors.dividerColor,
).paddingOnly(right: 10.w),
Expanded(
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
unit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
],
),
SizedBox(width: 12.w),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
],
).onPress(onUnitTap ?? () {}),
),
] else if (onRowTap != null) ...[
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
SizedBox(width: 8.w),
],
],
).paddingSymmetrical(0.w, 16.w).onPress(onRowTap ?? () {}),
if (showDivider) Divider(height: 1, color: AppColors.dividerColor),
],
);
}
/// Build form fields based on tracker type
Widget _buildFormFields(HealthTrackersViewModel viewModel) {
switch (widget.trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
return _buildBloodSugarForm(viewModel);
case HealthTrackerTypeEnum.bloodPressure:
return _buildBloodPressureForm(viewModel);
case HealthTrackerTypeEnum.weightTracker:
return _buildWeightForm(viewModel);
}
}
/// Blood Sugar form fields
Widget _buildBloodSugarForm(HealthTrackersViewModel viewModel) {
return Column(
children: [
_buildSettingsRow(
icon: AppAssets.heightIcon,
label: "Enter Blood Sugar".needTranslation,
inputField: _buildTextField(viewModel.bloodSugarController, '', keyboardType: TextInputType.number),
unit: viewModel.selectedBloodSugarUnit,
onUnitTap: () => _showBloodSugarUnitSelectionBottomSheet(context, viewModel),
),
_buildDateTimeFields(),
Divider(height: 1, color: AppColors.dividerColor),
_buildSettingsRow(
icon: AppAssets.weight_tracker_icon,
label: "Select Measure Time".needTranslation,
value: viewModel.selectedBloodSugarMeasureTime,
onRowTap: () => _showBloodSugarEntryTimeBottomSheet(context, viewModel),
),
],
);
}
/// Blood Pressure form fields
Widget _buildBloodPressureForm(HealthTrackersViewModel viewModel) {
return Column(
children: [
_buildSettingsRow(
icon: AppAssets.bloodPressureIcon,
iconColor: AppColors.greyTextColor,
label: "Enter Systolic Value".needTranslation,
inputField: _buildTextField(viewModel.systolicController, '', keyboardType: TextInputType.number),
),
_buildSettingsRow(
icon: AppAssets.bloodPressureIcon,
iconColor: AppColors.greyTextColor,
label: "Enter Diastolic Value".needTranslation,
inputField: _buildTextField(viewModel.diastolicController, '', keyboardType: TextInputType.number),
),
_buildSettingsRow(
icon: AppAssets.bodyIcon,
iconColor: AppColors.greyTextColor,
label: "Select Arm".needTranslation,
value: viewModel.selectedMeasuredArm,
onRowTap: () => _showMeasuredArmSelectionBottomSheet(context, viewModel),
),
_buildDateTimeFields(),
],
);
}
/// Weight form fields
Widget _buildWeightForm(HealthTrackersViewModel viewModel) {
return Column(
children: [
_buildSettingsRow(
icon: AppAssets.weightScale,
label: "Enter Weight".needTranslation,
inputField: _buildTextField(viewModel.weightController, '', keyboardType: TextInputType.number),
unit: viewModel.selectedWeightUnit,
onUnitTap: () => _showWeightUnitSelectionBottomSheet(context, viewModel),
),
_buildDateTimeFields(),
],
);
}
/// Common date and time fields
Widget _buildDateTimeFields() {
return Column(
children: [
TextInputWidget(
controller: dateController,
isReadOnly: true,
isArrowTrailing: true,
labelText: "Date",
hintText: "Select date".needTranslation,
focusNode: FocusNode(),
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.calendarGrey,
selectionType: SelectionTypeEnum.calendar,
isHideSwitcher: true,
onCalendarTypeChanged: (val) {},
onChange: (val) {
if (val == null) return;
try {
final parsedDate = DateTime.parse(val);
final formattedDate = DateFormat('dd MMM yyyy').format(parsedDate);
dateController.text = formattedDate;
log("date: $formattedDate");
} catch (e) {
dateController.text = val;
log("date: $val");
}
},
),
TextInputWidget(
controller: timeController,
isReadOnly: true,
isArrowTrailing: true,
labelText: "Time",
hintText: "Select time".needTranslation,
focusNode: FocusNode(),
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h),
leadingIcon: AppAssets.calendarGrey,
selectionType: SelectionTypeEnum.time,
isHideSwitcher: true,
onCalendarTypeChanged: (val) {},
onChange: (val) {
if (val == null) return;
timeController.text = val;
log("time: $val");
},
),
],
);
}
@override
Widget build(BuildContext context) {
final viewModel = context.watch<HealthTrackersViewModel>();
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: _getPageTitle(),
bottomChild: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(24.w),
child: CustomButton(
text: "Save".needTranslation,
onPressed: () async => await _saveEntry(viewModel),
borderRadius: 12.r,
padding: EdgeInsets.symmetric(vertical: 14.h),
),
),
),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.h),
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
child: _buildFormFields(viewModel),
),
),
),
);
}
}

@ -0,0 +1,118 @@
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
class HealthTrackersPage extends StatefulWidget {
const HealthTrackersPage({super.key});
@override
State<HealthTrackersPage> createState() => _HealthTrackersPageState();
}
Widget buildHealthTrackerCard({
required String icon,
required String title,
required String description,
required Color iconBgColor,
required VoidCallback onTap,
}) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: iconBgColor, borderRadius: 10.r),
height: 40.w,
width: 40.w,
child: Utils.buildSvgWithAssets(
icon: icon,
fit: BoxFit.none,
height: 22.w,
width: 22.w,
),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
title.toText16(weight: FontWeight.w600),
description.toText12(
fontWeight: FontWeight.w500,
color: Color(0xFF8F9AA3),
),
],
),
),
SizedBox(width: 12.w),
Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight,
width: 24.w,
height: 24.h,
fit: BoxFit.contain,
iconColor: AppColors.textColor,
),
],
).paddingAll(16.w),
).onPress(onTap);
}
class _HealthTrackersPageState extends State<HealthTrackersPage> {
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Health Trackers".needTranslation,
child: Column(
children: [
buildHealthTrackerCard(
iconBgColor: AppColors.primaryRedColor,
icon: AppAssets.bloodSugarOnlyIcon,
title: "Blood Sugar".needTranslation,
description: "Track your glucose levels, understand trends, and get personalized insights for better health.".needTranslation,
onTap: () {
context.navigateWithName(
AppRoutes.healthTrackerDetailPage,
arguments: HealthTrackerTypeEnum.bloodSugar,
);
},
),
SizedBox(height: 16.h),
buildHealthTrackerCard(
iconBgColor: AppColors.infoColor,
icon: AppAssets.bloodPressureIcon,
title: "Blood Pressure".needTranslation,
description: "Monitor your blood pressure levels, track systolic and diastolic readings, and maintain a healthy heart.".needTranslation,
onTap: () {
context.navigateWithName(
AppRoutes.healthTrackerDetailPage,
arguments: HealthTrackerTypeEnum.bloodPressure,
);
},
),
SizedBox(height: 16.h),
buildHealthTrackerCard(
iconBgColor: AppColors.successColor,
icon: AppAssets.weightIcon,
title: "Weight".needTranslation,
description: "Track your weight progress, set goals, and maintain a healthy body mass for overall wellness.".needTranslation,
onTap: () {
context.navigateWithName(
AppRoutes.healthTrackerDetailPage,
arguments: HealthTrackerTypeEnum.weightTracker,
);
},
),
],
).paddingSymmetrical(20.w, 24.h),
);
}
}

@ -0,0 +1,271 @@
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_export.dart';
import 'package:hmg_patient_app_new/core/enums.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/presentation/health_trackers/health_trackers_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class TrackerLastValueCard extends StatelessWidget {
final HealthTrackerTypeEnum trackerType;
const TrackerLastValueCard({super.key, required this.trackerType});
/// Get status text and color based on blood sugar value
(String status, Color color, Color bgColor) _getBloodSugarStatus(double value) {
if (value < 70) {
return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
} else if (value <= 100) {
return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
} else if (value <= 125) {
return ('Pre-diabetic'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
} else {
return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
}
}
/// Get status text and color based on blood pressure value (systolic)
(String status, Color color, Color bgColor) _getBloodPressureStatus(int systolic) {
if (systolic < 90) {
return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
} else if (systolic <= 120) {
return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
} else if (systolic <= 140) {
return ('Elevated'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
} else {
return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
}
}
/// Get status for weight (neutral - no good/bad status)
(String status, Color color, Color bgColor) _getWeightStatus() {
return ('Recorded'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
}
/// Get default unit based on tracker type
String _getDefaultUnit() {
switch (trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
return 'mg/dL';
case HealthTrackerTypeEnum.bloodPressure:
return 'mmHg';
case HealthTrackerTypeEnum.weightTracker:
return 'kg';
}
}
@override
Widget build(BuildContext context) {
return Consumer<HealthTrackersViewModel>(
builder: (context, viewModel, child) {
// Get the last record based on tracker type
dynamic lastRecord;
String displayValue = '--';
String unit = _getDefaultUnit();
DateTime? lastDate;
String status = '';
Color statusColor = AppColors.greyTextColor;
switch (trackerType) {
case HealthTrackerTypeEnum.bloodSugar:
final allResults = [
...viewModel.weekDiabeticPatientResult,
...viewModel.monthDiabeticPatientResult,
...viewModel.yearDiabeticPatientResult,
];
if (allResults.isNotEmpty) {
allResults.sort((a, b) {
final dateA = a.dateChart ?? DateTime(1900);
final dateB = b.dateChart ?? DateTime(1900);
return dateB.compareTo(dateA);
});
lastRecord = allResults.first;
final lastValue = lastRecord.resultValue?.toDouble() ?? 0.0;
displayValue = lastValue.toStringAsFixed(0);
unit = lastRecord.unit ?? 'mg/dL';
lastDate = lastRecord.dateChart;
final (s, c, _) = _getBloodSugarStatus(lastValue);
status = s;
statusColor = c;
}
break;
case HealthTrackerTypeEnum.bloodPressure:
final allResults = [
...viewModel.weekBloodPressureResult,
...viewModel.monthBloodPressureResult,
...viewModel.yearBloodPressureResult,
];
if (allResults.isNotEmpty) {
allResults.sort((a, b) {
final dateA = a.bloodPressureDate ?? DateTime(1900);
final dateB = b.bloodPressureDate ?? DateTime(1900);
return dateB.compareTo(dateA);
});
lastRecord = allResults.first;
final systolic = lastRecord.systolicePressure ?? 0;
final diastolic = lastRecord.diastolicPressure ?? 0;
displayValue = '$systolic/$diastolic';
unit = 'mmHg';
lastDate = lastRecord.bloodPressureDate;
final (s, c, _) = _getBloodPressureStatus(systolic);
status = s;
statusColor = c;
}
break;
case HealthTrackerTypeEnum.weightTracker:
final allResults = [
...viewModel.weekWeightMeasurementResult,
...viewModel.monthWeightMeasurementResult,
...viewModel.yearWeightMeasurementResult,
];
if (allResults.isNotEmpty) {
allResults.sort((a, b) {
final dateA = a.weightDate ?? DateTime(1900);
final dateB = b.weightDate ?? DateTime(1900);
return dateB.compareTo(dateA);
});
lastRecord = allResults.first;
final weightValue = lastRecord.weightMeasured?.toDouble() ?? 0.0;
displayValue = weightValue.toStringAsFixed(0);
unit = lastRecord.unit ?? 'kg';
lastDate = lastRecord.weightDate;
final (s, c, _) = _getWeightStatus();
status = s;
statusColor = c;
}
break;
}
final formattedDate = lastDate != null ? DateFormat('EEE DD MMM, yy').format(lastDate) : DateFormat('EEE DD MMM, yy').format(DateTime.now());
// Show shimmer while loading
if (viewModel.isLoading) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 24.w),
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Shimmer.fromColors(
baseColor: AppColors.shimmerBaseColor,
highlightColor: AppColors.shimmerHighlightColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40.h,
width: 120.w,
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(8.r),
),
),
SizedBox(height: 8.h),
Row(
children: [
Container(
height: 32.h,
width: 150.w,
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(16.r),
),
),
SizedBox(width: 8.w),
Container(
height: 32.h,
width: 80.w,
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(16.r),
),
),
],
),
],
),
),
);
}
// Show empty state if no records
if (lastRecord == null) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 24.w),
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
"--".toText32(isBold: true, color: AppColors.greyTextColor),
SizedBox(width: 6.w),
unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
],
),
SizedBox(height: 8.h),
AppCustomChipWidget(
labelText: "No records yet".needTranslation,
icon: AppAssets.doctor_calendar_icon,
),
],
),
);
}
return Container(
margin: EdgeInsets.symmetric(horizontal: 24.w),
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
displayValue.toText32(isBold: true, color: statusColor),
SizedBox(width: 6.w),
unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
],
),
SizedBox(height: 8.h),
Row(
children: [
AppCustomChipWidget(
labelText: "${"Last Record".needTranslation}: $formattedDate",
icon: AppAssets.doctor_calendar_icon,
),
SizedBox(width: 8.w),
if (trackerType != HealthTrackerTypeEnum.weightTracker) ...[
AppCustomChipWidget(
labelText: status.needTranslation,
icon: AppAssets.normalStatusGreenIcon,
iconColor: statusColor,
),
]
],
),
],
),
);
},
);
}
}

@ -50,7 +50,8 @@ class ServicesPage extends StatelessWidget {
"".needTranslation,
AppAssets.emergency_services_icon,
bgColor: AppColors.primaryRedColor,
true, route: null, onTap: () {
true,
route: null, onTap: () {
getIt.get<EmergencyServicesViewModel>().flushData();
getIt.get<EmergencyServicesViewModel>().getTransportationOrders(
showLoader: false,
@ -162,6 +163,15 @@ class ServicesPage extends StatelessWidget {
];
late final List<HmgServicesComponentModel> hmgHealthToolServices = [
HmgServicesComponentModel(
11,
"Health Trackers".needTranslation,
"".needTranslation,
AppAssets.general_health,
bgColor: AppColors.whiteColor,
true,
route: AppRoutes.healthTrackersPage,
),
HmgServicesComponentModel(
11,
"Daily Water Monitor".needTranslation,
@ -179,14 +189,14 @@ class ServicesPage extends StatelessWidget {
LoaderBottomSheet.hideLoader();
if (userDetail == null) {
waterMonitorVM.populateFromAuthenticatedUser();
context.navigateWithName(AppRoutes.waterMonitorSettingsScreen);
context.navigateWithName(AppRoutes.waterMonitorSettingsPage);
} else {
context.navigateWithName(AppRoutes.waterConsumptionScreen);
context.navigateWithName(AppRoutes.waterConsumptionPage);
}
},
onError: (error) {
LoaderBottomSheet.hideLoader();
context.navigateWithName(AppRoutes.waterConsumptionScreen);
context.navigateWithName(AppRoutes.waterConsumptionPage);
},
);
},

@ -537,7 +537,7 @@ class _LandingPageState extends State<LandingPage> {
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
child: Padding(
padding: EdgeInsets.all(12.h),
padding: EdgeInsets.all(16.h),
child: Column(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),

@ -52,7 +52,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
password: password,
onSuccess: () {
LoaderBottomSheet.hideLoader();
context.navigateWithName(AppRoutes.symptomsSelectorScreen);
context.navigateWithName(AppRoutes.symptomsSelectorPage);
},
onError: (String error) {
LoaderBottomSheet.hideLoader();

@ -19,8 +19,8 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class PossibleConditionsScreen extends StatelessWidget {
const PossibleConditionsScreen({super.key});
class PossibleConditionsPage extends StatelessWidget {
const PossibleConditionsPage({super.key});
Widget _buildLoadingShimmer() {
return ListView.separated(

@ -41,7 +41,7 @@ class _RiskFactorsScreenState extends State<RiskFactorsScreen> {
void _onNextPressed(SymptomsCheckerViewModel viewModel) {
if (viewModel.hasSelectedRiskFactors) {
context.navigateWithName(AppRoutes.suggestionsScreen);
context.navigateWithName(AppRoutes.suggestionsPage);
} else {
dialogService.showErrorBottomSheet(
message: 'Please select at least one risk before proceeding'.needTranslation,

@ -43,7 +43,7 @@ class _SuggestionsScreenState extends State<SuggestionsScreen> {
void _onNextPressed(SymptomsCheckerViewModel viewModel) {
if (viewModel.hasSelectedSuggestions) {
// Navigate to triage screen
context.navigateWithName(AppRoutes.triageScreen);
context.navigateWithName(AppRoutes.triagePage);
} else {
dialogService.showErrorBottomSheet(
message: 'Please select at least one option before proceeding'.needTranslation,

@ -17,14 +17,14 @@ import 'package:hmg_patient_app_new/widgets/chip/custom_selectable_chip.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
class SymptomsSelectorScreen extends StatefulWidget {
const SymptomsSelectorScreen({super.key});
class SymptomsSelectorPage extends StatefulWidget {
const SymptomsSelectorPage({super.key});
@override
State<SymptomsSelectorScreen> createState() => _SymptomsSelectorScreenState();
State<SymptomsSelectorPage> createState() => _SymptomsSelectorPageState();
}
class _SymptomsSelectorScreenState extends State<SymptomsSelectorScreen> {
class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
late DialogService dialogService;
@override
@ -41,7 +41,7 @@ class _SymptomsSelectorScreenState extends State<SymptomsSelectorScreen> {
void _onNextPressed(SymptomsCheckerViewModel viewModel) {
if (viewModel.hasSelectedSymptoms) {
// Navigate to triage screen
context.navigateWithName(AppRoutes.riskFactorsScreen);
context.navigateWithName(AppRoutes.riskFactorsPage);
} else {
dialogService.showErrorBottomSheet(
message: 'Please select at least one symptom before proceeding'.needTranslation,

@ -21,14 +21,14 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
class TriageScreen extends StatefulWidget {
const TriageScreen({super.key});
class TriagePage extends StatefulWidget {
const TriagePage({super.key});
@override
State<TriageScreen> createState() => _TriageScreenState();
State<TriagePage> createState() => _TriagePageState();
}
class _TriageScreenState extends State<TriageScreen> {
class _TriagePageState extends State<TriagePage> {
late SymptomsCheckerViewModel viewModel;
late DialogService dialogService;
@ -78,7 +78,7 @@ class _TriageScreenState extends State<TriageScreen> {
// Case 2: Should stop flag is true OR Case 3: Probability >= 70% OR Case 4: 7 or more questions answered
if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 7) {
// Navigate to results/possible conditions screen
context.navigateWithName(AppRoutes.possibleConditionsScreen);
context.navigateWithName(AppRoutes.possibleConditionsPage);
return;
}

@ -3,6 +3,7 @@ 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/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -90,8 +91,8 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(weight: FontWeight.w500),
subTitle.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500),
title.toText14(weight: FontWeight.w500),
subTitle.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
@ -106,7 +107,7 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
Widget _getDivider() {
return Divider(
color: AppColors.dividerColor,
).paddingSymmetrical(0, 16.h);
).paddingSymmetrical(0, 8.h);
}
@override
@ -114,8 +115,25 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
AppState appState = getIt.get<AppState>();
String name = "";
int? userAgeFromDOB;
if (appState.isAuthenticated) {
name = "${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!} ";
final user = appState.getAuthenticatedUser();
name = "${user!.firstName!} ${user.lastName!} ";
// Calculate age from authenticated user's DOB if available
if (user.dateofBirth != null && user.dateofBirth!.isNotEmpty) {
try {
DateTime dob = DateUtil.convertStringToDate(user.dateofBirth!);
final now = DateTime.now();
int age = now.year - dob.year;
if (now.month < dob.month || (now.month == dob.month && now.day < dob.day)) {
age--;
}
userAgeFromDOB = age;
} catch (e) {
// If date parsing fails, ignore
}
}
} else {
name = "Guest";
}
@ -132,8 +150,9 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
// Get display values
String genderText = viewModel.selectedGender ?? "Not set";
// Show age calculated from DOB, not the DOB itself
String ageText = viewModel.selectedAge != null ? "${viewModel.selectedAge} Years" : "Not set";
// Show age calculated from DOB (prefer viewModel's age, fallback to calculated from user's DOB)
int? displayAge = viewModel.selectedAge ?? userAgeFromDOB;
String ageText = displayAge != null ? "$displayAge Years" : "Not set";
String heightText =
viewModel.selectedHeight != null ? "${viewModel.selectedHeight!.round()} ${viewModel.isHeightCm ? 'cm' : 'ft'}" : "Not set";
String weightText =
@ -154,11 +173,11 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
padding: EdgeInsets.symmetric(vertical: 24.h, horizontal: 16.w),
child: Column(
children: [
"Hello $name, Is your information up to date?".needTranslation.toText18(
"Hello $name, Is your information up to date?".needTranslation.toText16(
weight: FontWeight.w600,
color: AppColors.textColor,
),
SizedBox(height: 24.h),
SizedBox(height: 32.h),
_buildEditInfoTile(
context: context,
leadingIcon: AppAssets.genderIcon,
@ -240,13 +259,10 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
icon: AppAssets.edit_icon,
iconColor: AppColors.primaryRedColor,
onPressed: () {
context
.read<SymptomsCheckerViewModel>()
.setUserInfoPage(0, isSinglePageEdit: false);
context.read<SymptomsCheckerViewModel>().setUserInfoPage(0, isSinglePageEdit: false);
context.navigateWithName(AppRoutes.userInfoFlowManager);
},
backgroundColor:
AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,
@ -257,22 +273,13 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
child: CustomButton(
text: "Yes, It is".needTranslation,
icon: AppAssets.tickIcon,
iconColor: hasEmptyFields
? AppColors.greyTextColor
: AppColors.whiteColor,
iconColor: hasEmptyFields ? AppColors.greyTextColor : AppColors.whiteColor,
onPressed: hasEmptyFields
? () {} // Empty function for disabled state
: () => context
.navigateWithName(AppRoutes.organSelectorPage),
backgroundColor: hasEmptyFields
? AppColors.greyLightColor
: AppColors.primaryRedColor,
borderColor: hasEmptyFields
? AppColors.greyLightColor
: AppColors.primaryRedColor,
textColor: hasEmptyFields
? AppColors.greyTextColor
: AppColors.whiteColor,
: () => context.navigateWithName(AppRoutes.organSelectorPage),
backgroundColor: hasEmptyFields ? AppColors.greyLightColor : AppColors.primaryRedColor,
borderColor: hasEmptyFields ? AppColors.greyLightColor : AppColors.primaryRedColor,
textColor: hasEmptyFields ? AppColors.greyTextColor : AppColors.whiteColor,
fontSize: 16.f,
),
),

@ -21,14 +21,14 @@ import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class WaterConsumptionScreen extends StatefulWidget {
const WaterConsumptionScreen({super.key});
class WaterConsumptionPage extends StatefulWidget {
const WaterConsumptionPage({super.key});
@override
State<WaterConsumptionScreen> createState() => _WaterConsumptionScreenState();
State<WaterConsumptionPage> createState() => _WaterConsumptionPageState();
}
class _WaterConsumptionScreenState extends State<WaterConsumptionScreen> {
class _WaterConsumptionPageState extends State<WaterConsumptionPage> {
@override
void initState() {
super.initState();
@ -98,9 +98,29 @@ class _WaterConsumptionScreenState extends State<WaterConsumptionScreen> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"History".needTranslation.toText16(isBold: true),
Row(
children: [
"History".needTranslation.toText16(isBold: true),
SizedBox(width: 8.w),
InkWell(
onTap: () => _showHistoryDurationBottomsheet(context, viewModel),
child: Container(
padding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 6.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
backgroundColor: AppColors.greyColor,
borderRadius: 8.r,
hasShadow: true,
),
child: Row(
children: [
viewModel.selectedDurationFilter.toText12(fontWeight: FontWeight.w500),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 16.h),
],
),
),
)
],
),
InkWell(
onTap: () => viewModel.setGraphView(!viewModel.isGraphView),
child: AnimatedSwitcher(
@ -124,26 +144,6 @@ class _WaterConsumptionScreenState extends State<WaterConsumptionScreen> {
),
),
),
SizedBox(width: 8.w),
InkWell(
onTap: () => _showHistoryDurationBottomsheet(context, viewModel),
child: Container(
padding: EdgeInsets.symmetric(vertical: 6.h, horizontal: 6.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
backgroundColor: AppColors.greyColor,
borderRadius: 8.r,
hasShadow: true,
),
child: Row(
children: [
viewModel.selectedDurationFilter.toText12(fontWeight: FontWeight.w500),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
],
),
),
)
],
),
],
),
if (!viewModel.isGraphView) _buildHistoryListView(viewModel) else ...[SizedBox(height: 16.h), _buildHistoryGraph()]

@ -13,14 +13,14 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
class WaterMonitorSettingsScreen extends StatefulWidget {
const WaterMonitorSettingsScreen({super.key});
class WaterMonitorSettingsPage extends StatefulWidget {
const WaterMonitorSettingsPage({super.key});
@override
State<WaterMonitorSettingsScreen> createState() => _WaterMonitorSettingsScreenState();
State<WaterMonitorSettingsPage> createState() => _WaterMonitorSettingsPageState();
}
class _WaterMonitorSettingsScreenState extends State<WaterMonitorSettingsScreen> {
class _WaterMonitorSettingsPageState extends State<WaterMonitorSettingsPage> {
late DialogService dialogService;
@override
@ -68,7 +68,6 @@ class _WaterMonitorSettingsScreenState extends State<WaterMonitorSettingsScreen>
required Function(String) onSelected,
bool useUpperCase = false,
}) {
dialogService.showFamilyBottomSheetWithoutHWithChild(
label: title.needTranslation,
message: "",

@ -94,7 +94,7 @@ class WaterActionButtonsWidget extends StatelessWidget {
),
_buildActionButton(
context: context,
onTap: () => context.navigateWithName(AppRoutes.waterMonitorSettingsScreen),
onTap: () => context.navigateWithName(AppRoutes.waterMonitorSettingsPage),
title: "Settings".needTranslation,
icon: Icon(
Icons.settings,

@ -6,8 +6,12 @@ import 'package:hmg_patient_app_new/presentation/authentication/register_step2.d
import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart';
import 'package:hmg_patient_app_new/presentation/covid19test/covid19_landing_page.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculators_page.dart';
import 'package:hmg_patient_app_new/presentation/health_trackers/add_health_tracker_entry_page.dart';
import 'package:hmg_patient_app_new/presentation/health_trackers/health_tracker_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
@ -23,12 +27,10 @@ import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_sele
import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart';
import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
import 'package:hmg_patient_app_new/presentation/vital_sign/vital_sign_page.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_consumption_screen.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_monitor_settings_screen.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_consumption_page.dart';
import 'package:hmg_patient_app_new/presentation/water_monitor/water_monitor_settings_page.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
import '../presentation/covid19test/covid19_landing_page.dart';
class AppRoutes {
static const String initialRoute = '/initialRoute';
static const String loginScreen = '/loginScreen';
@ -47,25 +49,29 @@ class AppRoutes {
static const String huaweiHealthExample = '/huaweiHealthExample';
static const String covid19Test = '/covid19Test';
static const String vitalSign = '/vitalSign';
//appointments
static const String bookAppointmentPage = '/bookAppointmentPage';
// Water Monitor
static const String waterConsumptionScreen = '/waterConsumptionScreen';
static const String waterMonitorSettingsScreen = '/waterMonitorSettingsScreen';
static const String waterConsumptionPage = '/waterConsumptionScreen';
static const String waterMonitorSettingsPage = '/waterMonitorSettingsScreen';
// Symptoms Checker
static const String organSelectorPage = '/organSelectorPage';
static const String symptomsSelectorScreen = '/symptomsCheckerScreen';
static const String suggestionsScreen = '/suggestionsScreen';
static const String riskFactorsScreen = '/riskFactorsScreen';
static const String possibleConditionsScreen = '/possibleConditionsScreen';
static const String triageScreen = '/triageProgressScreen';
//UserInfoSelection
static const String symptomsSelectorPage = '/symptomsCheckerScreen';
static const String suggestionsPage = '/suggestionsScreen';
static const String riskFactorsPage = '/riskFactorsScreen';
static const String possibleConditionsPage = '/possibleConditionsScreen';
static const String triagePage = '/triageProgressScreen';
static const String userInfoSelection = '/userInfoSelection';
static const String userInfoFlowManager = '/userInfoFlowManager';
// Health Trackers
static const String healthTrackersPage = '/healthTrackersListScreen';
static const String addHealthTrackerEntryPage = '/addHealthTrackerEntryPage';
static const String healthTrackerDetailPage = '/healthTrackerDetailPage';
static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(),
loginScreen: (context) => LoginScreen(),
@ -78,11 +84,11 @@ class AppRoutes {
comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(),
homeHealthCarePage: (context) => HhcProceduresPage(),
organSelectorPage: (context) => OrganSelectorPage(),
symptomsSelectorScreen: (context) => SymptomsSelectorScreen(),
riskFactorsScreen: (context) => RiskFactorsScreen(),
suggestionsScreen: (context) => SuggestionsScreen(),
possibleConditionsScreen: (context) => PossibleConditionsScreen(),
triageScreen: (context) => TriageScreen(),
symptomsSelectorPage: (context) => SymptomsSelectorPage(),
riskFactorsPage: (context) => RiskFactorsScreen(),
suggestionsPage: (context) => SuggestionsScreen(),
possibleConditionsPage: (context) => PossibleConditionsPage(),
triagePage: (context) => TriagePage(),
bloodDonationPage: (context) => BloodDonationPage(),
bookAppointmentPage: (context) => BookAppointmentPage(),
userInfoSelection: (context) => UserInfoSelectionScreen(),
@ -90,11 +96,23 @@ class AppRoutes {
smartWatches: (context) => SmartwatchInstructionsPage(),
huaweiHealthExample: (context) => HuaweiHealthExample(),
covid19Test: (context) => Covid19LandingPage(),
//
waterConsumptionScreen: (context) => WaterConsumptionScreen(),
waterMonitorSettingsScreen: (context) => WaterMonitorSettingsScreen(),
waterConsumptionPage: (context) => WaterConsumptionPage(),
waterMonitorSettingsPage: (context) => WaterMonitorSettingsPage(),
healthCalculatorsPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.calculator),
healthConvertersPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.converter),
vitalSign: (context) => VitalSignPage()
healthTrackersPage: (context) => HealthTrackersPage(),
vitalSign: (context) => VitalSignPage(),
addHealthTrackerEntryPage: (context) {
final args = ModalRoute.of(context)?.settings.arguments as HealthTrackerTypeEnum?;
return AddHealthTrackerEntryPage(
trackerType: args ?? HealthTrackerTypeEnum.bloodSugar,
);
},
healthTrackerDetailPage: (context) {
final args = ModalRoute.of(context)?.settings.arguments as HealthTrackerTypeEnum?;
return HealthTrackerDetailPage(
trackerType: args ?? HealthTrackerTypeEnum.bloodSugar,
);
},
};
}

@ -2,111 +2,112 @@ import 'package:flutter/material.dart';
class AppColors {
static const transparent = Colors.transparent;
static const mainPurple = Color(0xFF7954F7);
static const mainPurple = Color(0xFF7954F7); // #7954F7
static const scaffoldBgColor = Color(0xFFF8F8F8);
static const bottomSheetBgColor = Color(0xFFF8F8FA);
static const lightGreyEFColor = Color(0xffeaeaff);
static const greyF7Color = Color(0xffF7F7F7);
static const greyInfoTextColor = Color(0xff777777);
static const lightGrayColor = Color(0xff808080);
static const greyTextColorLight = Color(0xFFA2A2A2);
static const scaffoldBgColor = Color(0xFFF8F8F8); // #F8F8F8
static const bottomSheetBgColor = Color(0xFFF8F8FA); // #F8F8FA
static const lightGreyEFColor = Color(0xffeaeaff); // #EAEAFF
static const greyF7Color = Color(0xffF7F7F7); // #F7F7F7
static const greyInfoTextColor = Color(0xff777777); // #777777
static const lightGrayColor = Color(0xff808080); // #808080
static const greyTextColorLight = Color(0xFFA2A2A2); // #A2A2A2
// New UI Colors
static const whiteColor = Color(0xFFffffff);
static const Color bgScaffoldColor = Color(0xffF8F8F8);
static const Color primaryRedColor = Color(0xFFED1C2B);
static const Color primaryRedBorderColor = Color(0xFFED1C2B);
static const Color secondaryLightRedColor = Color(0xFFFEE9EA);
static const Color secondaryLightRedBorderColor = Color(0xFFFEE9EA);
static const Color bgRedLightColor = Color(0xFFFEE9EA);
static const Color bgGreenColor = Color(0xFF18C273);
static const Color textColor = Color(0xFF2E3039);
static const Color borderGrayColor = Color(0x332E3039);
static const Color textColorLight = Color(0xFF5E5E5E);
static const Color borderOnlyColor = Color(0xFF2E3039);
static const Color chipBorderColorOpacity20 = Color(0x332E3039);
static const Color dividerColor = Color(0x40D2D2D2);
static const Color warningColorYellow = Color(0xFFF4A308);
static const Color blackBgColor = Color(0xFF2E3039);
static const blackColor = textColor;
static const Color inputLabelTextColor = Color(0xff898A8D);
static const Color greyTextColor = Color(0xFF8F9AA3);
static const Color lightGrayBGColor = Color(0x142E3039);
static const Color checkBoxBorderColor = Color(0xffD2D2D2);
static const Color pharmacyBGColor = Color(0xFF359846);
static const lightGreenColor = Color(0xFF0ccedde);
static const textGreenColor = Color(0xFF18C273);
static const Color ratingColorYellow = Color(0xFFFFAF15);
static const Color spacerLineColor = Color(0x2E30391A);
static const whiteColor = Color(0xFFffffff); // #FFFFFF
static const Color bgScaffoldColor = Color(0xffF8F8F8); // #F8F8F8
static const Color primaryRedColor = Color(0xFFED1C2B); // #ED1C2B
static const Color primaryRedBorderColor = Color(0xFFED1C2B); // #ED1C2B
static const Color secondaryLightRedColor = Color(0xFFFEE9EA); // #FEE9EA
static const Color secondaryLightRedBorderColor = Color(0xFFFEE9EA); // #FEE9EA
static const Color bgRedLightColor = Color(0xFFFEE9EA); // #FEE9EA
static const Color bgGreenColor = Color(0xFF18C273); // #18C273
static const Color textColor = Color(0xFF2E3039); // #2E3039
static const Color borderGrayColor = Color(0x332E3039); // #2E3039 (20% opacity)
static const Color textColorLight = Color(0xFF5E5E5E); // #5E5E5E
static const Color borderOnlyColor = Color(0xFF2E3039); // #2E3039
static const Color chipBorderColorOpacity20 = Color(0x332E3039); // #2E3039 (20% opacity)
static const Color dividerColor = Color(0x40D2D2D2); // #D2D2D2 (25% opacity)
static const Color warningColorYellow = Color(0xFFF4A308); // #F4A308
static const Color blackBgColor = Color(0xFF2E3039); // #2E3039
static const blackColor = textColor; // #2E3039
static const Color inputLabelTextColor = Color(0xff898A8D); // #898A8D
static const Color greyTextColor = Color(0xFF8F9AA3); // #8F9AA3
static const Color lightGrayBGColor = Color(0x142E3039); // #2E3039 (8% opacity)
static const Color checkBoxBorderColor = Color(0xffD2D2D2); // #D2D2D2
static const Color pharmacyBGColor = Color(0xFF359846); // #359846
static const lightGreenColor = Color(0xFF0ccedde); // #0CCEDDE
static const textGreenColor = Color(0xFF18C273); // #18C273
static const Color ratingColorYellow = Color(0xFFFFAF15); // #FFAF15
static const Color spacerLineColor = Color(0x2E30391A); // #2E3039 (10% opacity)
//Chips
static const Color successColor = Color(0xff18C273);
static const Color successLightBgColor = Color(0xffDDF6EA);
static const Color errorColor = Color(0xFFED1C2B);
static const Color alertColor = Color(0xFFD48D05);
static const Color infoColor = Color(0xFF0B85F7);
static const Color warningColor = Color(0xFFFFCC00);
static const Color greyColor = Color(0xFFEFEFF0);
static const Color chipPrimaryRedBorderColor = Color(0xFFED1C2B);
static const Color chipSecondaryLightRedColor = Color(0xFFFEE9EA);
static const Color successLightColor = Color(0xFF18C273);
static const Color errorLightColor = Color(0xFFED1C2B);
static const Color alertLightColor = Color(0xFFD48D05);
static const Color infoLightColor = Color(0xFF0B85F7);
static const Color warningLightColor = Color(0xFFFFCC00);
static const Color greyLightColor = Color(0xFFEFEFF0);
static const Color thumbColor = Color(0xFF18C273);
static const Color switchBackgroundColor = Color(0x2618C273);
static const Color bottomNAVBorder = Color(0xFFEEEEEE);
static const Color quickLoginColor = Color(0xFF666666);
static const Color tooltipTextColor = Color(0xFF414D55);
static const Color graphGridColor = Color(0x4D18C273);
static const Color criticalLowAndHigh = Color(0xFFED1C2B);
static const Color highAndLow = Color(0xFFFFAF15);
static const Color labelTextColor = Color(0xFF838383);
static const Color calenderTextColor = Color(0xFFD0D0D0);
static const Color lightGreenButtonColor = Color(0x2618C273);
static const Color lightRedButtonColor = Color(0x1AED1C2B);
static const Color successColor = Color(0xff18C273); // #18C273
static const Color successLightBgColor = Color(0xffDDF6EA); // #DDF6EA
static const Color errorColor = Color(0xFFED1C2B); // #ED1C2B
static const Color alertColor = Color(0xFFD48D05); // #D48D05
static const Color infoColor = Color(0xFF0B85F7); // #0B85F7
static const Color warningColor = Color(0xFFFFCC00); // #FFCC00
static const Color greyColor = Color(0xFFEFEFF0); // #EFEFF0
static const Color chipPrimaryRedBorderColor = Color(0xFFED1C2B); // #ED1C2B
static const Color chipSecondaryLightRedColor = Color(0xFFFEE9EA); // #FEE9EA
// static const Color chipSecondaryLightRedColor = Color(0xFFFF9E15); // #FEE9EA
static const Color successLightColor = Color(0xFF18C273); // #18C273
static const Color errorLightColor = Color(0xFFED1C2B); // #ED1C2B
static const Color alertLightColor = Color(0xFFD48D05); // #D48D05
static const Color infoLightColor = Color(0xFF0B85F7); // #0B85F7
static const Color warningLightColor = Color(0xFFFFCC00); // #FFCC00
static const Color greyLightColor = Color(0xFFEFEFF0); // #EFEFF0
static const Color thumbColor = Color(0xFF18C273); // #18C273
static const Color switchBackgroundColor = Color(0x2618C273); // #18C273 (15% opacity)
static const Color bottomNAVBorder = Color(0xFFEEEEEE); // #EEEEEE
static const Color quickLoginColor = Color(0xFF666666); // #666666
static const Color tooltipTextColor = Color(0xFF414D55); // #414D55
static const Color graphGridColor = Color(0x4D18C273); // #18C273 (30% opacity)
static const Color criticalLowAndHigh = Color(0xFFED1C2B); // #ED1C2B
static const Color highAndLow = Color(0xFFFFAF15); // #FFAF15
static const Color labelTextColor = Color(0xFF838383); // #838383
static const Color calenderTextColor = Color(0xFFD0D0D0); // #D0D0D0
static const Color lightGreenButtonColor = Color(0x2618C273); // #18C273 (15% opacity)
static const Color lightRedButtonColor = Color(0x1AED1C2B); // #ED1C2B (10% opacity)
// Status Colors
static const Color statusPendingColor = Color(0xffCC9B14);
static const Color statusProcessingColor = Color(0xff2E303A);
static const Color statusCompletedColor = Color(0xff359846);
static const Color statusRejectedColor = Color(0xffD02127);
static const Color statusPendingColor = Color(0xffCC9B14); // #CC9B14
static const Color statusProcessingColor = Color(0xff2E303A); // #2E303A
static const Color statusCompletedColor = Color(0xff359846); // #359846
static const Color statusRejectedColor = Color(0xffD02127); // #D02127
// Info Banner Colors
static const Color infoBannerBgColor = Color(0xFFFFF4E6);
static const Color infoBannerBorderColor = Color(0xFFFFE5B4);
static const Color infoBannerIconColor = Color(0xFFCC9B14);
static const Color infoBannerTextColor = Color(0xFF856404);
static const Color infoBannerBgColor = Color(0xFFFFF4E6); // #FFF4E6
static const Color infoBannerBorderColor = Color(0xFFFFE5B4); // #FFE5B4
static const Color infoBannerIconColor = Color(0xFFCC9B14); // #CC9B14
static const Color infoBannerTextColor = Color(0xFF856404); // #856404
// SymptomsChecker
static const Color chipColorSeekMedicalAdvice = Color(0xFFFFAF15);
static const Color chipTextColorSeekMedicalAdvice = Color(0xFFAB7103);
static const Color chipColorMonitor = Color(0xFF18C273);
static const Color chipColorEmergency = Color(0xFFED1C2B);
static const Color chipColorSeekMedicalAdvice = Color(0xFFFFAF15); // #FFAF15
static const Color chipTextColorSeekMedicalAdvice = Color(0xFFAB7103); // #AB7103
static const Color chipColorMonitor = Color(0xFF18C273); // #18C273
static const Color chipColorEmergency = Color(0xFFED1C2B); // #ED1C2B
// Services Page Colors
static const Color eReferralCardColor = Color(0xFFFF8012);
static const Color bloodDonationCardColor = Color(0xFFFF5662);
static const Color bookAppointment = Color(0xFF415364);
static const Color eReferralCardColor = Color(0xFFFF8012); // #FF8012
static const Color bloodDonationCardColor = Color(0xFFFF5662); // #FF5662
static const Color bookAppointment = Color(0xFF415364); // #415364
// Water Monitor
static const Color blueColor = Color(0xFF4EB5FF);
static const Color blueGradientColorOne = Color(0xFFF1F7FD);
static const Color blueGradientColorTwo = Color(0xFFD9EFFF);
static const Color blueColor = Color(0xFF4EB5FF); // #4EB5FF
static const Color blueGradientColorOne = Color(0xFFF1F7FD); // #F1F7FD
static const Color blueGradientColorTwo = Color(0xFFD9EFFF); // #D9EFFF
// Shimmer
static const Color shimmerBaseColor = Color(0xFFE0E0E0);
static const Color shimmerHighlightColor = Color(0xFFF5F5F5);
static const Color covid29Color = Color(0xff2563EB);
static const Color shimmerBaseColor = Color(0xFFE0E0E0); // #E0E0E0
static const Color shimmerHighlightColor = Color(0xFFF5F5F5); // #F5F5F5
static const Color covid29Color = Color(0xff2563EB); // #2563EB
}

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
///
/// **Parameters:**
/// - [dataPoints]: List of `DataPoint` objects to plot.
/// - [secondaryDataPoints]: Optional list for a second line (e.g., diastolic in blood pressure).
/// - [leftLabelFormatter]: Function to build left axis labels.
/// - [bottomLabelFormatter]: Function to build bottom axis labels.
/// - [width]: Optional width of the chart.
@ -18,6 +19,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
/// - [maxY], [maxX], [minX]: Axis bounds.
/// - [spotColor]: Color of the touched spot marker.
/// - [graphColor]: Color of the line.
/// - [secondaryGraphColor]: Color of the secondary line.
/// - [graphShadowColor]: Color of the area below the line.
/// - [graphGridColor]: Color of the grid lines.
/// - [bottomLabelColor]: Color of bottom axis labels.
@ -43,6 +45,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
/// )
class CustomGraph extends StatelessWidget {
final List<DataPoint> dataPoints;
final List<DataPoint>? secondaryDataPoints; // For dual-line graphs (e.g., blood pressure)
final double? width;
final double height;
final double? maxY;
@ -50,6 +53,7 @@ class CustomGraph extends StatelessWidget {
final double? minX;
final Color spotColor;
final Color graphColor;
final Color? secondaryGraphColor; // Color for secondary line
final Color graphShadowColor;
final Color graphGridColor;
final Color bottomLabelColor;
@ -79,6 +83,7 @@ class CustomGraph extends StatelessWidget {
const CustomGraph(
{super.key,
required this.dataPoints,
this.secondaryDataPoints,
required this.leftLabelFormatter,
this.width,
required this.scrollDirection,
@ -89,6 +94,7 @@ class CustomGraph extends StatelessWidget {
this.isFullScreeGraph = false,
this.spotColor = AppColors.bgGreenColor,
this.graphColor = AppColors.bgGreenColor,
this.secondaryGraphColor,
this.graphShadowColor = AppColors.graphGridColor,
this.graphGridColor = AppColors.graphGridColor,
this.bottomLabelColor = AppColors.textColor,
@ -225,7 +231,7 @@ class CustomGraph extends StatelessWidget {
return FlSpot(entry.key.toDouble(), value);
}).toList();
var data = [
var data = <LineChartBarData>[
LineChartBarData(
spots: allSpots,
isCurved: true,
@ -254,6 +260,31 @@ class CustomGraph extends StatelessWidget {
)
];
// Add secondary line if provided (for dual-line graphs like blood pressure)
if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) {
final List<FlSpot> secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) {
double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
return FlSpot(entry.key.toDouble(), value);
}).toList();
data.add(
LineChartBarData(
spots: secondarySpots,
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
barWidth: 2,
gradient: LinearGradient(
colors: [secondaryGraphColor ?? AppColors.blueColor, secondaryGraphColor ?? AppColors.blueColor],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
dotData: FlDotData(show: showLinePoints),
belowBarData: BarAreaData(show: false),
),
);
}
return data;
}
}

@ -3,17 +3,14 @@ import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.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/enums.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
import '../core/dependencies.dart';
// TODO: Import AppColors if bgRedColor is defined there
// import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart';
import 'package:hmg_patient_app_new/widgets/time_picker_widget.dart';
class TextInputWidget extends StatelessWidget {
final String labelText;
@ -49,6 +46,7 @@ class TextInputWidget extends StatelessWidget {
final int maxLines;
final Color? hintColor;
final bool? isHideSwitcher;
final bool? isArrowTrailing;
// final List<Country> countryList;
// final Function(Country)? onCountryChange;
@ -87,6 +85,7 @@ class TextInputWidget extends StatelessWidget {
this.minLines = 3,
this.maxLines = 6,
this.isHideSwitcher,
this.isArrowTrailing,
// this.countryList = const [],
// this.onCountryChange,
});
@ -167,7 +166,8 @@ class TextInputWidget extends StatelessWidget {
],
),
),
if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context),
if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context, isArrowTrailing: isArrowTrailing ?? false),
if (selectionType == SelectionTypeEnum.time) _buildTimePickerIcon(context, isArrowTrailing: isArrowTrailing ?? false),
if (selectionType == SelectionTypeEnum.search) _buildTrailingIconForSearch(context),
],
),
@ -200,7 +200,7 @@ class TextInputWidget extends StatelessWidget {
child: Utils.buildSvgWithAssets(icon: leadingIcon!));
}
Widget _buildTrailingIcon(BuildContext context) {
Widget _buildTrailingIcon(BuildContext context, {bool isArrowTrailing = false}) {
final AppState appState = getIt.get<AppState>();
return Container(
height: 40.h,
@ -233,7 +233,47 @@ class TextInputWidget extends StatelessWidget {
onChange!(picked.toString());
}
},
child: Utils.buildSvgWithAssets(icon: AppAssets.calendar),
child: Utils.buildSvgWithAssets(icon: isArrowTrailing ? AppAssets.arrow_down : AppAssets.calendar),
),
);
}
Widget _buildTimePickerIcon(BuildContext context, {bool isArrowTrailing = false}) {
return Container(
height: 40.h,
width: 40.h,
margin: EdgeInsets.zero,
padding: EdgeInsets.all(8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 12.r,
color: AppColors.whiteColor,
),
child: GestureDetector(
onTap: () async {
// Parse existing time if available
TimeOfDay? initialTime;
if (controller?.text.isNotEmpty ?? false) {
initialTime = TimePickerWidget.parseTime(controller!.text);
}
final picked = await TimePickerWidget.show(
context,
initialTime: initialTime,
use24HourFormat: false, // You can make this configurable if needed
onTimeSelected: (time) {
if (onChange != null) {
final formattedTime = TimePickerWidget.formatTime(time);
onChange!(formattedTime);
}
},
);
// Update controller if time was picked
if (picked != null && controller != null) {
controller!.text = TimePickerWidget.formatTime(picked);
}
},
child: Utils.buildSvgWithAssets(icon: isArrowTrailing ? AppAssets.arrow_down : AppAssets.alarm_clock_icon),
),
);
}

@ -0,0 +1,348 @@
import 'package:flutter/cupertino.dart';
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/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
/// A reusable time picker widget that can be used anywhere in the app
/// Shows a bottom sheet with iOS-style time picker
class TimePickerWidget {
/// Shows a time picker bottom sheet
///
/// [context] - BuildContext for showing the bottom sheet
/// [initialTime] - Initial time to display (defaults to current time)
/// [use24HourFormat] - Whether to use 24-hour format (defaults to false)
/// [onTimeSelected] - Callback when time is selected
///
/// Returns the selected TimeOfDay or null if cancelled
static Future<TimeOfDay?> show(
BuildContext context, {
TimeOfDay? initialTime,
bool use24HourFormat = false,
bool displaySelectedTime = false,
Function(TimeOfDay)? onTimeSelected,
}) async {
final selectedTime = initialTime ?? TimeOfDay.now();
final result = await showModalBottomSheet<TimeOfDay>(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (BuildContext context) {
return _TimePickerBottomSheet(
initialTime: selectedTime,
use24HourFormat: use24HourFormat,
displaySelectedTime: displaySelectedTime,
onTimeChanged: (time) {
// Time is being changed in real-time
},
);
},
);
if (result != null && onTimeSelected != null) {
onTimeSelected(result);
}
return result;
}
/// Formats TimeOfDay to string (HH:mm format)
static String formatTime(TimeOfDay time, {bool use24HourFormat = false}) {
if (use24HourFormat) {
return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
} else {
final hour = time.hourOfPeriod == 0 ? 12 : time.hourOfPeriod;
final period = time.period == DayPeriod.am ? 'AM' : 'PM';
return '${hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')} $period';
}
}
/// Parses time string to TimeOfDay
static TimeOfDay? parseTime(String timeString) {
try {
final parts = timeString.split(':');
if (parts.length == 2) {
final hour = int.parse(parts[0]);
final minute = int.parse(parts[1].split(' ')[0]);
return TimeOfDay(hour: hour, minute: minute);
}
} catch (e) {
return null;
}
return null;
}
}
class _TimePickerBottomSheet extends StatefulWidget {
final TimeOfDay initialTime;
final bool use24HourFormat;
final bool displaySelectedTime;
final Function(TimeOfDay) onTimeChanged;
const _TimePickerBottomSheet({
required this.initialTime,
required this.use24HourFormat,
required this.displaySelectedTime,
required this.onTimeChanged,
});
@override
State<_TimePickerBottomSheet> createState() => _TimePickerBottomSheetState();
}
class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> {
late int selectedHour;
late int selectedMinute;
late DayPeriod selectedPeriod;
@override
void initState() {
super.initState();
selectedHour = widget.use24HourFormat ? widget.initialTime.hour : widget.initialTime.hourOfPeriod;
if (selectedHour == 0 && !widget.use24HourFormat) selectedHour = 12;
selectedMinute = widget.initialTime.minute;
selectedPeriod = widget.initialTime.period;
}
TimeOfDay _getCurrentTime() {
if (widget.use24HourFormat) {
return TimeOfDay(hour: selectedHour, minute: selectedMinute);
} else {
int hour = selectedHour;
if (selectedPeriod == DayPeriod.pm && hour != 12) {
hour += 12;
} else if (selectedPeriod == DayPeriod.am && hour == 12) {
hour = 0;
}
return TimeOfDay(hour: hour, minute: selectedMinute);
}
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.r),
topRight: Radius.circular(20.r),
),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header
Container(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppColors.dividerColor,
width: 1,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Select Time".needTranslation.toText18(
weight: FontWeight.w600,
color: AppColors.textColor,
),
GestureDetector(
onTap: () => Navigator.pop(context),
child: Utils.buildSvgWithAssets(
icon: AppAssets.cancel,
width: 24.h,
height: 24.h,
iconColor: AppColors.textColor,
),
),
],
),
),
// Time Picker
SizedBox(
height: 250.h,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Hour Picker
Expanded(
child: CupertinoPicker(
scrollController: FixedExtentScrollController(
initialItem: widget.use24HourFormat ? selectedHour : (selectedHour - 1),
),
itemExtent: 50.h,
onSelectedItemChanged: (index) {
setState(() {
if (widget.use24HourFormat) {
selectedHour = index;
} else {
selectedHour = index + 1;
}
widget.onTimeChanged(_getCurrentTime());
});
},
children: List.generate(
widget.use24HourFormat ? 24 : 12,
(index) {
final hour = widget.use24HourFormat ? index : index + 1;
return Center(
child: Text(
hour.toString().padLeft(2, '0'),
style: TextStyle(
fontSize: 24.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
),
);
},
),
),
),
// Separator
Text(
':',
style: TextStyle(
fontSize: 24.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
),
// Minute Picker
Expanded(
child: CupertinoPicker(
scrollController: FixedExtentScrollController(
initialItem: selectedMinute,
),
itemExtent: 50.h,
onSelectedItemChanged: (index) {
setState(() {
selectedMinute = index;
widget.onTimeChanged(_getCurrentTime());
});
},
children: List.generate(
60,
(index) => Center(
child: Text(
index.toString().padLeft(2, '0'),
style: TextStyle(
fontSize: 24.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
),
),
),
),
),
// AM/PM Picker (only for 12-hour format)
if (!widget.use24HourFormat)
Expanded(
child: CupertinoPicker(
scrollController: FixedExtentScrollController(
initialItem: selectedPeriod == DayPeriod.am ? 0 : 1,
),
itemExtent: 50.h,
onSelectedItemChanged: (index) {
setState(() {
selectedPeriod = index == 0 ? DayPeriod.am : DayPeriod.pm;
widget.onTimeChanged(_getCurrentTime());
});
},
children: [
Center(
child: Text(
'AM',
style: TextStyle(
fontSize: 24.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
),
),
Center(
child: Text(
'PM',
style: TextStyle(
fontSize: 24.f,
fontWeight: FontWeight.w500,
color: AppColors.textColor,
),
),
),
],
),
),
],
),
),
if (widget.displaySelectedTime)
// Current Time Display
Container(
margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
padding: EdgeInsets.symmetric(vertical: 12.h),
decoration: BoxDecoration(
color: AppColors.lightGrayBGColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Center(
child: TimePickerWidget.formatTime(
_getCurrentTime(),
use24HourFormat: widget.use24HourFormat,
).toText20(
weight: FontWeight.w600,
color: AppColors.textColor,
),
),
),
// Action Buttons
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: "Cancel".needTranslation,
onPressed: () => Navigator.pop(context),
textColor: AppColors.textColor,
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
),
),
SizedBox(width: 12.w),
Expanded(
child: CustomButton(
height: 56.h,
text: "Confirm".needTranslation,
onPressed: () {
Navigator.pop(context, _getCurrentTime());
},
textColor: AppColors.whiteColor,
backgroundColor: AppColors.primaryRedColor,
),
),
],
),
),
],
),
),
);
}
}

@ -0,0 +1,165 @@
// Example usage of TimePickerWidget
//
// This file demonstrates how to use the TimePickerWidget in your Flutter app.
// The TimePickerWidget is a reusable component that shows a bottom sheet with
// an iOS-style time picker.
// ============================================================================
// EXAMPLE 1: Using with TextInputWidget
// ============================================================================
/*
TextInputWidget(
labelText: "Appointment Time",
hintText: "Select time",
controller: timeController,
selectionType: SelectionTypeEnum.time,
isReadOnly: true,
onChange: (value) {
print("Selected time: $value");
},
)
*/
// ============================================================================
// EXAMPLE 2: Direct usage with custom button
// ============================================================================
/*
ElevatedButton(
onPressed: () async {
final selectedTime = await TimePickerWidget.show(
context,
initialTime: TimeOfDay.now(),
use24HourFormat: false,
onTimeSelected: (time) {
print("Time selected: ${TimePickerWidget.formatTime(time)}");
},
);
if (selectedTime != null) {
print("Final time: ${TimePickerWidget.formatTime(selectedTime)}");
}
},
child: Text("Pick Time"),
)
*/
// ============================================================================
// EXAMPLE 3: Using with 24-hour format
// ============================================================================
/*
TextInputWidget(
labelText: "Meeting Time",
hintText: "Select time (24h)",
controller: timeController,
selectionType: SelectionTypeEnum.time,
isReadOnly: true,
onChange: (value) {
// The value will be formatted as "14:30" for 2:30 PM in 24h format
print("Selected time (24h): $value");
},
)
// Or programmatically:
final time = await TimePickerWidget.show(
context,
use24HourFormat: true, // Enable 24-hour format
);
*/
// ============================================================================
// EXAMPLE 4: Parsing and formatting times
// ============================================================================
/*
// Parse time string to TimeOfDay
String timeString = "02:30 PM";
TimeOfDay? parsedTime = TimePickerWidget.parseTime(timeString);
// Format TimeOfDay to string
TimeOfDay time = TimeOfDay(hour: 14, minute: 30);
String formatted12h = TimePickerWidget.formatTime(time); // "02:30 PM"
String formatted24h = TimePickerWidget.formatTime(time, use24HourFormat: true); // "14:30"
*/
// ============================================================================
// EXAMPLE 5: Complete form example with date and time
// ============================================================================
/*
class AppointmentForm extends StatefulWidget {
@override
_AppointmentFormState createState() => _AppointmentFormState();
}
class _AppointmentFormState extends State<AppointmentForm> {
final TextEditingController dateController = TextEditingController();
final TextEditingController timeController = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
// Date picker
TextInputWidget(
labelText: "Appointment Date",
hintText: "Select date",
controller: dateController,
selectionType: SelectionTypeEnum.calendar,
isReadOnly: true,
onChange: (value) {
print("Date selected: $value");
},
),
SizedBox(height: 16),
// Time picker
TextInputWidget(
labelText: "Appointment Time",
hintText: "Select time",
controller: timeController,
selectionType: SelectionTypeEnum.time,
isReadOnly: true,
onChange: (value) {
print("Time selected: $value");
},
),
],
);
}
}
*/
// ============================================================================
// Features:
// ============================================================================
// iOS-style cupertino picker (works on both iOS and Android)
// Support for 12-hour format (with AM/PM)
// Support for 24-hour format
// Beautiful bottom sheet UI matching app design
// Real-time preview of selected time
// Confirm/Cancel buttons
// Easy integration with TextInputWidget
// Parse and format time utilities
// Fully customizable and reusable
// ============================================================================
// API Reference:
// ============================================================================
// TimePickerWidget.show() - Shows the time picker bottom sheet
// Parameters:
// - context: BuildContext (required)
// - initialTime: TimeOfDay? (optional, defaults to current time)
// - use24HourFormat: bool (optional, defaults to false)
// - onTimeSelected: Function(TimeOfDay)? (optional callback)
// Returns: Future<TimeOfDay?>
//
// TimePickerWidget.formatTime() - Formats TimeOfDay to string
// Parameters:
// - time: TimeOfDay (required)
// - use24HourFormat: bool (optional, defaults to false)
// Returns: String (e.g., "02:30 PM" or "14:30")
//
// TimePickerWidget.parseTime() - Parses time string to TimeOfDay
// Parameters:
// - timeString: String (required, e.g., "02:30 PM")
// Returns: TimeOfDay?
Loading…
Cancel
Save