Compare commits
20 Commits
dev_v3.13.
...
dev_v3.13.
| Author | SHA1 | Date |
|---|---|---|
|
|
2824abcc87 | 7 months ago |
|
|
656d8dd6b4 | 7 months ago |
|
|
c09170b506 | 7 months ago |
|
|
5461d78a1b | 7 months ago |
|
|
e79c521ea9 | 8 months ago |
|
|
dbfe9663a5 | 9 months ago |
|
|
2c5ed4ffe7 | 1 year ago |
|
|
fa1bb291b9 | 1 year ago |
|
|
eba3ab2cf8 | 1 year ago |
|
|
eafb7f5f7b | 1 year ago |
|
|
636a67b720 | 1 year ago |
|
|
9047ebf723 | 1 year ago |
|
|
fa87e74b02 | 1 year ago |
|
|
4c4ae6caef | 1 year ago |
|
|
3d27fbdea8 | 1 year ago |
|
|
3d2e0a5442 | 1 year ago |
|
|
b74a433798 | 1 year ago |
|
|
0f0b541563 | 1 year ago |
|
|
45bc6a91a4 | 1 year ago |
|
|
da3e266989 | 1 year ago |
@ -1,7 +0,0 @@
|
||||
#Sun Sep 20 09:53:06 EEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
#distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
@ -1,17 +1,28 @@
|
||||
include ':app'
|
||||
include ':vital-sign-engine'
|
||||
project(':vital-sign-engine').projectDir = new File('/Users/mohamedmekawy/Documents/Work/DiplomaticQuarter_3.16/packages/vital_sign_camera/android/libs')
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version '8.1.0' apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
id "com.google.protobuf" version "0.9.1" apply false
|
||||
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
<svg height="1792" id="download" width="1792" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28H160q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19L403 621q-31-29-14-70 17-39 59-39h256V64q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 531 B |
@ -0,0 +1,5 @@
|
||||
<svg id="Download" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
class="color000000 svgShape"
|
||||
d="M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28H160q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19L403 621q-31-29-14-70 17-39 59-39h256V64q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z" fill="#ffffff"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,59 @@
|
||||
import 'package:diplomaticquarterapp/core/viewModels/medical/labs_view_model.dart';
|
||||
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/data_display/medical/LabResult/lab_result_graph.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FullScreenGraph extends StatelessWidget {
|
||||
final List<DataPoint> completeeGraphValues ;
|
||||
final List<ThresholdRange> threshold ;
|
||||
|
||||
const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
isShowAppBar: true,
|
||||
appBarTitle: TranslationBase.of(context).labResult,
|
||||
showNewAppBar: true,
|
||||
showNewAppBarTitle: true,
|
||||
backgroundColor: Color(0xffF8F8F8),
|
||||
body: RotatedBox(
|
||||
quarterTurns: 1,
|
||||
child: SizedBox(
|
||||
// width: MediaQuery.sizeOf(context).height,
|
||||
height: MediaQuery.sizeOf(context).width,
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
|
||||
child: DynamicResultChart(
|
||||
dataPoints: completeeGraphValues,
|
||||
thresholds: threshold,
|
||||
// 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// double getMax(List<DataPoint> dataPoints) {
|
||||
// double max = double.negativeInfinity;
|
||||
// for (var point in dataPoints) {
|
||||
// if (point.value > max) {
|
||||
// max = point.y;
|
||||
// }
|
||||
// }
|
||||
// return max;
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,321 @@
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DynamicResultChart extends StatelessWidget {
|
||||
final List<DataPoint> dataPoints;
|
||||
final List<ThresholdRange> thresholds;
|
||||
final double? width;
|
||||
final double height;
|
||||
final double? maxX;
|
||||
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});
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
final spots = dataPoints.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.value);
|
||||
}).toList();
|
||||
final widthPerPoint = 30.0; // Customize as needed
|
||||
final chartWidth = (dataPoints.length + 1) * widthPerPoint;
|
||||
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: minY,
|
||||
maxY: maxY,
|
||||
minX: dataPoints.first.labelValue-1,
|
||||
maxX: maxX,
|
||||
lineTouchData:
|
||||
LineTouchData(touchTooltipData: LineTouchTooltipData(
|
||||
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()];
|
||||
if(dataPoint.isStringResource){
|
||||
return LineTooltipItem(
|
||||
'${double.parse(dataPoint.actualValue).toStringAsFixed(2)}',
|
||||
const TextStyle(color: Colors.white),
|
||||
);
|
||||
}
|
||||
return LineTooltipItem(
|
||||
'${dataPoint.label} ${double.parse(dataPoint.actualValue).toStringAsFixed(2)}',
|
||||
const TextStyle(color: Colors.white),
|
||||
);
|
||||
}
|
||||
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, _) {
|
||||
// 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(
|
||||
actualValue,
|
||||
style: const TextStyle(fontSize: 10),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameSize: 60,
|
||||
sideTitles: SideTitles(
|
||||
showTitles: !isFullScreenGraph,
|
||||
reservedSize: 50,
|
||||
getTitlesWidget: (value, _) {
|
||||
if (value.toInt() >= 0 &&
|
||||
value.toInt() < dataPoints.length) {
|
||||
var label = dataPoints[value.toInt()].label;
|
||||
if (dataPoints[value.toInt()].isStringResource) {
|
||||
label = TranslationBase.of(context)
|
||||
.getTranslation(
|
||||
dataPoints[value.toInt()].label);
|
||||
}
|
||||
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsetsDirectional.only(top: 8.0,),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
interval: 1, // ensures 1:1 mapping with spots
|
||||
),
|
||||
),
|
||||
topTitles: AxisTitles(),
|
||||
rightTitles: AxisTitles(),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<LineChartBarData> _buildColoredLineSegments(
|
||||
List<DataPoint> dataPoints, List<ThresholdRange> thresholds) {
|
||||
List<LineChartBarData> segments = [];
|
||||
Color getColor(double value) {
|
||||
for (int i = thresholds.length - 1; i >= 0; i--) {
|
||||
if (value >= thresholds[i].value) {
|
||||
return thresholds[i].lineColor;
|
||||
}
|
||||
}
|
||||
return Colors.grey;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dataPoints.length - 1; i++) {
|
||||
final dp1 = dataPoints[i];
|
||||
final dp2 = dataPoints[i + 1];
|
||||
|
||||
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);
|
||||
|
||||
segments.add(LineChartBarData(
|
||||
spots: [spot1, spot2],
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
gradient: LinearGradient(
|
||||
colors: [color1, color2],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
dotData: FlDotData(show: false), // We'll handle dots separately
|
||||
));
|
||||
}
|
||||
|
||||
// Add dot markers separately so they are on top
|
||||
|
||||
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.value);
|
||||
}).toList();
|
||||
print("the spots are $allSpots");
|
||||
|
||||
var data = [
|
||||
...segments,
|
||||
LineChartBarData(
|
||||
spots: allSpots,
|
||||
isCurved: false,
|
||||
barWidth: 0, // invisible line
|
||||
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,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
];
|
||||
|
||||
var max = LineChartHelper.calculateMaxAxisValues(data).maxX;
|
||||
print("the maxX is the -------> $max");
|
||||
return data;
|
||||
}
|
||||
|
||||
List<HorizontalRangeAnnotation> _buildRangeShades(
|
||||
List<ThresholdRange> thresholds) {
|
||||
List<HorizontalRangeAnnotation> ranges = [];
|
||||
for (int i = 0; i < thresholds.length - 1; i++) {
|
||||
ranges.add(HorizontalRangeAnnotation(
|
||||
y1: thresholds[i].value,
|
||||
y2: thresholds[i + 1].value,
|
||||
color: thresholds[i].color,
|
||||
));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
final List<DataPoint> sampleData = [
|
||||
DataPoint(value: 1.4, label: 'Jan 2024', date: DateTime(2024, 1, 1)),
|
||||
DataPoint(value: 3.6, label: 'Feb 2024', date: DateTime(2024, 2, 1)),
|
||||
DataPoint(value: 1.96, label: 'This result', date: DateTime(2024, 3, 1)),
|
||||
];
|
||||
final List<ThresholdRange> thresholdLevels = [
|
||||
ThresholdRange(
|
||||
label: 'Critical Low',
|
||||
value: 1.4,
|
||||
color: Color(0xfff6e9e9),
|
||||
lineColor: Color(0xFFe9a2a4)),
|
||||
ThresholdRange(
|
||||
label: 'Low',
|
||||
value: 3.6,
|
||||
color: Color(0xFFf2fbf5),
|
||||
lineColor: Color(0xFFeecd94)),
|
||||
ThresholdRange(
|
||||
label: 'Normal',
|
||||
value: 5.96,
|
||||
color: Color(0xFFf2fbf5),
|
||||
lineColor: Color(0xFF5dc36b)),
|
||||
ThresholdRange(
|
||||
label: 'High',
|
||||
value: 7.15,
|
||||
color: Color(0xfff6e9e9),
|
||||
lineColor: Color(0xFFeecd94)),
|
||||
ThresholdRange(
|
||||
label: 'Critical High',
|
||||
value: 10.15,
|
||||
color: Color(0xfff6e9e9),
|
||||
lineColor: Color(0xFFe9a2a4)),
|
||||
];
|
||||
|
||||
class DataPoint {
|
||||
final double value;
|
||||
double labelValue;
|
||||
String label;
|
||||
String actualValue;
|
||||
final DateTime date;
|
||||
bool isStringResource;
|
||||
|
||||
DataPoint(
|
||||
{required this.value,
|
||||
required this.label,
|
||||
required this.date,
|
||||
this.actualValue = "",
|
||||
this.isStringResource = false,
|
||||
this.labelValue = 0.0});
|
||||
}
|
||||
|
||||
class ThresholdRange {
|
||||
final String label;
|
||||
final double value;
|
||||
final Color color;
|
||||
final Color lineColor;
|
||||
final String? actualValue;
|
||||
|
||||
ThresholdRange(
|
||||
{required this.label,
|
||||
required this.value,
|
||||
required this.color,
|
||||
required this.lineColor,
|
||||
this.actualValue});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThresholdRange(label: $label, value: $value, color: ${color.value.toRadixString(16)}, lineColor: ${lineColor.value.toRadixString(16)})';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
import 'package:diplomaticquarterapp/core/model/labs/lab_result.dart';
|
||||
import 'package:diplomaticquarterapp/core/model/labs/patient_lab_orders.dart';
|
||||
import 'package:diplomaticquarterapp/theme/colors.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/data_display/medical/LabResult/newUI/labWidgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:readmore/readmore.dart';
|
||||
|
||||
import '../FlowChartPage.dart';
|
||||
|
||||
class LabItem extends StatefulWidget {
|
||||
final LabResultList item;
|
||||
PatientLabOrders? patientLabOrders;
|
||||
|
||||
LabItem({super.key, required this.item, this.patientLabOrders});
|
||||
|
||||
@override
|
||||
State<LabItem> createState() => _LabItemState();
|
||||
}
|
||||
|
||||
class _LabItemState extends State<LabItem> {
|
||||
bool _isShowMoreGeneral = false;
|
||||
bool showBottomSheet = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (showBottomSheet) {
|
||||
print('the bottom sheet is showing');
|
||||
openFlowChart(context, '');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isShowMoreGeneral = !_isShowMoreGeneral;
|
||||
});
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.item.filterName ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 21,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xff2B353E),
|
||||
letterSpacing: -0.64,
|
||||
height: 25 / 16),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
_isShowMoreGeneral
|
||||
? Icons.keyboard_arrow_up
|
||||
: Icons.keyboard_arrow_down,
|
||||
color: Color(0xff2B353E),
|
||||
size: 26,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
ReadMoreText(
|
||||
widget.item.description ?? '',
|
||||
trimMode: TrimMode.Line,
|
||||
trimLines: 3,
|
||||
style: const TextStyle(color: Color(0xff575757), fontSize: 11),
|
||||
colorClickableText: CustomColors.accentColor,
|
||||
trimCollapsedText: '${TranslationBase.of(context).readMore}',
|
||||
trimExpandedText: ' show less',
|
||||
),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (_isShowMoreGeneral)
|
||||
...List.generate(widget.item.patientLabResultList?.length ?? 0,
|
||||
(index) {
|
||||
var data = widget.item.patientLabResultList?[index];
|
||||
return Column(
|
||||
children: [
|
||||
ItemResultCardWidgetWithParams(
|
||||
title: data?.description ?? '',
|
||||
subTitle: data?.resultValue ?? '',
|
||||
referenceRange: data?.referanceRange ?? '',
|
||||
percentage: data?.percentage ?? 0.0,
|
||||
note: data?.testShortDescription ?? '',
|
||||
source: "",
|
||||
buttonText: "",
|
||||
type: data?.calculatedResultFlag?.getType() ??
|
||||
ResultTypes.unknown,
|
||||
shouldShowResultBarAndGraph: data?.shouldShowResultBarAndGraph()??true,
|
||||
onButtonPressed: () {
|
||||
openFlowChart(context, data?.description ?? '');
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
);
|
||||
})
|
||||
// Expanded(
|
||||
// child: ListView.builder(
|
||||
// itemCount: widget.item.patientLabResultList?.length ?? 0,
|
||||
// itemBuilder: (context, index) {
|
||||
// var data = widget.item.patientLabResultList?[index];
|
||||
// Column(
|
||||
// children: [
|
||||
// EllipsisTextWithMore(
|
||||
// text: data?.packageShortDescription ?? '',
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: 8,
|
||||
// ),
|
||||
// ItemResultCardWidgetWithParams(
|
||||
// title: data?.description ?? '',
|
||||
// subTitle: data?.resultValue ?? '',
|
||||
// referenceRange: data?.referanceRange ?? '',
|
||||
// percentage: 20,
|
||||
// note: data?.testShortDescription ?? '',
|
||||
// source: "",
|
||||
// buttonText: "",
|
||||
// type: data?.calculatedResultFlag?.getType() ??
|
||||
// ResultTypes.unknown),
|
||||
// ],
|
||||
// );
|
||||
// }))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
openFlowChart(BuildContext context, String procedure) {
|
||||
print('openFlowChart: the bottom sheet is showing');
|
||||
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
scrollControlDisabledMaxHeightRatio: .75,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20), topRight: Radius.circular(20))),
|
||||
builder: (context) {
|
||||
print('builder: the bottom sheet is showing');
|
||||
|
||||
return FlowChartPage(
|
||||
filterName: procedure,
|
||||
patientLabOrder: widget.patientLabOrders,
|
||||
);
|
||||
}).then((value) {
|
||||
setState(() {
|
||||
showBottomSheet = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,543 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///@author
|
||||
///
|
||||
enum ResultTypes {
|
||||
lowCriticalLow,
|
||||
criticalLow,
|
||||
low,
|
||||
normal,
|
||||
high,
|
||||
criticalHigh,
|
||||
highCriticalHigh,
|
||||
IRR,
|
||||
unknown
|
||||
}
|
||||
|
||||
class ItemResultCardWidget extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const ItemResultCardWidget({
|
||||
Key? key,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, bottom: 12),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomResultProgressBar extends StatelessWidget {
|
||||
final num percentage;
|
||||
final String value;
|
||||
final ResultTypes type;
|
||||
|
||||
CustomResultProgressBar(
|
||||
{Key? key,
|
||||
required this.percentage,
|
||||
required this.value,
|
||||
required this.type})
|
||||
: super(key: key);
|
||||
|
||||
final GlobalKey lcl = GlobalKey();
|
||||
|
||||
final GlobalKey cl = GlobalKey();
|
||||
|
||||
final GlobalKey l = GlobalKey();
|
||||
|
||||
final GlobalKey n = GlobalKey();
|
||||
|
||||
final GlobalKey h = GlobalKey();
|
||||
|
||||
final GlobalKey ch = GlobalKey();
|
||||
|
||||
final GlobalKey hch = GlobalKey();
|
||||
|
||||
OverlayEntry? overlayEntry;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
final double totalWidth = constraints.maxWidth;
|
||||
final double spacing = 2;
|
||||
final double tooltipPosition = (totalWidth * (percentage / 100)).clamp(0, totalWidth - 50);
|
||||
return Stack(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
_buildResultBar(
|
||||
key: cl,
|
||||
flex: 3,
|
||||
color: Color(0xFFDE7676),
|
||||
title: TranslationBase.of(context).criticalLow,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: l,
|
||||
flex: 2,
|
||||
color: Color(0xFFEAB157),
|
||||
title: TranslationBase.of(context).low,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: n,
|
||||
flex: 5,
|
||||
color: Color(0xFF09AA28),
|
||||
title: TranslationBase.of(context).normal,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: h,
|
||||
flex: 2,
|
||||
color: Color(0xFFEAB157),
|
||||
title: TranslationBase.of(context).high,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: ch,
|
||||
flex: 3,
|
||||
color: Color(0xFFDE7676),
|
||||
title: TranslationBase.of(context).criticalHigh,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!percentage.isNegative)
|
||||
Positioned(
|
||||
left: tooltipPosition,
|
||||
top: 33,
|
||||
child: Column(
|
||||
children: [
|
||||
TextCloud(
|
||||
text: "${value}",
|
||||
color: getColorForResultType(type),
|
||||
width: 100,
|
||||
height: 20,
|
||||
padding: EdgeInsets.zero,
|
||||
axisDirection: AxisDirection.up,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Color getColorForResultType(ResultTypes type) {
|
||||
switch (type) {
|
||||
case ResultTypes.lowCriticalLow:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.criticalHigh:
|
||||
return Color(0xFFDE7676);
|
||||
case ResultTypes.low:
|
||||
case ResultTypes.high:
|
||||
return Color(0xFFEAB157);
|
||||
case ResultTypes.normal:
|
||||
return Color(0xFF09AA28);
|
||||
default:
|
||||
return Color(0xFF09AA28);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildResultBar({
|
||||
required int flex,
|
||||
required Color color,
|
||||
required String title,
|
||||
Key? key}) {
|
||||
return Expanded(
|
||||
key: key,
|
||||
flex: flex,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontFamily: 'Poppins',
|
||||
letterSpacing: -0.3,
|
||||
color: color,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Container(
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextCloud extends StatelessWidget {
|
||||
final String text; // write here box content
|
||||
final Color color; // Set box Color
|
||||
final EdgeInsets padding; // Set content padding
|
||||
final double width; // Box width
|
||||
final double height; // Box Height
|
||||
final AxisDirection axisDirection; // Set triangle location up,left,right,down
|
||||
final double locationOfArrow; // set between 0 and 1, If 0.5 is set triangle position will be centered
|
||||
const TextCloud({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.color = Colors.white,
|
||||
this.padding = const EdgeInsets.all(10),
|
||||
this.width = 200,
|
||||
this.height = 100,
|
||||
this.axisDirection = AxisDirection.down,
|
||||
this.locationOfArrow = 0.5,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Size arrowSize = const Size(12, 12);
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
width: width,
|
||||
height: height,
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(color: Colors.white, fontSize: 9, letterSpacing: -.3),
|
||||
)),
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
double angle = 0;
|
||||
switch (axisDirection) {
|
||||
case AxisDirection.left:
|
||||
angle = pi * -0.5;
|
||||
break;
|
||||
case AxisDirection.up:
|
||||
angle = pi * -2;
|
||||
break;
|
||||
case AxisDirection.right:
|
||||
angle = pi * 0.5;
|
||||
break;
|
||||
case AxisDirection.down:
|
||||
angle = pi;
|
||||
break;
|
||||
default:
|
||||
angle = 0;
|
||||
}
|
||||
return Positioned(
|
||||
left: axisDirection == AxisDirection.left
|
||||
? -arrowSize.width + 5
|
||||
: (axisDirection == AxisDirection.up || axisDirection == AxisDirection.down ? width * locationOfArrow - arrowSize.width / 2 : null),
|
||||
right: axisDirection == AxisDirection.right ? -arrowSize.width + 5 : null,
|
||||
top: axisDirection == AxisDirection.up
|
||||
? -arrowSize.width + 5
|
||||
: (axisDirection == AxisDirection.right || axisDirection == AxisDirection.left ? height * locationOfArrow - arrowSize.width / 2 : null),
|
||||
bottom: axisDirection == AxisDirection.down ? -arrowSize.width + 5 : null,
|
||||
child: Transform.rotate(
|
||||
angle: angle,
|
||||
child: CustomPaint(
|
||||
size: arrowSize,
|
||||
painter: ArrowPaint(color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrowPaint extends CustomPainter {
|
||||
final Color color;
|
||||
|
||||
ArrowPaint({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Path path_0 = Path();
|
||||
path_0.moveTo(size.width * 0.5745375, size.height * 0.06573708);
|
||||
path_0.lineTo(size.width * 0.9813667, size.height * 0.8794000);
|
||||
path_0.cubicTo(size.width * 1.001950, size.height * 0.9205625, size.width * 0.9852625, size.height * 0.9706208, size.width * 0.9441000, size.height * 0.9912000);
|
||||
path_0.cubicTo(size.width * 0.9325250, size.height * 0.9969875, size.width * 0.9197667, size.height, size.width * 0.9068292, size.height);
|
||||
path_0.lineTo(size.width * 0.09316958, size.height);
|
||||
path_0.cubicTo(size.width * 0.04714583, size.height, size.width * 0.009836208, size.height * 0.9626917, size.width * 0.009836208, size.height * 0.9166667);
|
||||
path_0.cubicTo(size.width * 0.009836208, size.height * 0.9037292, size.width * 0.01284829, size.height * 0.8909708, size.width * 0.01863392, size.height * 0.8794000);
|
||||
path_0.lineTo(size.width * 0.4254625, size.height * 0.06573708);
|
||||
path_0.cubicTo(size.width * 0.4460458, size.height * 0.02457225, size.width * 0.4961042, size.height * 0.007886875, size.width * 0.5372667, size.height * 0.02846929);
|
||||
path_0.cubicTo(size.width * 0.5533958, size.height * 0.03653296, size.width * 0.5664708, size.height * 0.04961000, size.width * 0.5745375, size.height * 0.06573708);
|
||||
path_0.close();
|
||||
|
||||
Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
|
||||
paint_0_fill.color = color;
|
||||
canvas.drawPath(path_0, paint_0_fill);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ItemResultCardWidgetWithParams extends StatelessWidget {
|
||||
final String title;
|
||||
final String subTitle;
|
||||
final String note;
|
||||
final String source;
|
||||
final String referenceRange;
|
||||
final num percentage;
|
||||
final String buttonText;
|
||||
final ResultTypes type;
|
||||
final bool isArabic;
|
||||
final bool shouldShowResultBarAndGraph;
|
||||
final Function()? onButtonPressed;
|
||||
|
||||
const ItemResultCardWidgetWithParams({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.subTitle,
|
||||
required this.referenceRange,
|
||||
required this.percentage,
|
||||
required this.note,
|
||||
required this.source,
|
||||
required this.buttonText,
|
||||
required this.type,
|
||||
this.onButtonPressed,
|
||||
this.isArabic = false,
|
||||
this.shouldShowResultBarAndGraph = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fontFamily = isArabic ? 'Cairo' : 'Poppins';
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 21),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||
border: BorderDirectional(
|
||||
start: BorderSide(
|
||||
color: getColorForResultType(type),
|
||||
width: MediaQuery.of(context).size.width * 0.015,
|
||||
),
|
||||
),
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
// Title Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xff2E303A),
|
||||
letterSpacing: -.64),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: shouldShowResultBarAndGraph,
|
||||
child: GestureDetector(
|
||||
onTap: onButtonPressed,
|
||||
child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).viewFlowChart, isLast: true, isCapitable: false),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ResultStatusWidget(type: type),
|
||||
SizedBox(width: 5),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
subTitle,
|
||||
style: TextStyle(fontSize: 14, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -0.56),
|
||||
),
|
||||
Text(
|
||||
"${TranslationBase.of(context).referenceRange} \n $referenceRange",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xff2E303A),
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Progress Bar
|
||||
Visibility(
|
||||
visible: shouldShowResultBarAndGraph,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 55,
|
||||
child: CustomResultProgressBar(
|
||||
percentage: percentage,
|
||||
value: subTitle,
|
||||
type: type,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Note Section
|
||||
if (note.isNotEmpty)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Divider(color: Color(0xFFEFEFEF), thickness: 1),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${TranslationBase.of(context).notes}:",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: const Color(0xFF2E303A),
|
||||
letterSpacing: -0.4,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
note,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: const Color(0xFF575757),
|
||||
letterSpacing: -0.4,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color getColorForResultType(ResultTypes type) {
|
||||
switch (type) {
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.criticalHigh:
|
||||
case ResultTypes.lowCriticalLow:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
return Color(0xFFDE7676);
|
||||
case ResultTypes.low:
|
||||
case ResultTypes.high:
|
||||
return Color(0xFFEAB157);
|
||||
case ResultTypes.normal:
|
||||
return Color(0xFF09AA28);
|
||||
//todo handle irr here
|
||||
default:
|
||||
return Color(0xFF09AA28);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResultStatusWidget extends StatelessWidget {
|
||||
final ResultTypes type;
|
||||
|
||||
const ResultStatusWidget({
|
||||
Key? key,
|
||||
required this.type,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color? color;
|
||||
IconData? icon;
|
||||
|
||||
switch (type) {
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.lowCriticalLow:
|
||||
color = const Color(0xFFDE7676);
|
||||
icon = Icons.arrow_circle_down;
|
||||
break;
|
||||
case ResultTypes.low:
|
||||
color = const Color(0xFFEAB157);
|
||||
icon = Icons.arrow_circle_down;
|
||||
break;
|
||||
case ResultTypes.criticalHigh:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
color = const Color(0xFFDE7676);
|
||||
icon = Icons.arrow_circle_up;
|
||||
break;
|
||||
case ResultTypes.high:
|
||||
color = const Color(0xFFEAB157);
|
||||
icon = Icons.arrow_circle_up;
|
||||
break;
|
||||
case ResultTypes.normal:
|
||||
color = const Color(0xFF09AA28);
|
||||
icon = Icons.check_circle;
|
||||
break;
|
||||
case ResultTypes.unknown:
|
||||
case ResultTypes.IRR:
|
||||
color = Color(0xFF09AA28);
|
||||
icon = Icons.check_circle;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the icon if defined, otherwise a placeholder widget
|
||||
return icon != null
|
||||
? Icon(
|
||||
icon,
|
||||
color: color,
|
||||
)
|
||||
: const SizedBox(); // Use SizedBox to keep the layout consistent
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue