lab items are added as cureved and side ranges changed

lab_result
taha.alam 5 months ago
parent 9a23b7dd7c
commit ab210552f0

@ -721,27 +721,35 @@ class LabsService extends BaseService {
List<ThresholdRange> getThresholdValue() {
return [
ThresholdRange(
label: 'criticalLow',
label: 'LCL',
value: 0,
color: Color(0xffffffff),
lineColor: Color(0xFFe9a2a4)), ThresholdRange(
label: 'CL',
value: 0,
color: Color(0xffffffff),
lineColor: Color(0xFFe9a2a4)),
ThresholdRange(
label: 'low',
label: 'L',
value: 20,
color: Color(0xFFf2fbf5),
lineColor: Color(0xFFeecd94)),
ThresholdRange(
label: 'normal',
label: 'N',
value: 40,
color: Color(0xFFf2fbf5),
lineColor: Color(0xFF5dc36b)),
ThresholdRange(
label: 'high',
label: 'H',
value: 60,
color: Color(0xffffffff),
lineColor: Color(0xFFeecd94)),
ThresholdRange(
label: 'criticalHigh',
label: 'CH',
value: 80,
color: Color(0xffffffff),
lineColor: Color(0xFFe9a2a4)), ThresholdRange(
label: 'HCH',
value: 80,
color: Color(0xffffffff),
lineColor: Color(0xFFe9a2a4)),

@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app/core/enum/filter_type.dart';
import 'package:hmg_patient_app/core/enum/viewstate.dart';
import 'package:hmg_patient_app/core/model/labs/LabOrderResult.dart';
@ -29,6 +30,8 @@ class LabsViewModel extends BaseViewModel {
List<DataPoint> threePointGraphValue = [];
List<DataPoint> completeeGraphValues = [];
List<ThresholdRange> threshold = [];
double maxYForThreeDots = 0.0;
double maxYForCompleteGraph = 0.0;
List months = ['Jan','Feb','Mar','April','May','Jun','July','Aug','Sep','Oct','Nov','Dec'];
@ -164,6 +167,7 @@ class LabsViewModel extends BaseViewModel {
}
getPatientLabOrdersResults({PatientLabOrders? patientLabOrder, String? procedure, bool? isVidaPlus}) async {
maxYForThreeDots = 0.0;
setState(ViewState.Busy);
await _labsService.getPatientLabOrdersResults(
patientLabOrder: patientLabOrder,
@ -202,11 +206,19 @@ class LabsViewModel extends BaseViewModel {
recentThree.reversed.forEach((element) {
try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
// debugPrint("the double.parse(element.resultValue!) is ${double.parse(element.resultValue!)}");
if(double.parse(element.resultValue!)> maxYForThreeDots){
debugPrint("the double.parse(element.resultValue!) is ${double.parse(element.resultValue!)}");
maxYForThreeDots = double.parse(element.resultValue!);
}
// threePointGraphValue.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime));
threePointGraphValue.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), actualValue: element.resultValue!,label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime));
threePointGraphValue.add(DataPoint( labelValue: counter,value : double.parse(element.resultValue!), actualValue: element.resultValue!,label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime, referenceRangeValue:element.calculatedResultFlag ??"IRR"));
counter++;
} catch (e) {}
});
debugPrint("the maxY is ${maxYForThreeDots}");
threePointGraphValue.last.label = "thisResult";
threePointGraphValue.last.isStringResource = true;
setState(ViewState.Idle);
@ -218,6 +230,7 @@ class LabsViewModel extends BaseViewModel {
createFullGraphDatPoints(
VoidCallback onComplete
){
maxYForCompleteGraph = 0.0;
completeeGraphValues.clear();
setState(ViewState.Busy);
double counter = 1;
@ -226,8 +239,11 @@ class LabsViewModel extends BaseViewModel {
try {
print("the mapping is being done");
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
if(double.parse(element.resultValue!)> maxYForCompleteGraph){
maxYForCompleteGraph = double.parse(element.resultValue!);
}
// completeeGraphValues.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime));
completeeGraphValues.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime,actualValue: element.resultValue!));
completeeGraphValues.add(DataPoint( labelValue: counter,value : double.parse(element.resultValue!), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime,actualValue: element.resultValue!, referenceRangeValue:element.calculatedResultFlag??"IRR" ));
} catch (e) {
print("the mapping is having exception $e");

@ -36,7 +36,9 @@ class FlowChartPage extends StatelessWidget {
procedure: filterName,
isVidaPlus: Utils.isVidaPlusProject(
projectViewModel!, int.parse(patientLabOrder!.projectID!))),
builder: (context, model, w) => SizedBox(
builder: (context, model, w) {
print("the maxY in builder is ${model.maxYForCompleteGraph}");
return SizedBox(
height: MediaQuery.sizeOf(context).height * .75,
child: (model.state == ViewState.Busy)
? Material(
@ -58,19 +60,31 @@ class FlowChartPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
top: 16.0, right: 16.0, bottom: 10),
child: GestureDetector(
onTap: () => Navigator.pop(context),
child: Align(
alignment: Alignment.centerRight,
child: Icon(
Icons.close,
top: 16.0, right: 16.0, bottom: 10, left: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(filterName??"", style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.w700,
color: Colors.black,
size: 28,
letterSpacing: -0.64,
),),
GestureDetector(
onTap: () => Navigator.pop(context),
child: Align(
alignment: Alignment.centerRight,
child: Icon(
Icons.close,
color: Colors.black,
size: 28,
),
),
),
),
],
),
),
Container(
@ -110,6 +124,7 @@ class FlowChartPage extends StatelessWidget {
child: DynamicResultChart(
dataPoints: model.threePointGraphValue,
thresholds: model.threshold,
maxY: model.maxYForThreeDots,
width: MediaQuery.sizeOf(context).width - 77,
scrollDirection: Axis.horizontal,
height: MediaQuery.sizeOf(context).height / 4,
@ -124,12 +139,14 @@ class FlowChartPage extends StatelessWidget {
return FullScreenGraph(
completeeGraphValues: model.completeeGraphValues,
threshold: model.threshold,
maxY : model.maxYForCompleteGraph
);
}));
});
},
child: Text(TranslationBase.of(context)
.seeAllGraphValues),
// child: Text(TranslationBase.of(context)
// .seeAllGraphValues),
child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).seeAllGraphValues, isLast: true, isCapitable: false),
)
],
),
@ -151,7 +168,7 @@ class FlowChartPage extends StatelessWidget {
),
),
),
),
);}
);
}
}

