graph changes for high and low

lab-result-changes
tahaalam 2 months ago
parent 393eaad2c7
commit ac15ab26f5

@ -862,7 +862,7 @@ class ApiConsts {
// ************ static values for Api ****************
static final double appVersionID = 19.3;
static final double appVersionID = 50.3;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958";

@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:core';
import 'dart:math';
@ -39,6 +40,13 @@ class LabViewModel extends ChangeNotifier {
String labSpecialResult = "";
List<String> labOrderTests = [];
String patientLabResultReportPDFBase64 = "";
String? flagForHighReferenceRange;
double highRefrenceValue = double.negativeInfinity;
double lowRefenceValue = double.infinity;
double highTransformedReferenceValue = double.negativeInfinity;
double lowTransformedReferenceValue = double.infinity;
String? flagForLowReferenceRange;
PatientLabOrdersResponseModel? currentlySelectedPatientOrder;
@ -67,6 +75,8 @@ class LabViewModel extends ChangeNotifier {
List<String> get labSuggestions => _labSuggestionsList;
Set<TestDetails> uniqueTests = {};
List<TestDetails> uniqueTestsList = [];
List<String> indexedCharacterForUniqueTest = [];
double maxY = 0.0;
double maxX = double.infinity;
@ -195,6 +205,19 @@ class LabViewModel extends ChangeNotifier {
createdOn: item.createdOn,
model: item))
};
var sortedResult = SplayTreeSet<TestDetails>.from(uniqueTests, (a, b) => a.description?[0].toUpperCase().compareTo(b.description?[0] ?? "") ?? -1);
uniqueTestsList = uniqueTests.toList();
uniqueTestsList.sort((a, b) {
return a.description!.toLowerCase().compareTo(b.description!.toLowerCase());
});
indexedCharacterForUniqueTest.clear();
for (var test in uniqueTestsList) {
String label = test.description ?? "";
if (label.isEmpty) continue;
if (indexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue;
indexedCharacterForUniqueTest.add(label[0].toLowerCase());
}
for (var element in uniqueTests) {
labOrderTests.add(element.description ?? "");
}
@ -313,7 +336,12 @@ class LabViewModel extends ChangeNotifier {
var recentThree = sort(sortedResponse);
mainLabResults = recentThree;
double counter = 1;
double highRefrenceValue = double.negativeInfinity;
String? flagForHighReferenceRange;
double lowRefenceValue = double.infinity;
String? flagForLowReferenceRange;
recentThree.reversed.forEach((element) {
try {
var dateTime =
@ -324,6 +352,14 @@ class LabViewModel extends ChangeNotifier {
maxY = resultValue;
maxX = maxY;
}
if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) {
highRefrenceValue = double.parse(element.referenceHigh ?? "0.0");
flagForHighReferenceRange = element.calculatedResultFlag;
}
if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) {
lowRefenceValue = double.parse(element.referenceLow ?? "0.0");
flagForLowReferenceRange = element.calculatedResultFlag;
}
filteredGraphValues.add(DataPoint(
value: transformedValue,
@ -335,9 +371,19 @@ class LabViewModel extends ChangeNotifier {
referenceValue: element.calculatedResultFlag ?? "",
));
counter++;
} catch (e) {}
});
maxY += transformValueInRange(50, "H");
//todo handle the value if flags are null
if(flagForLowReferenceRange != null && flagForHighReferenceRange!= null){
this.flagForHighReferenceRange = flagForHighReferenceRange;
this.flagForLowReferenceRange = flagForLowReferenceRange;
highTransformedReferenceValue = transformValueInRange(highRefrenceValue, flagForHighReferenceRange??"");
lowTransformedReferenceValue = transformValueInRange(lowRefenceValue, flagForLowReferenceRange??"");
this.highRefrenceValue = highRefrenceValue;
this.lowRefenceValue = lowRefenceValue;
}
LabResult recentResult = recentThree.first;
checkIfGraphShouldBeDisplayed(recentResult);
recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));

@ -250,4 +250,7 @@ class TestDetails {
data['CreatedOn'] = this.createdOn;
return data;
}
@override
String toString() { return description??"";}
}

