Merge pull request 'Lab result view along with the listing of items is added' (#61) from feature/lab_result into master

Reviewed-on: #61
pull/66/head
Haroon6138 1 month ago
commit eaee656a74

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 17.0625C4.5472 17.0625 0.9375 13.4528 0.9375 9C0.9375 4.5472 4.5472 0.9375 9 0.9375C13.4528 0.9375 17.0625 4.5472 17.0625 9C17.0625 13.4528 13.4528 17.0625 9 17.0625ZM9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V9.37499H9.87151C10.0893 9.37492 10.3198 9.37484 10.5016 9.39662C10.6342 9.41249 11.1221 9.47231 11.3472 9.92903C11.5725 10.3861 11.3138 10.7931 11.2436 10.903C11.1469 11.0544 11.0021 11.2289 10.8651 11.3939L10.8416 11.4223C10.626 11.6824 10.3693 11.9784 10.1118 12.2139C9.98322 12.3315 9.83449 12.4526 9.67303 12.5484C9.52334 12.6371 9.28695 12.75 9 12.75C8.71306 12.75 8.47666 12.6371 8.32697 12.5484C8.16551 12.4526 8.01679 12.3315 7.88821 12.2139C7.63072 11.9784 7.37397 11.6824 7.15837 11.4223L7.13486 11.3939C6.99791 11.2289 6.85315 11.0544 6.75644 10.903C6.68621 10.7931 6.42751 10.3861 6.65279 9.92903C6.87788 9.47231 7.36577 9.41249 7.49837 9.39662C7.68024 9.37484 7.9107 9.37492 8.1285 9.37499H8.25L8.25 6C8.25 5.58579 8.58579 5.25 9 5.25Z" fill="#F43333"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 2C3.55229 2 4 2.44772 4 3L4 14C4 15.6782 4.00213 16.8362 4.11923 17.7072C4.17092 18.0917 4.23383 18.5232 4.45131 18.8563C4.60954 18.5142 4.76255 18.1696 4.91553 17.8252C5.29664 16.967 5.67762 16.1092 6.13888 15.2891C6.58328 14.499 7.11953 13.7058 7.77477 13.1011C8.43871 12.4882 9.28219 12.0189 10.3058 12.0189C11.6975 12.0189 12.5705 12.9129 13.1308 13.4867L13.1883 13.5455C13.8415 14.2124 14.1538 14.4717 14.6132 14.4717C15.0859 14.4717 15.4115 14.2943 15.733 13.9424C16.1205 13.5181 16.396 12.9986 16.6658 12.49C16.7134 12.4003 16.7608 12.3109 16.8086 12.2224C17.5056 10.9328 18.5501 9 20.9995 9C21.5518 9 21.9995 9.44772 21.9995 10C21.9995 10.5523 21.5518 11 20.9995 11C19.8601 11 19.3296 11.7648 18.5475 13.2114L18.4738 13.348C18.1403 13.9674 17.7401 14.7105 17.2097 15.2911C16.5941 15.9651 15.7622 16.4717 14.6132 16.4717C13.2476 16.4717 12.3831 15.5847 11.8309 15.0182C11.7865 14.9725 11.7412 14.925 11.6952 14.8766C11.3159 14.4783 10.8784 14.0189 10.3058 14.0189C9.93769 14.0189 9.55589 14.1788 9.13128 14.5707C8.69797 14.9707 8.28242 15.5577 7.88206 16.2696C7.45308 17.0322 7.09983 17.8308 6.7465 18.6295C6.56362 19.043 6.38072 19.4565 6.18729 19.865C6.22215 19.8708 6.25731 19.876 6.2928 19.8808C7.16378 19.9979 8.32182 20 10 20H21C21.5523 20 22 20.4477 22 21C22 21.5523 21.5523 22 21 22H9.928C8.33933 22 7.04616 22.0001 6.0263 21.8629C4.96232 21.7199 4.04736 21.4113 3.31802 20.682C2.58869 19.9526 2.28011 19.0377 2.13706 17.9737C1.99995 16.9539 1.99997 15.6607 2 14.0721L2 3C2 2.44772 2.44772 2 3 2Z" fill="#2E3039"/>
<path d="M8 4C8.55229 4 9 3.55228 9 3C9 2.44772 8.55229 2 8 2L7 2C6.44772 2 6 2.44772 6 3C6 3.55228 6.44772 4 7 4L8 4Z" fill="#2E3039"/>
<path d="M11 8C11.5523 8 12 7.55228 12 7C12 6.44772 11.5523 6 11 6H7C6.44772 6 6 6.44772 6 7C6 7.55228 6.44772 8 7 8L11 8Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 13C10 12.4477 10.4477 12 11 12L16 12C16.5523 12 17 12.4477 17 13C17 13.5523 16.5523 14 16 14L11 14C10.4477 14 10 13.5523 10 13Z" fill="#2E3039"/>
<path d="M7 13C7 12.4477 7.44772 12 8 12H8.00898C8.56127 12 9.00898 12.4477 9.00898 13C9.00898 13.5523 8.56127 14 8.00898 14H8C7.44772 14 7 13.5523 7 13Z" fill="#2E3039"/>
<path d="M7 17C7 16.4477 7.44772 16 8 16L13 16C13.5523 16 14 16.4477 14 17C14 17.5523 13.5523 18 13 18L8 18C7.44772 18 7 17.5523 7 17Z" fill="#2E3039"/>
<path d="M14.991 17C14.991 16.4477 15.4387 16 15.991 16H16C16.5523 16 17 16.4477 17 17C17 17.5523 16.5523 18 16 18H15.991C15.4387 18 14.991 17.5523 14.991 17Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2C7 1.44772 6.55229 1 6 1C5.44772 1 5 1.44772 5 2V2.44885C4.23882 2.6903 3.57734 3.06994 3.01802 3.6746C2.18949 4.57031 1.83279 5.69272 1.66416 7.04866C1.49997 8.36894 1.49998 10.0541 1.5 12.1739L1.5 12.8261C1.49998 14.9459 1.49997 16.6311 1.66416 17.9513C1.83279 19.3073 2.18949 20.4297 3.01802 21.3254C3.8568 22.2322 4.92535 22.6329 6.21533 22.8204C7.45142 23.0001 9.02292 23 10.9712 23H13.0288C14.977 23 16.5486 23.0001 17.7847 22.8204C19.0747 22.6329 20.1432 22.2322 20.982 21.3254C21.8105 20.4297 22.1672 19.3073 22.3358 17.9513C22.5 16.6311 22.5 14.9459 22.5 12.8261V12.1739C22.5 10.0541 22.5 8.36895 22.3358 7.04866C22.1672 5.69272 21.8105 4.57031 20.982 3.6746C20.4227 3.06993 19.7612 2.6903 19 2.44885V2C19 1.44772 18.5523 1 18 1C17.4477 1 17 1.44772 17 2V2.09173C15.903 1.99995 14.5863 1.99998 13.0288 2L10.9712 2C9.41375 1.99998 8.09705 1.99995 7 2.09173V2ZM3.66385 9.14417C3.8099 9 4.03921 9 4.49783 9L19.5022 9C19.9608 9 20.1901 9 20.3362 9.14417C20.4822 9.28833 20.4851 9.51472 20.491 9.96751C20.4997 10.6407 20.5 11.3942 20.5 12.2432V12.7568C20.5 14.9616 20.4982 16.5221 20.3511 17.7045C20.2067 18.8656 19.9374 19.5094 19.5138 19.9673C19.1004 20.4142 18.5353 20.6903 17.497 20.8412C16.4214 20.9975 14.9957 21 12.95 21H11.05C9.00425 21 7.57858 20.9975 6.503 20.8412C5.4647 20.6903 4.89956 20.4142 4.48622 19.9673C4.06263 19.5094 3.79327 18.8656 3.64887 17.7045C3.50182 16.5221 3.5 14.9616 3.5 12.7568V12.2432C3.5 11.3942 3.50027 10.6407 3.509 9.96751C3.51487 9.51472 3.51781 9.28833 3.66385 9.14417Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4C2.44772 4 2 4.44772 2 5C2 5.55228 2.44772 6 3 6L5 6C5.55228 6 6 5.55229 6 5C6 4.44772 5.55228 4 5 4L3 4Z" fill="#2E3039"/>
<path d="M9 4C8.44772 4 8 4.44772 8 5C8 5.55228 8.44772 6 9 6L21 6C21.5523 6 22 5.55229 22 5C22 4.44772 21.5523 4 21 4L9 4Z" fill="#2E3039"/>
<path d="M8 12C8 11.4477 8.44772 11 9 11L21 11C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13L9 13C8.44772 13 8 12.5523 8 12Z" fill="#2E3039"/>
<path d="M3 11C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H5C5.55228 13 6 12.5523 6 12C6 11.4477 5.55228 11 5 11L3 11Z" fill="#2E3039"/>
<path d="M8 19C8 18.4477 8.44772 18 9 18L21 18C21.5523 18 22 18.4477 22 19C22 19.5523 21.5523 20 21 20L9 20C8.44772 20 8 19.5523 8 19Z" fill="#2E3039"/>
<path d="M3 18C2.44772 18 2 18.4477 2 19C2 19.5523 2.44772 20 3 20H5C5.55228 20 6 19.5523 6 19C6 18.4477 5.55228 18 5 18H3Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 975 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 17.0625C4.5472 17.0625 0.9375 13.4528 0.9375 9C0.9375 4.5472 4.5472 0.9375 9 0.9375C13.4528 0.9375 17.0625 4.5472 17.0625 9C17.0625 13.4528 13.4528 17.0625 9 17.0625ZM9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V9.37499H9.87151C10.0893 9.37492 10.3198 9.37484 10.5016 9.39662C10.6342 9.41249 11.1221 9.47231 11.3472 9.92903C11.5725 10.3861 11.3138 10.7931 11.2436 10.903C11.1469 11.0544 11.0021 11.2289 10.8651 11.3939L10.8416 11.4223C10.626 11.6824 10.3693 11.9784 10.1118 12.2139C9.98322 12.3315 9.83449 12.4526 9.67303 12.5484C9.52334 12.6371 9.28695 12.75 9 12.75C8.71306 12.75 8.47666 12.6371 8.32697 12.5484C8.16551 12.4526 8.01679 12.3315 7.88821 12.2139C7.63072 11.9784 7.37397 11.6824 7.15837 11.4223L7.13486 11.3939C6.99791 11.2289 6.85315 11.0544 6.75644 10.903C6.68621 10.7931 6.42751 10.3861 6.65279 9.92903C6.87788 9.47231 7.36577 9.41249 7.49837 9.39662C7.68024 9.37484 7.9107 9.37492 8.1285 9.37499H8.25L8.25 6C8.25 5.58579 8.58579 5.25 9 5.25Z" fill="#FF9E15"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.9375 9C0.9375 13.4528 4.54725 17.0625 9 17.0625C13.4528 17.0625 17.0625 13.4528 17.0625 9C17.0625 4.54725 13.4528 0.9375 9 0.9375C4.54725 0.9375 0.9375 4.54725 0.9375 9ZM12.507 6.19724C12.8123 6.47699 12.8325 6.95173 12.5528 7.25698L8.42776 11.757C8.28976 11.9077 8.0955 11.9955 7.8915 12C7.68675 12.0045 7.4895 11.925 7.34475 11.7803L5.46975 9.90527C5.1765 9.61277 5.1765 9.13723 5.46975 8.84473C5.76225 8.55148 6.23776 8.55148 6.53026 8.84473L7.85175 10.1655L11.4472 6.24302C11.727 5.93777 12.2018 5.91749 12.507 6.19724Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 701 B

@ -0,0 +1,8 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 10.75C6.30964 10.75 5.75 11.3096 5.75 12C5.75 12.6904 6.30964 13.25 7 13.25H7.00897C7.69933 13.25 8.25897 12.6904 8.25897 12C8.25897 11.3096 7.69933 10.75 7.00897 10.75H7Z" fill="#2E3039"/>
<path d="M10.9955 10.75C10.3052 10.75 9.74551 11.3096 9.74551 12C9.74551 12.6904 10.3052 13.25 10.9955 13.25H11.0045C11.6948 13.25 12.2545 12.6904 12.2545 12C12.2545 11.3096 11.6948 10.75 11.0045 10.75H10.9955Z" fill="#2E3039"/>
<path d="M14.991 10.75C14.3007 10.75 13.741 11.3096 13.741 12C13.741 12.6904 14.3007 13.25 14.991 13.25H15C15.6904 13.25 16.25 12.6904 16.25 12C16.25 11.3096 15.6904 10.75 15 10.75H14.991Z" fill="#2E3039"/>
<path d="M7 14.75C6.30964 14.75 5.75 15.3096 5.75 16C5.75 16.6904 6.30964 17.25 7 17.25H7.00897C7.69933 17.25 8.25897 16.6904 8.25897 16C8.25897 15.3096 7.69933 14.75 7.00897 14.75H7Z" fill="#2E3039"/>
<path d="M10.9955 14.75C10.3052 14.75 9.74551 15.3096 9.74551 16C9.74551 16.6904 10.3052 17.25 10.9955 17.25H11.0045C11.6948 17.25 12.2545 16.6904 12.2545 16C12.2545 15.3096 11.6948 14.75 11.0045 14.75H10.9955Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 1C6 0.447715 5.55229 0 5 0C4.44772 0 4 0.447715 4 1V1.44885C3.23882 1.6903 2.57734 2.06994 2.01802 2.6746C1.18949 3.57031 0.83279 4.69272 0.664161 6.04866C0.499967 7.36894 0.499982 9.05406 0.5 11.1739L0.5 11.8261C0.499982 13.9459 0.499967 15.6311 0.664161 16.9513C0.83279 18.3073 1.18949 19.4297 2.01802 20.3254C2.8568 21.2322 3.92535 21.6329 5.21533 21.8204C6.45141 22.0001 8.02291 22 9.97119 22H12.0288C13.977 22 15.5486 22.0001 16.7847 21.8204C18.0747 21.6329 19.1432 21.2322 19.982 20.3254C20.8105 19.4297 21.1672 18.3073 21.3358 16.9513C21.5 15.6311 21.5 13.9459 21.5 11.8261V11.1739C21.5 9.05408 21.5 7.36895 21.3358 6.04866C21.1672 4.69272 20.8105 3.57031 19.982 2.6746C19.4227 2.06993 18.7612 1.6903 18 1.44885V1C18 0.447715 17.5523 0 17 0C16.4477 0 16 0.447715 16 1V1.09173C14.903 0.999955 13.5863 0.999976 12.0288 1L9.97122 1C8.41376 0.999976 7.09704 0.999955 6 1.09173V1ZM3.49783 8C3.03921 8 2.8099 8 2.66385 8.14417C2.51781 8.28833 2.51487 8.51472 2.509 8.96751C2.50027 9.64067 2.5 10.3942 2.5 11.2432V11.7568C2.5 13.9616 2.50182 15.5221 2.64887 16.7045C2.79327 17.8656 3.06263 18.5094 3.48622 18.9673C3.89956 19.4142 4.4647 19.6903 5.503 19.8412C6.57858 19.9975 8.00425 20 10.05 20H11.95C13.9957 20 15.4214 19.9975 16.497 19.8412C17.5353 19.6903 18.1004 19.4142 18.5138 18.9673C18.9374 18.5094 19.2067 17.8656 19.3511 16.7045C19.4982 15.5221 19.5 13.9616 19.5 11.7568V11.2432C19.5 10.3942 19.4997 9.64067 19.491 8.96751C19.4851 8.51472 19.4822 8.28833 19.3362 8.14417C19.1901 8 18.9608 8 18.5022 8L3.49783 8Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,7 @@
<svg width="21" height="24" viewBox="0 0 21 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.5" width="21" height="3" rx="1.5" fill="#18C273"/>
<rect y="5.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.7"/>
<rect y="10.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.5"/>
<rect y="15.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.35"/>
<rect y="20.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 493 B

@ -7,6 +7,7 @@ class AppAssets {
static const String arrow_forward = '$svgBasePath/arrow_forward.svg';
static const String externalLink = '$svgBasePath/external_link.svg';
static const String calendar = '$svgBasePath/calendar.svg';
static const String rangeCalendar = '$svgBasePath/range_calender.svg';
static const String hmc = '$svgBasePath/hmc.svg';
static const String ksa = '$svgBasePath/ksa.svg';
static const String sms = '$svgBasePath/sms.svg';
@ -133,6 +134,13 @@ class AppAssets {
static const String minus = '$svgBasePath/minus.svg';
static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg';
static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg';
static const String lab_result_indicator = '$svgBasePath/refernce_range_indicator.svg';
static const String ic_date_filter = '$svgBasePath/ic_date_filter.svg';
static const String ic_list = '$svgBasePath/ic_list.svg';
static const String ic_graph = '$svgBasePath/graph.svg';
static const String ic_normal_result = '$svgBasePath/normal_result.svg';
static const String ic_low_result = '$svgBasePath/low_result.svg';
static const String ic_critical_low_result = '$svgBasePath/critical_low_result.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';

@ -6,9 +6,22 @@ class DataPoint {
final double value;
///label shown on the bottom of the graph
String label;
String refernceValue;
String actualValue;
DateTime time;
String displayTime;
DataPoint(
{required this.value,
required this.label,
required this.refernceValue,
required this.actualValue,
required this.time,
required this.displayTime,
});
@override
String toString() {
return "the time is $time";
}
}

@ -100,6 +100,7 @@ class AppDependencies {
() => LabViewModel(
labRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt()
),
);

@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
class LabHistoryViewModel extends ChangeNotifier{
bool isGraphShowing = false;
}

@ -0,0 +1,93 @@
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/lab/models/Range.dart';
class LabRangeViewModel extends ChangeNotifier {
List months = [
'Jan',
'Feb',
'Mar',
'April',
'May',
'Jun',
'July',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
];
bool isGraphVisible = true;
Range? _currentlySelectedRange;
Range? get currentlySelectedRange => _currentlySelectedRange;
set currentlySelectedRange(Range? value) {
_currentlySelectedRange = value;
notifyListeners();
}
DateTime? _toDate;
DateTime? get toDate => _toDate;
set toDate(DateTime? value) {
_toDate = value;
notifyListeners();
}
DateTime? _fromDate;
DateTime? get fromDate => _fromDate;
set fromDate(DateTime? value) {
_fromDate = value;
notifyListeners();
}
LabRangeViewModel();
get getCurrentYear => DateTime.now().year;
calculateDatesFromRange() {
_toDate = DateTime.now();
switch (_currentlySelectedRange) {
case Range.WEEKLY:
_fromDate = _toDate!.subtract(Duration(days: 7));
case Range.LAST_MONTH:
_fromDate = _toDate!.subtract(Duration(days: 30));
case Range.LAST_6MONTH:
_fromDate = _toDate!.subtract(Duration(days: (30 * 6)));
case Range.THIS_YEAR:
_toDate = DateTime(_toDate!.year, DateTime.december, 31);
_fromDate = DateTime(_toDate!.year, DateTime.january, 01);
default:
}
}
getDateString(DateTime? date){
if(date == null) return "-";
String year = date.year.toString().substring(2);
return '${date.day} ${months[date.month-1]},$year';
}
flush(){
toDate = null;
fromDate = null;
currentlySelectedRange = null;
isGraphVisible = true;
notifyListeners();
}
resetCurrentlySelectedRange(){
currentlySelectedRange = null;
}
alterGraphVisibility(){
isGraphVisible = !isGraphVisible;
notifyListeners();
}
}

@ -6,8 +6,11 @@ import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/resp_models/lab_result.dart' show LabResult;
abstract class LabRepo {
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders();
Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName);
}
class LabRepoImp implements LabRepo {
@ -19,7 +22,6 @@ class LabRepoImp implements LabRepo {
@override
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<PatientLabOrdersResponseModel>>? apiResponse;
Failure? failure;
@ -56,4 +58,56 @@ class LabRepoImp implements LabRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(
PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName
) async {
Map<String, dynamic> request = Map();
request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
request['OrderNo'] = laborder!.orderNo;
request['isDentalAllowedBackend'] = false;
request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
request['Procedure'] = procedureName;
request['LanguageID'] = 1;
try {
GenericApiModel<List<LabResult>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_Patient_LAB_ORDERS_RESULT,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListPLR'];
if (list == null || list.isEmpty) {
throw Exception("lab list is empty");
}
final labOrders = list.map((item) => LabResult.fromJson(item as Map<String, dynamic>)).toList().cast<LabResult>();
apiResponse = GenericApiModel<List<LabResult>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: labOrders,
);
} 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()));
}
}
}

@ -1,7 +1,20 @@
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:logger/logger.dart';
class LabViewModel extends ChangeNotifier {
bool isLabOrdersLoading = false;
@ -9,17 +22,42 @@ class LabViewModel extends ChangeNotifier {
LabRepo labRepo;
ErrorHandlerService errorHandlerService;
NavigationService navigationService;
List<PatientLabOrdersResponseModel> patientLabOrders = [];
List<PatientLabOrdersResponseModel> filteredLabOrders = [];
List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
List<LabResult> mainLabResults = [];
List<DataPoint> mainGraphPoints = [];
List<DataPoint> filteredGraphValues = [];
List months = [
'Jan',
'Feb',
'Mar',
'April',
'May',
'Jun',
'July',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
];
late List<String> _labSuggestionsList = [];
List<String> get labSuggestions => _labSuggestionsList;
Set<TestDetails> uniqueTests = {};
LabViewModel({required this.labRepo, required this.errorHandlerService});
double maxYForThreeDots = 0.0;
LabViewModel(
{required this.labRepo,
required this.errorHandlerService,
required this.navigationService});
initLabProvider() {
patientLabOrders.clear();
@ -84,9 +122,321 @@ class LabViewModel extends ChangeNotifier {
uniqueTests = {
for (var item in patientLabOrders)
if (item.testDetails != null)
...?item.testDetails?.map<TestDetails>((test) =>
TestDetails(description: test.description.toString(), testCode: test.testCode.toString(), testID: test.testID, createdOn: item.createdOn))
...?item.testDetails?.map<TestDetails>((test) => TestDetails(
description: test.description.toString(),
testCode: test.testCode.toString(),
testID: test.testID,
createdOn: item.createdOn,
model: item))
};
uniqueTests.forEach(print);
}
Future<void> getPatientLabResult(
PatientLabOrdersResponseModel laborder, String procedureName) async {
LoaderBottomSheet.showLoader();
mainLabResults.clear();
filteredGraphValues.clear();
final result = await labRepo.getPatientLabResults(
laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
procedureName);
result.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
var sortedResponse = sortByFlagAndValue(apiResponse.data!);
var recentThree = sort(sortedResponse);
mainLabResults = recentThree;
double counter = 1;
recentThree.reversed.forEach((element) {
try {
var dateTime =
DateUtil.convertStringToDate(element.verifiedOnDateTime!);
if (double.parse(element.resultValue!) > maxYForThreeDots) {
maxYForThreeDots = double.parse(element.resultValue!);
}
filteredGraphValues.add(DataPoint(
value: transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""),
actualValue:element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
refernceValue: element.calculatedResultFlag ?? "",
));
counter++;
} catch (e) {}
});
LabResult recentResult = recentThree.first;
recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
navigationService.push(MaterialPageRoute(
builder: (_) =>
LabResultDetails(recentLabResult: recentResult)));
notifyListeners();
}
},
);
}
String resultDate(DateTime date){
return '${date.day} ${months[date.month-1]},${date.year}';
}
double transformValueInRange(double inputValue, String flag) {
// Define range boundaries
double rangeStart, rangeEnd;
switch (flag) {
case'LCL':
case 'CL':
rangeStart = 0.0;
rangeEnd = 19.0;
break;
case 'L':
rangeStart = 20.0;
rangeEnd = 39.0;
break;
case 'N':
rangeStart = 40.0;
rangeEnd = 59.0;
break;
case 'H':
rangeStart = 60.0;
rangeEnd = 79.0;
break;
case 'HCH':
case 'CH':
rangeStart = 80.0;
rangeEnd = 100.0;
break;
default:
throw ArgumentError('Invalid flag: $flag');
}
// Clamp input value to 0-100 and map it to the range bounds
final clampedValue = inputValue.clamp(0.0, 100.0);
final normalizedValue = clampedValue / 100.0; // Normalize input to 0-1
// Map the normalized value to the target range bounds
final transformedValue = rangeStart + (normalizedValue * (rangeEnd - rangeStart));
return transformedValue;
}
void getSelectedDateRange(DateTime? start, DateTime? end) {
if(start == null && end == null) {
print("the dates are null");
mainLabResults.forEach((element) {
final time = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
try{
filteredGraphValues.add(DataPoint(
value: transformValueInRange(double.parse(element.resultValue!),
element.calculatedResultFlag ?? ""),
actualValue: element.resultValue!,
label: formatDateAsMMYY(time),
displayTime: resultDate(time),
time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
refernceValue: element.calculatedResultFlag ?? "",
));
}catch(e){
}
});
}else {
filteredGraphValues.clear();
mainLabResults.forEach((element) {
try {
var dateTime =
DateUtil.convertStringToDate(element.verifiedOnDateTime!);
if (start != null && end == null) {
if (dateTime.isAtSameMomentAs(start)) {
filteredGraphValues.add(DataPoint(
value: transformValueInRange(
double.parse(element.resultValue!),
element.calculatedResultFlag ?? ""),
actualValue: element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time:
DateUtil.convertStringToDate(element.verifiedOnDateTime),
refernceValue: element.calculatedResultFlag ?? ""));
}
} else if (start != null && end != null) {
if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) {
filteredGraphValues.add(DataPoint(
value: transformValueInRange(
double.parse(element.resultValue!),
element.calculatedResultFlag ?? ""),
actualValue: element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time:
DateUtil.convertStringToDate(element.verifiedOnDateTime),
refernceValue: element.calculatedResultFlag ?? ""));
}
}
} catch (e) {}
});
}
filteredGraphValues = sortFilteredList(filteredGraphValues).reversed.toList();
notifyListeners();
}
String formatDateAsMMYY(DateTime date) {
String year = date.year.toString().substring(2);
return '${months[date.month-1]},$year';
}
List<LabResult> sortByFlagAndValue(List<LabResult> original) {
const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH'];
int getFlagPriority(String? flag) {
if (flag == null) return priorityOrder.length;
final index = priorityOrder.indexOf(flag);
return index == -1 ? priorityOrder.length : index;
}
double parseResultValue(String? value) {
if (value == null) return double.nan;
return double.tryParse(value) ?? double.nan;
}
final copy = List<LabResult>.from(original);
copy.sort((a, b) {
final aFlagPriority = getFlagPriority(a.calculatedResultFlag);
final bFlagPriority = getFlagPriority(b.calculatedResultFlag);
if (aFlagPriority != bFlagPriority) {
return aFlagPriority.compareTo(bFlagPriority);
}
final aValue = parseResultValue(a.resultValue);
final bValue = parseResultValue(b.resultValue);
return aValue.compareTo(bValue);
});
return copy;
}
List<LabResult> sort(List<LabResult> original) {
DateTime? parseVerifiedDate(String? raw) {
if (raw == null) return null;
final regex = RegExp(r'\/Date\((\d+)\)\/');
final match = regex.firstMatch(raw);
if (match != null) {
final millis = int.tryParse(match.group(1)!);
if (millis != null) {
return DateTime.fromMillisecondsSinceEpoch(millis);
}
}
return null;
}
final copy = List<LabResult>.from(original);
copy.sort((a, b) {
final aDate = DateUtil.convertStringToDate(a.verifiedOnDateTime);
final bDate = DateUtil.convertStringToDate(b.verifiedOnDateTime);
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;
if (bDate == now) return -1;
return bDate.compareTo(aDate); // descending
});
return copy.toList();
}
List<DataPoint> sortFilteredList(List<DataPoint> original) {
final copy = List<DataPoint>.from(original);
copy.sort((a, b) {
final aDate =a.time;
final bDate = a.time;
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;
if (bDate == now) return -1;
return bDate.compareTo(aDate); // descending
});
return copy.toList();
}
Color getColor(String flag) {
switch (flag) {
case 'LCL':
return AppColors.criticalLowAndHigh;
case 'CL':
return AppColors.criticalLowAndHigh;
case 'L':
return AppColors.highAndLow;
case 'N':
return AppColors.bgGreenColor;
case 'H':
return AppColors.highAndLow;
case 'CH':
return AppColors.criticalLowAndHigh;
case 'HCH':
return AppColors.criticalLowAndHigh;
default:
return Colors.grey;
}
}
String getFormattedDate(DateTime date){
return DateFormat('EEEE, dd MMMM. yyyy').format(date);
}
String getAssetUrlWRTResult(String refernceValue) {
switch (refernceValue) {
case 'CL':
case 'LCL':
return AppAssets.ic_critical_low_result;
case 'L':
return AppAssets.ic_low_result;
case 'N':
return AppAssets.ic_normal_result;
case 'H':
return AppAssets.ic_low_result;
case 'CH':
case 'HCH':
return AppAssets.ic_critical_low_result;
default:
return AppAssets.ic_normal_result;
}
}
bool getRotationWRTResult(String refernceValue) {
switch (refernceValue) {
case 'CL':
case 'LCL':
case 'L':
case 'N':
return false;
case 'H':
case 'CH':
case 'HCH':
return true;
default:
return true;
}
}
}

@ -0,0 +1,6 @@
enum Range{
WEEKLY,
LAST_MONTH,
LAST_6MONTH,
THIS_YEAR,
}

@ -0,0 +1,116 @@
class LabResult {
String? description;
dynamic femaleInterpretativeData;
int? gender;
int? lineItemNo;
dynamic maleInterpretativeData;
dynamic notes;
String? packageID;
int? patientID;
String? projectID;
String? referanceRange;
String? resultValue;
String? sampleCollectedOn;
String? sampleReceivedOn;
String? setupID;
dynamic superVerifiedOn;
String? testCode;
String? uOM;
String? verifiedOn;
String? verifiedOnDateTime;
String? changeResult;
String? calculatedResultFlag;
String? criticalHigh;
String? referenceHigh;
String? criticalLow;
String? referenceLow;
LabResult(
{this.description,
this.femaleInterpretativeData,
this.gender,
this.lineItemNo,
this.maleInterpretativeData,
this.notes,
this.packageID,
this.patientID,
this.projectID,
this.referanceRange,
this.resultValue,
this.sampleCollectedOn,
this.sampleReceivedOn,
this.setupID,
this.superVerifiedOn,
this.testCode,
this.uOM,
this.verifiedOn,
this.calculatedResultFlag,
this.verifiedOnDateTime,
this.criticalHigh,
this.referenceHigh,
this.criticalLow,
this.referenceLow,
});
LabResult.fromJson(Map<String, dynamic> json) {
description = json['Description'];
femaleInterpretativeData = json['FemaleInterpretativeData'];
gender = json['Gender'];
lineItemNo = json['LineItemNo'];
maleInterpretativeData = json['MaleInterpretativeData'];
notes = json['Notes'];
packageID = json['PackageID'];
patientID = json['PatientID'];
projectID = json['ProjectID'];
referanceRange = json['ReferanceRange'];
resultValue = json['ResultValue'];
sampleCollectedOn = json['SampleCollectedOn'];
sampleReceivedOn = json['SampleReceivedOn'];
setupID = json['SetupID'];
superVerifiedOn = json['SuperVerifiedOn'];
testCode = json['TestCode'];
uOM = json['UOM'];
verifiedOn = json['VerifiedOn'];
verifiedOnDateTime = json['VerifiedOnDateTime'];
changeResult = json['ChangeResult'];
calculatedResultFlag = json['CalculatedResultFlag'];
criticalHigh = json['CriticalHigh'];
referenceHigh = json['ReferenceHigh'];
criticalLow = json['CriticalLow'];
referenceLow = json['ReferenceLow'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Description'] = this.description;
data['FemaleInterpretativeData'] = this.femaleInterpretativeData;
data['Gender'] = this.gender;
data['LineItemNo'] = this.lineItemNo;
data['MaleInterpretativeData'] = this.maleInterpretativeData;
data['Notes'] = this.notes;
data['PackageID'] = this.packageID;
data['PatientID'] = this.patientID;
data['ProjectID'] = this.projectID;
data['ReferanceRange'] = this.referanceRange;
data['ResultValue'] = this.resultValue;
data['SampleCollectedOn'] = this.sampleCollectedOn;
data['SampleReceivedOn'] = this.sampleReceivedOn;
data['SetupID'] = this.setupID;
data['SuperVerifiedOn'] = this.superVerifiedOn;
data['TestCode'] = this.testCode;
data['UOM'] = this.uOM;
data['VerifiedOn'] = this.verifiedOn;
data['VerifiedOnDateTime'] = this.verifiedOnDateTime;
data['ChangeResult'] = this.changeResult;
data['CriticalHigh'] = this.criticalHigh;
data['ReferenceHigh'] = this.referenceHigh;
data['CriticalLow'] = this.criticalLow;
data['ReferenceLow'] = this.referenceLow;
return data;
}
@override
String toString() {
return 'LabOrderResult(flag: $calculatedResultFlag, value: $resultValue, verifiedOn: $verifiedOnDateTime)';
}
}

@ -227,7 +227,8 @@ class TestDetails {
String? testCode;
String? testID;
String? createdOn;
TestDetails({this.description, this.testCode, this.testID, this.createdOn});
PatientLabOrdersResponseModel? model;
TestDetails({this.description, this.testCode, this.testID, this.createdOn, this.model});
TestDetails.fromJson(Map<String, dynamic> json) {
description = json['Description'];

@ -12,6 +12,8 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
@ -78,7 +80,7 @@ void main() async {
create: (_) => LabViewModel(
labRepo: getIt(),
errorHandlerService: getIt(),
),
navigationService: getIt()),
),
ChangeNotifierProvider<RadiologyViewModel>(
create: (_) => RadiologyViewModel(
@ -148,7 +150,11 @@ void main() async {
),
ChangeNotifierProvider<AppointmentViaRegionViewmodel>(
create: (_) => AppointmentViaRegionViewmodel(
navigationService: getIt(), appState: getIt()))
navigationService: getIt(), appState: getIt())),
ChangeNotifierProvider<LabHistoryViewModel>(
create: (_) => LabHistoryViewModel()),
ChangeNotifierProvider<LabRangeViewModel>(
create: (_) => LabRangeViewModel())
], child: MyApp()),
),
);

@ -29,6 +29,7 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';

@ -29,56 +29,52 @@ class LabOrderByTest extends StatelessWidget {
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
if (!isLoading) {
onTap();
}
},
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
),
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
),
SizedBox(height: 12.h),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true),
// chipType: ChipTypeEnum.lightBg,
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
// borderRadius: 5,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true),
// chipType: ChipTypeEnum.lightBg,
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
// borderRadius: 5,
),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
onTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
)));
],
),
));
}
}

@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
@ -30,6 +31,8 @@ class LabOrdersPage extends StatefulWidget {
class _LabOrdersPageState extends State<LabOrdersPage> {
late LabViewModel labProvider;
late LabRangeViewModel rangeViewModel;
List<List<TestDetails>?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
@ -45,6 +48,8 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
@override
Widget build(BuildContext context) {
labProvider = Provider.of<LabViewModel>(context);
rangeViewModel = Provider.of<LabRangeViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
@ -154,9 +159,14 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
child: FadeInAnimation(
child: LabOrderByTest(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
if(model.uniqueTests.toList()[index].model != null) {
rangeViewModel.flush();
model.getPatientLabResult(
model.uniqueTests
.toList()[index]
.model!, model.uniqueTests
.toList()[index].description!);
}
},
tests: model.uniqueTests.toList()[index],
index: index,

@ -0,0 +1,336 @@
import 'dart:async';
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/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/Range.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
typedef OnRangeSelected = void Function(DateTime? start, DateTime? end);
class LabResultCalender extends StatefulWidget {
final OnRangeSelected onRangeSelected;
const LabResultCalender({super.key, required this.onRangeSelected});
@override
State<LabResultCalender> createState() => _LabResultCalenderState();
}
class _LabResultCalenderState extends State<LabResultCalender> {
late DateRangePickerController _calendarController;
DateTime? start;
DateTime? end;
late LabRangeViewModel model;
@override
void initState() {
_calendarController = DateRangePickerController();
scheduleMicrotask(() {
_calendarController.selectedRange = PickerDateRange(model.fromDate,model.toDate);
});
super.initState();
}
@override
Widget build(BuildContext context) {
model = Provider.of<LabRangeViewModel>(context);
return Padding(
padding: EdgeInsets.symmetric(horizontal: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<LabRangeViewModel>(
builder: (_, model, __) => selectionChip(model),
).paddingOnly(bottom: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
padding: EdgeInsets.all(
16.h
),
child: Column(
children: [
Row(
children: [
fromDateComponent(),
Text(
"to".needTranslation,
style: TextStyle(
color: AppColors.calenderTextColor,
fontSize: 14.h,
fontWeight: FontWeight.w500,
letterSpacing: -.2
),
).paddingSymmetrical(24.h,0.h),
toDateComponent(),
],
),
Divider(
color: AppColors.spacerLineColor,
thickness: 1,
).paddingOnly(bottom: 16.h, top: 16.h),
Material(
color: Colors.white,
child: SfDateRangePicker(
controller: _calendarController,
selectionMode: DateRangePickerSelectionMode.range,
showNavigationArrow: true,
headerHeight: 40.h,
backgroundColor: Colors.white,
headerStyle: DateRangePickerHeaderStyle(
backgroundColor: Colors.white,
textAlign: TextAlign.start,
textStyle: TextStyle(
fontSize: 18.fSize,
fontWeight: FontWeight.w600,
letterSpacing: -0.46,
color: AppColors.primaryRedColor,
fontFamily: "Poppins",
),
),
monthViewSettings: DateRangePickerMonthViewSettings(
viewHeaderStyle: DateRangePickerViewHeaderStyle(
backgroundColor: Colors.white,
textStyle: TextStyle(
fontSize: 14.fSize,
fontWeight: FontWeight.w600,
letterSpacing: -0.46,
color: AppColors.textColor,
),
),
showTrailingAndLeadingDates: false,
dayFormat: "EEE",
),
selectionShape: DateRangePickerSelectionShape.rectangle,
selectionRadius: 12.h,
selectionColor: AppColors.transparent,
startRangeSelectionColor: AppColors.primaryRedColor,
endRangeSelectionColor: AppColors.primaryRedColor,
rangeSelectionColor:
AppColors.primaryRedColor.withOpacity(0.1),
todayHighlightColor: Colors.transparent,
monthCellStyle: DateRangePickerMonthCellStyle(
textStyle: TextStyle(
fontSize: 12.fSize,
color: AppColors.textColor,
),
todayTextStyle: TextStyle(
color: AppColors.textColor,
fontWeight: FontWeight.bold,
),
),
onSelectionChanged:
(DateRangePickerSelectionChangedArgs args) {
if (args.value is PickerDateRange) {
final PickerDateRange range = args.value;
start = range.startDate;
end = range.endDate;
model.fromDate = start;
model.toDate = end;
model.resetCurrentlySelectedRange();
}
},
),
),
],
),
),
Row(
children: [
Consumer<LabRangeViewModel>(
builder: (_, model, __) => Visibility(
visible: (model.fromDate != null || model.toDate != null),
child: Expanded(
child: Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(),
onPressed: () {
_calendarController.selectedRange = null;
_calendarController.selectedDate = null;
model.flush();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
icon: AppAssets.cancel,
iconColor: AppColors.primaryRedColor,
height: 56.h,
),
),
SizedBox(width: 16.h,)
],
),
),
),
),
Expanded(
child: CustomButton(
text: LocaleKeys.search.tr(),
onPressed: () {
Navigator.of(context).pop();
widget.onRangeSelected(model.fromDate, model.toDate);
},
backgroundColor: AppColors.lightGreenButtonColor,
borderColor: Colors.transparent,
textColor: AppColors.textGreenColor,
icon: AppAssets.reminder_bell,
iconColor: AppColors.textGreenColor,
height: 56.h,
),
),
],
).paddingOnly(top: 24.h),
],
),
);
}
fromDateComponent() {
return Consumer<LabRangeViewModel>(
builder: (_, model, __) {
return displayDate("Start Date".needTranslation,
model.getDateString(model.fromDate), model.fromDate == null);
},
);
}
toDateComponent() {
return Consumer<LabRangeViewModel>(
builder: (_, model, __) {
return displayDate("End Date".needTranslation,
model.getDateString(model.toDate), model.toDate == null);
},
);
}
displayDate(String label, String? date, bool isNotSelected) => Expanded(
child: Row(
spacing: 12.h,
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.rangeCalendar,
iconColor: isNotSelected ? AppColors.borderOnlyColor: AppColors.blackColor ,
height: 24,
width: 24),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
color: AppColors.inputLabelTextColor,
fontSize: 12.h,
fontWeight: FontWeight.w500,
),
),
Text(
date!,
style: TextStyle(
color: AppColors.textColor,
fontSize: 14.h,
fontWeight: FontWeight.w500,
),
)
],
)
],
),
);
selectionChip(LabRangeViewModel model) {
return Row(
spacing: 8.h,
children: [
AppCustomChipWidget(
labelText: "This Week".needTranslation,
backgroundColor: model.currentlySelectedRange == Range.WEEKLY
? AppColors.primaryRedColor.withOpacity(0.1)
: AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.WEEKLY
? AppColors.primaryRedBorderColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
).onPress((){
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.WEEKLY;
model.calculateDatesFromRange();
}),
AppCustomChipWidget(
labelText: "Last Month".needTranslation,
backgroundColor: model.currentlySelectedRange == Range.LAST_MONTH
? AppColors.primaryRedColor.withOpacity(0.1)
: AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.LAST_MONTH
? AppColors.primaryRedBorderColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
).onPress((){
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.LAST_MONTH;
model.calculateDatesFromRange();
}),
AppCustomChipWidget(
labelText: "Last 6 Months".needTranslation,
backgroundColor: model.currentlySelectedRange == Range.LAST_6MONTH
? AppColors.primaryRedColor.withOpacity(0.1)
: AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.LAST_6MONTH
? AppColors.primaryRedBorderColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
).onPress((){
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.LAST_6MONTH;
model.calculateDatesFromRange();
}),
AppCustomChipWidget(
labelText: "Year ${model.getCurrentYear}",
backgroundColor: model.currentlySelectedRange == Range.THIS_YEAR
? AppColors.primaryRedColor.withOpacity(0.1)
: AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.THIS_YEAR
? AppColors.primaryRedBorderColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
).onPress((){
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.THIS_YEAR;
model.calculateDatesFromRange();
}),
],
);
}
}