@ -28,7 +28,7 @@ class _VitalSignDetailsWidgetState extends State<LabResultDetailsWidget> {
columnWidths: {
0: FlexColumnWidth(2),
1: FlexColumnWidth(1),
2: FlexColumnWidth(1),
// 2: FlexColumnWidth(1),
},
children: fullData(widget.labResult!, context),
);
@ -41,7 +41,7 @@ class _VitalSignDetailsWidgetState extends State<LabResultDetailsWidget> {
children: [
Utils.tableColumnTitle(TranslationBase.of(context).date),
Utils.tableColumnTitle(TranslationBase.of(context).labResults),
Utils.tableColumnTitle(TranslationBase.of(context).change),
// Utils.tableColumnTitle(TranslationBase.of(context).change),
],
),
);
@ -52,7 +52,7 @@ class _VitalSignDetailsWidgetState extends State<LabResultDetailsWidget> {
children: [
Utils.tableColumnValue(DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(labOrderResultList![i].verifiedOnDateTime!)), isLast: i == (labOrderResultList.length - 1), mProjectViewModel: projectViewModel),
Utils.tableColumnValue(labOrderResultList[i].resultValue!, isLast: i == (labOrderResultList.length - 1), mProjectViewModel: projectViewModel),
changeResult(labOrderResultList[i].changeResult ?? '', labOrderResultList[i].calculatedResultFlag??'IRR')
// changeResult(labOrderResultList[i].changeResult ?? '', labOrderResultList[i].calculatedResultFlag??'IRR')
],
),
);

@ -8,8 +8,9 @@ import 'package:flutter/material.dart';
class FullScreenGraph extends StatelessWidget {
final List<DataPoint> completeeGraphValues ;
final List<ThresholdRange> threshold ;
final double maxY ;
const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold});
const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold, required this. maxY});
@override
Widget build(BuildContext context) {
return AppScaffold(
@ -30,13 +31,13 @@ class FullScreenGraph extends StatelessWidget {
child: DynamicResultChart(
dataPoints: completeeGraphValues,
thresholds: threshold,
maxY : maxY,
// width:((completeeGraphValues.length <=2)? MediaQuery.sizeOf(context).height : (MediaQuery.sizeOf(context).height* (
// completeeGraphValues.length <= 3
// ? 1
// : (completeeGraphValues.length/3))
// ) )-77,
width: MediaQuery.sizeOf(context).height-100,
maxX: completeeGraphValues.length.toDouble(),
scrollDirection: Axis.horizontal,
height: MediaQuery.sizeOf(context).width,
isFullScreenGraph: true,

@ -8,24 +8,33 @@ class DynamicResultChart extends StatelessWidget {
final List<ThresholdRange> thresholds;
final double? width;
final double height;
final double? maxX;
final double? maxY;
final Axis scrollDirection;
final bool isFullScreenGraph;
const DynamicResultChart(
{super.key, required this.dataPoints, required this.thresholds, required this.width, required this.scrollDirection, required this.height, this.maxX, this.isFullScreenGraph = false});
DynamicResultChart(
{super.key, required this.dataPoints, required this.thresholds, required this.width, required this.scrollDirection, required this.height, this.maxY, this.isFullScreenGraph = false});
@override
Widget build(BuildContext context) {
var minY = 0.0;
var maxY = 0.0;
if (thresholds.isNotEmpty) {
print("the thresholds are $thresholds");
minY = thresholds.first.value;
maxY = thresholds.last.value + 1;
} else {
return SizedBox.shrink();
}
// var maxY = 0.0;
print("the maxY from parameter is $maxY");
double interval = 20;
if((maxY??0)>10 &&(maxY??0)<=20) interval = 2;
else if((maxY??0)>5 &&(maxY??0)<=10) interval = 1;
else if((maxY??0)>=0 &&(maxY??0)<=5) interval = .4;
// else if((maxY??0)>=100) interval = 20;
print("the interval parameter is $interval");
// if (thresholds.isNotEmpty) {
// print("the thresholds are $thresholds");
// minY = thresholds.first.value;
// maxY = thresholds.last.value + 1;
// } else {
// return SizedBox.shrink();
// }
final spots = dataPoints.asMap().entries.map((entry) {
return FlSpot(entry.key.toDouble(), entry.value.value);
@ -42,10 +51,10 @@ class DynamicResultChart extends StatelessWidget {
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: LineChart(
LineChartData(
minY: minY,
maxY: maxY,
minX: dataPoints.first.labelValue - 1,
maxX: maxX,
minY: 0,
maxY: ((maxY?.ceilToDouble()??0.0)+interval ).floorToDouble(),
// minX: dataPoints.first.labelValue - 1,
// maxX: maxX,
lineTouchData: LineTouchData(touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) {
if (touchedSpots.isEmpty) return [];
@ -72,32 +81,52 @@ class DynamicResultChart extends StatelessWidget {
})),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 77,
interval: .1, // Let fl_chart handle it
getTitlesWidget: (value, _) {
// print("the value is ======== ${value}");
// Compare with 2-decimal precision to avoid close duplicates
final matchingThreshold = thresholds.firstWhere(
(t) => t.value.toStringAsFixed(1) == value.toStringAsFixed(1),
orElse: () => ThresholdRange(label: '', value: 0, color: Colors.transparent, lineColor: Colors.transparent),
);
var actualValue = (matchingThreshold.actualValue != null)
? "${TranslationBase.of(context).getTranslation(matchingThreshold.label)} (${matchingThreshold.actualValue})"
: '${TranslationBase.of(context).getTranslation(matchingThreshold.label)}';
if (matchingThreshold.label.isNotEmpty) {
sideTitles : SideTitles(
showTitles: true,
reservedSize: 50,
interval: interval,
getTitlesWidget: (value, meta) {
// Skip the last (maxY) label
if (value == ((maxY?.ceilToDouble()??0.0)+interval ).floorToDouble()) {
return const SizedBox.shrink();
}
return Text(
actualValue,
value.toStringAsFixed(1),
style: const TextStyle(fontSize: 10),
);
}
return const SizedBox.shrink();
},
),
},
)
),
// leftTitles: AxisTitles(
// sideTitles: SideTitles(
// showTitles: true,
// reservedSize: 77,
// interval: .1, // Let fl_chart handle it
// getTitlesWidget: (value, _) {
// // print("the value is ======== ${value}");
//
// // Compare with 2-decimal precision to avoid close duplicates
// final matchingThreshold = thresholds.firstWhere(
// (t) => t.value.toStringAsFixed(1) == value.toStringAsFixed(1),
// orElse: () => ThresholdRange(label: '', value: 0, color: Colors.transparent, lineColor: Colors.transparent),
// );
//
// var actualValue = (matchingThreshold.actualValue != null)
// ? "${TranslationBase.of(context).getTranslation(matchingThreshold.label)} (${matchingThreshold.actualValue})"
// : '${TranslationBase.of(context).getTranslation(matchingThreshold.label)}';
// if (matchingThreshold.label.isNotEmpty) {
// return Text(
// "${value.toStringAsFixed(0)}",
// style: const TextStyle(fontSize: 10),
// );
// }
// return const SizedBox.shrink();
// },
// ),
// ),
bottomTitles: AxisTitles(
axisNameSize: 60,
sideTitles: SideTitles(
@ -128,27 +157,38 @@ class DynamicResultChart extends StatelessWidget {
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(show: false),
borderData: FlBorderData(show: true,border: const Border(
bottom: BorderSide(
color: Colors.grey,
width: 0.5,
),
left: BorderSide(
color: Colors.grey,
width: .5
),
right:BorderSide.none,
top: BorderSide.none,
),),
lineBarsData: _buildColoredLineSegments(dataPoints, thresholds),
gridData: FlGridData(show: false),
extraLinesData: ExtraLinesData(
horizontalLines: thresholds.map((t) {
return HorizontalLine(
y: t.value,
color: t.lineColor,
strokeWidth: 2,
// label: HorizontalLineLabel(
// show: true,
// alignment: Alignment.centerLeft,
// style: const TextStyle(fontSize: 10),
// labelResolver: (_) => '${t.label} (${t.value.toStringAsFixed(2)})',
// ),
);
}).toList(),
),
rangeAnnotations: RangeAnnotations(
horizontalRangeAnnotations: _buildRangeShades(thresholds),
),
gridData: FlGridData(show: true, drawVerticalLine: false, getDrawingHorizontalLine:(value)=> FlLine(color: Colors.grey, strokeWidth: 0.5)),
// extraLinesData: ExtraLinesData(
// horizontalLines: [
// HorizontalLine(
// color: Colors.black,
// strokeWidth: 2,
// dashArray: [5, 5], // optional: dashed line
// label: HorizontalLineLabel(
// show: true,
// alignment: Alignment.centerRight,
// style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
// labelResolver: (line) => 'Threshold ${line.y.toInt()}',
// ),
// ),
// ],
// ),
// rangeAnnotations: RangeAnnotations(
// horizontalRangeAnnotations: _buildRangeShades(thresholds),
// ),
),
),
),
@ -157,10 +197,11 @@ class DynamicResultChart extends StatelessWidget {
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, List<ThresholdRange> thresholds) {
List<LineChartBarData> segments = [];
Color getColor(double value) {
Color getColor(String value) {
print("the value to compare is $value");
for (int i = thresholds.length - 1; i >= 0; i--) {
if (value >= thresholds[i].value) {
return thresholds[i].lineColor;
if (value == thresholds[i].label) {
return thresholds[i].lineColor;
}
}
return Colors.grey;
@ -173,20 +214,34 @@ class DynamicResultChart extends StatelessWidget {
final spot1 = FlSpot(i.toDouble(), dp1.value);
final spot2 = FlSpot((i + 1).toDouble(), dp2.value);
final color1 = getColor(dp1.value);
final color2 = getColor(dp2.value);
final color1 = getColor(dp1.referenceRangeValue);
final color2 = getColor(dp2.referenceRangeValue);
segments.add(LineChartBarData(
spots: [spot1, spot2],
isCurved: false,
barWidth: 2,
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
gradient: LinearGradient(
colors: [color1, color2],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
dotData: FlDotData(show: false), // We'll handle dots separately
));
// dotData: FlDotData(
// show: true,
// getDotPainter: (spot, _, __, index) {
// final value = spot.y;
// final color = getColor(value);
// return FlDotCirclePainter(
// radius: 4,
// color: color,
// strokeWidth: 1,
// strokeColor: Colors.white,
// );
// },
// ),
)
);
}
// Add dot markers separately so they are on top
@ -197,16 +252,23 @@ class DynamicResultChart extends StatelessWidget {
print("the spots are $allSpots");
var data = [
...segments,
// ...segments
LineChartBarData(
spots: allSpots,
isCurved: false,
barWidth: 0, // invisible line
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
barWidth: 2, // invisible line
gradient: LinearGradient(
colors: [Color(0xFF5dc36b), Color(0xFF5dc36b)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
dotData: FlDotData(
show: true,
getDotPainter: (spot, _, __, index) {
final value = spot.y;
final color = getColor(value);
final color = Color(0xFF5dc36b);
return FlDotCirclePainter(
radius: 4,
color: color,
@ -256,8 +318,9 @@ class DataPoint {
String actualValue;
final DateTime date;
bool isStringResource;
String referenceRangeValue;
DataPoint({required this.value, required this.label, required this.date, this.isStringResource = false, this.labelValue = 0.0, this.actualValue = ""});
DataPoint({required this.value, required this.label, required this.date, this.isStringResource = false, this.labelValue = 0.0, this.actualValue = "", this.referenceRangeValue= ""});
}
class ThresholdRange {

@ -86,7 +86,7 @@ class _LabItemState extends State<LabItem> {
subTitle: data?.resultValue ?? '',
referenceRange: data?.referanceRange ?? '',
percentage: data?.percentage ?? 0.0,
note: data?.testShortDescription ?? '',
note: data?.notes ?? '',
source: "",
buttonText: "",
type: data?.calculatedResultFlag?.getType() ?? ResultTypes.unknown,

Loading…
Cancel
Save