@ -0,0 +1,235 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart' show AnimationConfiguration, SlideAnimation, FadeInAnimation;
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/debouncer.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'lab_order_by_test.dart';
class AlphabeticScroll extends StatefulWidget{
final List<String> alpahbetsAvailable;
final List<TestDetails> details;
final AppState appState;
final LabViewModel labViewModel;
final DateRangeSelectorRangeViewModel rangeViewModel;
const AlphabeticScroll({super.key, required this.alpahbetsAvailable, required this.details, required this.appState, required this.labViewModel, required this.rangeViewModel});
@override
State<AlphabeticScroll> createState() => _AlphabetScrollPageState();
}
class _AlphabetScrollPageState extends State<AlphabeticScroll> {
final ItemScrollController itemScrollController = ItemScrollController();
final ScrollOffsetController scrollOffsetController = ScrollOffsetController();
final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
final ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create();
final ScrollController _scrollController = ScrollController();
Map<String, List<TestDetails>> data = {};
Map<String, num> density = {};
Map<String, double> _offsetMap = {};
Map<String, GlobalKey> _offsetKeys = {};
int _activeIndex = 0; // <-- Highlighted letter
@override
void initState() {
super.initState();
scheduleMicrotask((){
for(var char in widget.alpahbetsAvailable){
data[char] = widget.details.where((element)=>element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList();
}
setState((){});
});
itemPositionsListener.itemPositions.addListener((){
final positions = itemPositionsListener.itemPositions.value;
if (positions.isEmpty) return;
// Get FIRST visible item (top-most)
final firstVisible = positions
.where((p) => p.itemTrailingEdge > 0) // visible
.reduce((min, p) =>
p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
if(_activeIndex == firstVisible.index) return ;
setState(() {
_activeIndex = firstVisible.index;
});
print("Active index = $_activeIndex");
});
}
@override
void dispose() {
itemPositionsListener.itemPositions.removeListener((){
});
super.dispose();
}
void _scrollToLetter(String letter) async {
// itemScrollController.jumpTo(index:density[letter]?.toInt()??0, );
itemScrollController.scrollTo(
index: data.keys.toList().indexOf(letter).toInt()??0,
duration: Duration(seconds: 2),
curve: Curves.easeInOutCubic);
// final key = _offsetKeys[letter];
// if (key == null) return;
//
// BuildContext? ctx = key.currentContext;
//
// // Retry until built (max 100ms)
// int retry = 0;
// while (ctx == null && retry < 5) {
// await Future.delayed(Duration(milliseconds: 20));
// ctx = key.currentContext;
// retry++;
// }
//
// if (ctx == null) {
// print("$letter still not built");
// return;
// }
//
// final renderBox = ctx.findRenderObject() as RenderBox;
// final yOffset = renderBox.localToGlobal(Offset.zero).dy;
//
// _scrollController.animateTo(
// _scrollController.offset + yOffset - 80,
// duration: Duration(milliseconds: 400),
// curve: Curves.easeInOut,
// );
}
@override
Widget build(BuildContext context) {
return
SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Add this
children: [
Expanded(
child: Column(
children: [
SizedBox(
height: (MediaQuery.sizeOf(context).height),
child: _buildList(widget.alpahbetsAvailable)
),
],
),
),
SizedBox(
width: 24.w,
height: MediaQuery.sizeOf(context).height-(70.h+ kToolbarHeight+100.h),
child: Column(
mainAxisSize: MainAxisSize.max, // Add this
mainAxisAlignment: MainAxisAlignment.center, // Changed from center to start
crossAxisAlignment: CrossAxisAlignment.center,
children:List.generate(widget.alpahbetsAvailable.length, (i) {
final isActive = (i == _activeIndex);
return GestureDetector(
onTap: () {
setState(() => _activeIndex = i);
_scrollToLetter(widget.alpahbetsAvailable[i]);
},
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 1.0, end: isActive ? 1.8 : 1.0),
duration: Duration(milliseconds: 120),
curve: Curves.easeOut,
builder: (_, scale, child) {
return Transform.scale(
scale: scale,
child: Opacity(
opacity: isActive ? 1.0 : 0.5,
child: widget.alpahbetsAvailable[i].toUpperCase().toText14(
color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor
),
),
);
},
),
);
}
),
),
),
],
),
);
// );
}
Widget _buildList(List<String> alphabet) {
return ScrollablePositionedList.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemScrollController: itemScrollController,
scrollOffsetController: scrollOffsetController,
itemPositionsListener: itemPositionsListener,
scrollOffsetListener: scrollOffsetListener,
itemCount: data.length,
itemBuilder: (_, index) {
final letter = alphabet[index].toLowerCase();
print("the letter is $letter");
final items = data[letter]!;
return Container(
key: _offsetKeys[letter],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:
List.generate(items.length,(itemIndex)=>
AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: getIt<AppState>(),
onTap: () {
if (items[itemIndex].model != null) {
widget.rangeViewModel.flush();
widget.labViewModel.getPatientLabResult(items[itemIndex].model!, items[itemIndex].description!,
(widget.appState.isArabic() ? items[itemIndex].testDescriptionAr! : items[itemIndex].testDescriptionEn!));
}
},
tests: items[itemIndex],
index: itemIndex,
isExpanded: true)),
),
))
// ...items.indexed((item) =>
//
//
//
// )
),
);
},
);
}
}

@ -27,6 +27,7 @@ class LabOrderByTest extends StatelessWidget {
@override
build(BuildContext context) {
return AnimatedContainer(
key:key,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),

File diff suppressed because one or more lines are too long

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
@ -279,11 +280,37 @@ class LabResultDetails extends StatelessWidget {
dataPoints: labmodel.filteredGraphValues,
makeGraphBasedOnActualValue: true,
leftLabelReservedSize: 40,
showGridLines: true,
leftLabelInterval: getInterval(labmodel),
maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/2,
// maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/2,
maxY: (labmodel.maxY),
maxX: labmodel.filteredGraphValues.length.toDouble()-.75,
horizontalInterval: .1,
getDrawingHorizontalLine: (value){
print("the FLline transformed line is $value");
print("the labmodel.highTransformedReferenceValue is ${labmodel.highTransformedReferenceValue}");
print("thelabmodel.lowTransformedReferenceValue is ${labmodel.lowTransformedReferenceValue}");
if(value == labmodel.highRefrenceValue ||value== labmodel.highRefrenceValue) {
return FlLine(
color: AppColors.greyTextColor,
strokeWidth: 1,
// dashArray: [5, 5],
);
}
return FlLine(
color: Colors.transparent,
strokeWidth: 1,
// dashArray: [5, 5],
);;
},
leftLabelFormatter: (value) {
return leftLabels(value.toStringAsFixed(2));
// return leftLabels(value.toStringAsFixed(2));
if(value == labmodel.highTransformedReferenceValue)
return leftLabels("High".needTranslation);
if(value== labmodel.lowTransformedReferenceValue)
return leftLabels("Low".needTranslation);
// switch (value.toInt()) {
// case 10:
// return leftLabels("Critical Low".needTranslation);
@ -297,7 +324,7 @@ class LabResultDetails extends StatelessWidget {
// return leftLabels(
// "Critical High".needTranslation);
// default:
// return SizedBox.shrink();
return SizedBox.shrink();
// }
},
graphColor:graphColor ,
@ -347,14 +374,15 @@ class LabResultDetails extends StatelessWidget {
}
double? getInterval(LabViewModel labmodel) {
var maxX = labmodel.maxY;
if(maxX<1) return .5;
if(maxX >1 && maxX < 5) return 1;
if(maxX >5 && maxX < 10) return 5;
if(maxX >10 && maxX < 50) return 10;
if(maxX >50 && maxX < 100) return 20;
if(maxX >100 && maxX < 200) return 30;
return 50;
return .1;
// var maxX = labmodel.maxY;
// if(maxX<1) return .5;
// if(maxX >1 && maxX < 5) return 1;
// if(maxX >5 && maxX < 10) return 5;
// if(maxX >10 && maxX < 50) return 10;
// if(maxX >50 && maxX < 100) return 20;
// if(maxX >100 && maxX < 200) return 30;
// return 50;
}
Widget getLabDescription(BuildContext context) {

@ -0,0 +1,205 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import '../../core/dependencies.dart';
class CollapsingToolbar extends StatefulWidget {
final String title;
Widget child;
VoidCallback? search;
VoidCallback? report;
VoidCallback? logout;
VoidCallback? history;
VoidCallback? instructions;
VoidCallback? requests;
Widget? bottomChild;
Widget? trailing;
bool isClose;
bool isLeading;
CollapsingToolbar({
super.key,
required this.title,
required this.child,
this.search,
this.isClose = false,
this.bottomChild,
this.report,
this.logout,
this.history,
this.instructions,
this.requests,
this.isLeading = true,
this.trailing,
});
@override
State<CollapsingToolbar> createState() => _CollapsingToolbarState();
}
class _CollapsingToolbarState extends State<CollapsingToolbar> {
bool isCollapsed = false;
final ScrollController _controller = ScrollController();
double expandedHeight = 0;
double get maxCollapseOffset => expandedHeight - kToolbarHeight;
@override
void initState() {
super.initState();
_controller.addListener(() {
// If scrolling UP beyond collapsed point force stop
print("the height is $maxCollapseOffset");
if (_controller.offset > maxCollapseOffset) {
_controller.jumpTo(maxCollapseOffset);
}
});
}
@override
Widget build(BuildContext context) {
expandedHeight = MediaQuery.of(context).size.height * 0.11.h;
AppState appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body:
// Column(
// children: [
NestedScrollView(
controller: _controller,
floatHeaderSlivers: true,
physics: isCollapsed?NeverScrollableScrollPhysics():BouncingScrollPhysics(),
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
expandedHeight: MediaQuery.of(context).size.height * 0.11.h,
stretch: true,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light),
surfaceTintColor: Colors.transparent,
backgroundColor: AppColors.bgScaffoldColor,
leading: widget.isLeading
? Transform.flip(
flipX: appState.isArabic(),
child: IconButton(
icon: Utils.buildSvgWithAssets(icon: widget.isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h),
padding: EdgeInsets.only(left: 12),
onPressed: () => Navigator.pop(context),
highlightColor: Colors.transparent,
),
)
: SizedBox.shrink(),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final double maxHeight = 100.h;
final double minHeight = kToolbarHeight;
double t = (constraints.maxHeight - minHeight) / (maxHeight - minHeight);
t = t - 1;
if (t < 0.7) t = 0.7;
t = t.clamp(0.0, 1.0);
final double fontSize = lerpDouble(14, 18, t)!;
final double bottomPadding = lerpDouble(0, 0, t)!;
final double leftPadding = lerpDouble(150, 24, t)!;
return Stack(
children: [
Align(
alignment: Alignment.lerp(
Alignment.center,
Alignment.bottomLeft,
t,
)!,
child: Padding(
padding: EdgeInsets.only(left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
child: Row(
spacing: 4.h,
children: [
Text(
widget.title,
maxLines: 1,
style: TextStyle(
fontSize: (27 - (5 * (2 - t))).f,
fontWeight: FontWeight.lerp(
FontWeight.w300,
FontWeight.w600,
t,
)!,
color: AppColors.blackColor,
letterSpacing: -0.5),
).expanded,
if (widget.logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(widget.logout!),
if (widget.report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(widget.report!),
if (widget.history != null) actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(widget.history!),
if (widget.instructions != null) actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(widget.instructions!),
if (widget.requests != null) actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(widget.requests!),
if (widget.search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(widget.search!).paddingOnly(right: 24),
if (widget.trailing != null) widget.trailing!,
],
)),
),
],
);
},
),
),
];
},
body: widget.child,
),
// ],
// ),
);
}
Widget actionButton(BuildContext context, double t, {required String title, required String icon}) {
return AnimatedSize(
duration: Duration(milliseconds: 150),
child: Container(
height: 40.h,
padding: EdgeInsets.all(8.w),
margin: EdgeInsets.only(right: 24.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.secondaryLightRedColor,
borderRadius: 10.r,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
spacing: 8.h,
children: [
Utils.buildSvgWithAssets(icon: icon, iconColor: AppColors.primaryRedColor),
if (t == 1)
Text(
title,
style: context.dynamicTextStyle(
color: AppColors.primaryRedColor,
letterSpacing: -0.4,
fontSize: (14 - (2 * (1 - t))).f,
fontWeight: FontWeight.lerp(
FontWeight.w300,
FontWeight.w500,
t,
)!,
),
),
],
),
),
);
}
}

@ -56,6 +56,9 @@ class CustomGraph extends StatelessWidget {
final FontWeight? bottomLabelFontWeight;
final double? leftLabelInterval;
final double? leftLabelReservedSize;
final bool? showGridLines;
final GetDrawingGridLine? getDrawingHorizontalLine;
final double? horizontalInterval;
///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) leftLabelFormatter;
@ -89,13 +92,17 @@ class CustomGraph extends StatelessWidget {
this.leftLabelReservedSize,
this.makeGraphBasedOnActualValue = false,
required this.bottomLabelFormatter,
this.minX,
this.minX,
this.showGridLines = false,
this.getDrawingHorizontalLine,
this.horizontalInterval,
});
@override
Widget build(BuildContext context) {
// var maxY = 0.0;
double interval = 20;
print("the maxY is $maxY");
return Material(
color: Colors.white,
@ -190,12 +197,12 @@ class CustomGraph extends StatelessWidget {
),
lineBarsData: _buildColoredLineSegments(dataPoints),
gridData: FlGridData(
show: true,
show: showGridLines??true,
drawVerticalLine: false,
// horizontalInterval: 40,
checkToShowHorizontalLine: (value) =>
value >= 0 && value <= 100,
getDrawingHorizontalLine: (value) {
horizontalInterval:horizontalInterval,
// checkToShowHorizontalLine: (value) =>
// value >= 0 && value <= 100,
getDrawingHorizontalLine: getDrawingHorizontalLine??(value) {
return FlLine(
color: graphGridColor,
strokeWidth: 1,

@ -91,6 +91,8 @@ dependencies:
url: https://github.com/fleoparra/hms-flutter-plugin.git
path: flutter-hms-map
scrollable_positioned_list: ^0.3.8
dev_dependencies:
flutter_test:
sdk: flutter

Loading…
Cancel
Save