Completed the health Tracker Module, Only the email part is left.
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 |
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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…
Reference in New Issue