@ -0,0 +1,290 @@
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart' show LabRangeViewModel;
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
import 'package:provider/provider.dart' show Consumer, Provider;
import '../../../widgets/common_bottom_sheet.dart'
show showCommonBottomSheetWithoutHeight;
import '../../book_appointment/widgets/appointment_calendar.dart'
show AppointmentCalendar;
class LabResultDetails extends StatelessWidget {
final LabResult recentLabResult;
// final List<DataPoint> graphPoint;
late LabViewModel model;
LabResultDetails({super.key, required this.recentLabResult});
@override
Widget build(BuildContext context) {
model = Provider.of<LabViewModel>(context, listen: false);
return CollapsingListView(
title: 'Lab Result Details'.needTranslation,
child: SingleChildScrollView(
child: Column(
spacing: 16.h,
children: [LabNameAndStatus, LabGraph(context)],
).paddingAll(24.h),
),
);
}
Widget get LabNameAndStatus => Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
recentLabResult.testCode ?? "",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
color: AppColors.textColor,
letterSpacing: -2),
),
Text(
"Result of ${recentLabResult.verifiedOn ?? ""}".needTranslation,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
],
),
//todo change the text color according to the provided test values
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Row(
spacing: 4.h,
children: [
Flexible(
child: Text(
recentLabResult.resultValue ?? "",
style: TextStyle(
fontSize: 24.fSize,
fontWeight: FontWeight.w600,
color: model.getColor(
recentLabResult.calculatedResultFlag ?? "",
),
letterSpacing: -2,
),
overflow: TextOverflow.ellipsis, // prevent overflow
maxLines: 1,
softWrap: false,
),
),
Visibility(
visible: recentLabResult.referanceRange != null,
child: Text(
"(Reference range ${recentLabResult.referanceRange})".needTranslation,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
),
],
),
),
Utils.buildSvgWithAssets(
icon: AppAssets.lab_result_indicator,
width: 21,
height: 23,
iconColor: model.getColor(
recentLabResult.calculatedResultFlag ?? "",
),
),
],
)
],
));
Widget LabGraph(BuildContext context) => Consumer<LabRangeViewModel>(
builder: (_, model, ___) => Consumer<LabViewModel>(
builder: (_, labmodel, ___) => Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
height: 260.h,
padding: EdgeInsets.all(16.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
//title and filter icon
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
model.isGraphVisible?"History FlowChart".needTranslation: "History".needTranslation,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
Row(
spacing: 16.h,
children: [
//todo handle when the graph icon is being displayed
Utils.buildSvgWithAssets(
icon: model.isGraphVisible?AppAssets.ic_list:AppAssets.ic_graph,
width: 24,
height: 24)
.onPress(() {
model.alterGraphVisibility();
}),
Utils.buildSvgWithAssets(
icon: AppAssets.ic_date_filter,
width: 24,
height: 24)
.onPress(() {
showCommonBottomSheetWithoutHeight(
title: "Set The Date Range".needTranslation,
context,
child: LabResultCalender(
onRangeSelected: (start, end) {
// if (start != null) {
labmodel.getSelectedDateRange(start, end);
// }
},
),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
}),
],
)
],
).paddingOnly(bottom: model.isGraphVisible? 16.h :24.h),
historyBody(model, labmodel)
],
)),
));
Widget leftLabels(String value) {
return Text(
value,
style: TextStyle(
fontWeight: FontWeight.w300,
fontFamily: 'Poppins',
fontSize: 8.fSize,
color: AppColors.textColor,
),
);
}
Widget buildBottomLabel(String label) {
return Padding(
padding: const EdgeInsets.only(top:8.0),
child: Text(
label,
style: TextStyle(
fontSize: 8.fSize,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.labelTextColor),
),
);
}
Widget historyBody(LabRangeViewModel model, LabViewModel labmodel) {
if(model.isGraphVisible){
return CustomGraph(
dataPoints: labmodel.filteredGraphValues,
maxY: 100,
leftLabelFormatter: (value) {
switch (value.toInt()) {
case 20:
return leftLabels("Critical Low".needTranslation);
case 40:
return leftLabels("Low".needTranslation);
case 60:
return leftLabels("Normal".needTranslation);
case 80:
return leftLabels("High".needTranslation);
case 100:
return leftLabels(
"Critical High".needTranslation);
default:
return SizedBox.shrink();
}
},
bottomLabelFormatter: (value, data) {
if(data.isEmpty) return SizedBox.shrink();
if (value == 0) {
return buildBottomLabel(data[value.toInt()].label);
}
if (value == data.length - 1) {
return buildBottomLabel(data[value.toInt()].label);
}
if (value == ((data.length - 1) / 2)) {
return buildBottomLabel(data[value.toInt()].label);
}
return SizedBox.shrink();
},
scrollDirection: Axis.horizontal,
height: 180.h);
}else {
return labHistoryList(model, labmodel);
}
}
Widget labHistoryList(LabRangeViewModel model, LabViewModel labmodel) {
return SizedBox(
height: 180.h,
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: labmodel.filteredGraphValues.length,itemBuilder: (context, index){
var data = labmodel.filteredGraphValues.reversed.toList()[index];
return LabHistoryItem(
dayNameAndDate: labmodel.getFormattedDate(data.time),
result: data.actualValue,
assetUrl: labmodel.getAssetUrlWRTResult(data.refernceValue),
shouldRotateIcon: labmodel.getRotationWRTResult(data.refernceValue),
);
},
separatorBuilder: (_, __) => Divider(
color: AppColors.spacerLineColor,
thickness: 1.h,
).paddingOnly(top: 4.h, bottom: 4.h),
),
);
}
}

@ -0,0 +1,51 @@
import 'package:flutter/material.dart' ;
import 'package:flutter/src/widgets/framework.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/theme/colors.dart';
class LabHistoryItem extends StatelessWidget{
final String dayNameAndDate;
final String result;
final String assetUrl;
final bool shouldRotateIcon;
const LabHistoryItem({super.key, required this.dayNameAndDate, required this.result, required this.assetUrl, this.shouldRotateIcon = false});
@override
Widget build(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
dayNameAndDate,
style: TextStyle(
fontSize: 14.fSize,
fontWeight: FontWeight.w500,
fontFamily: 'Poppins',
color: AppColors.labelTextColor
),
),
Text(
result,
style: TextStyle(
fontSize: 18.fSize,
fontWeight: FontWeight.w600,
fontFamily: 'Poppins',
color: AppColors.textColor
),
)
],
),
Transform.flip(
flipY: shouldRotateIcon,
child: Utils.buildSvgWithAssets(icon: assetUrl,height: 18, width: 18)
),
],
);
}

@ -69,4 +69,10 @@ static const Color quickLoginColor = Color(0xFF666666);
static const Color tooltipTextColor = Color(0xFF414D55);
static const Color graphGridColor = Color(0x4D18C273);
static const Color criticalLowAndHigh = Color(0xFFED1C2B);
static const Color highAndLow = Color(0xFFFFAF15);
static const Color labelTextColor = Color(0xFF838383);
static const Color calenderTextColor = Color(0xFFD0D0D0);
static const Color lightGreenButtonColor = Color(0x2618C273);
}

@ -43,6 +43,7 @@ class CustomGraph extends StatelessWidget {
final double height;
final double? maxY;
final double? maxX;
final double? minX;
final Color spotColor;
final Color graphColor;
final Color graphShadowColor;
@ -52,7 +53,9 @@ class CustomGraph extends StatelessWidget {
final FontWeight? bottomLabelFontWeight;
///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart
final Widget Function(double value) leftLabelFormatter;
final Widget Function(double) leftLabelFormatter;
final Widget Function(double , List<DataPoint>) bottomLabelFormatter;
final Axis scrollDirection;
final bool showBottomTitleDates;
@ -76,6 +79,8 @@ class CustomGraph extends StatelessWidget {
this.bottomLabelColor = AppColors.textColor,
this.bottomLabelFontWeight = FontWeight.w500,
this.bottomLabelSize,
required this.bottomLabelFormatter,
this.minX,
});
@override
@ -94,117 +99,108 @@ class CustomGraph extends StatelessWidget {
child: SizedBox(
width: width,
height: height,
child: Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: LineChart(
LineChartData(
minY: 0,
maxY:
((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(),
// minX: dataPoints.first.labelValue - 1,
maxX: maxX,
minX: -0.2,
lineTouchData: LineTouchData(
getTouchLineEnd: (_, __) => 0,
getTouchedSpotIndicator: (barData, indicators) {
// Only show custom marker for touched spot
return indicators.map((int index) {
return TouchedSpotIndicatorData(
FlLine(color: Colors.transparent),
FlDotData(
show: true,
getDotPainter: (spot, percent, barData, idx) {
return FlDotCirclePainter(
radius: 8,
color: spotColor,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
);
child: LineChart(
LineChartData(
minY: 0,
maxY:
((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(),
// minX: dataPoints.first.labelValue - 1,
maxX: maxX,
minX: minX ??-0.2,
lineTouchData: LineTouchData(
getTouchLineEnd: (_, __) => 0,
getTouchedSpotIndicator: (barData, indicators) {
// Only show custom marker for touched spot
return indicators.map((int index) {
return TouchedSpotIndicatorData(
FlLine(color: Colors.transparent),
FlDotData(
show: true,
getDotPainter: (spot, percent, barData, idx) {
return FlDotCirclePainter(
radius: 8,
color: spotColor,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
);
}).toList();
},
enabled: true,
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (_) => Colors.white,
getTooltipItems: (touchedSpots) {
if (touchedSpots.isEmpty) return [];
// Only show tooltip for the first touched spot, hide others
return touchedSpots.map((spot) {
if (spot == touchedSpots.first) {
final dataPoint = dataPoints[spot.x.toInt()];
return LineTooltipItem(
// '${dataPoint.label} ${spot.y.toStringAsFixed(2)}',
'${dataPoint.actualValue} ${dataPoint.displayTime}',
TextStyle(
color: Colors.black,
fontSize: 12.fSize,
fontWeight: FontWeight.w500),
);
}
return null; // hides the rest
}).toList();
},
enabled: true,
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (_) => Colors.white,
getTooltipItems: (touchedSpots) {
if (touchedSpots.isEmpty) return [];
// Only show tooltip for the first touched spot, hide others
return touchedSpots.map((spot) {
if (spot == touchedSpots.first) {
final dataPoint = dataPoints[spot.x.toInt()];
return LineTooltipItem(
// '${dataPoint.label} ${spot.y.toStringAsFixed(2)}',
'${dataPoint.value} ',
TextStyle(
color: Colors.black,
fontSize: 12.fSize,
fontWeight: FontWeight.w500),
);
}
return null; // hides the rest
}).toList();
},
),
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 77,
interval: .1, // Let fl_chart handle it
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
bottomTitles: AxisTitles(
axisNameSize: 60,
sideTitles: SideTitles(
showTitles: showBottomTitleDates,
reservedSize: 50,
getTitlesWidget: (value, _) {
if ((value.toDouble() >= 0) &&
(value.toDouble() < (maxX ?? dataPoints.length))) {
var label = dataPoints[value.toInt()].label;
return buildBottomLabel(label);
}
return const SizedBox.shrink();
},
interval: 1, // ensures 1:1 mapping with spots
),
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 77,
interval: .1, // Let fl_chart handle it
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
top: BorderSide.none,
bottomTitles: AxisTitles(
axisNameSize: 20,
sideTitles: SideTitles(
showTitles: showBottomTitleDates,
reservedSize: 20,
getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints, );
},
interval: 1, // ensures 1:1 mapping with spots
),
),
lineBarsData: _buildColoredLineSegments(dataPoints),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 20,
checkToShowHorizontalLine: (value) =>
value >= 0 && value <= 100,
getDrawingHorizontalLine: (value) {
return FlLine(
color: AppColors.graphGridColor,
strokeWidth: 1,
dashArray: [5, 5],
);
},
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
top: BorderSide.none,
),
),
lineBarsData: _buildColoredLineSegments(dataPoints),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 20,
checkToShowHorizontalLine: (value) =>
value >= 0 && value <= 100,
getDrawingHorizontalLine: (value) {
return FlLine(
color: AppColors.graphGridColor,
strokeWidth: 1,
dashArray: [5, 5],
);
},
),
),
),
));
@ -259,29 +255,20 @@ class CustomGraph extends StatelessWidget {
// );
// }
Widget buildBottomLabel(String label) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
label,
style: TextStyle(
fontSize: bottomLabelSize ?? 8.fSize, color: bottomLabelColor),
),
);
}
}
final List<DataPoint> sampleData = [
DataPoint(
value: 20,
label: 'Jan 2024',
),
DataPoint(
value: 36,
label: 'Feb 2024',
),
DataPoint(
value: 80,
label: 'This result',
),
];
// final List<DataPoint> sampleData = [
// DataPoint(
// value: 20,
// label: 'Jan 2024',
// ),
// DataPoint(
// value: 36,
// label: 'Feb 2024',
// ),
// DataPoint(
// value: 80,
// label: 'This result',
// ),
// ];

@ -84,6 +84,7 @@ dependencies:
location: ^8.0.1
gms_check: ^1.0.4
huawei_location: ^6.14.2+301
intl: ^0.20.2
dev_dependencies:
flutter_test:
@ -119,8 +120,8 @@ flutter:
weight: 500
- asset: assets/fonts/poppins/Poppins-Regular.ttf
weight: 400
# - asset: assets/fonts/poppins/Poppins-Light.ttf
# weight: 300
- asset: assets/fonts/poppins/Poppins-Light.ttf
weight: 300
# - asset: assets/fonts/poppins/Poppins-ExtraLight.ttf
# weight: 200
# - asset: assets/fonts/poppins/Poppins-Thin.ttf

Loading…
Cancel
Save