Merge branch 'dev_v3.13.6_vital_sign' of http://34.17.52.180/Haroon6138/diplomatic-quarter into dev_v3.13.6_vital_sign
commit
b1bf7032e5
Binary file not shown.
|
After Width: | Height: | Size: 520 B |
Binary file not shown.
|
After Width: | Height: | Size: 428 B |
@ -0,0 +1,28 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BackHomeButton extends StatelessWidget {
|
||||||
|
final void Function() onPressed;
|
||||||
|
final Size deviceSize;
|
||||||
|
|
||||||
|
const BackHomeButton({
|
||||||
|
Key? key,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.deviceSize,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(15, deviceSize.height * 0.05, 0, 0),
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
color: Colors.white,
|
||||||
|
iconSize: 25,
|
||||||
|
onPressed: onPressed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
||||||
|
|
||||||
|
class BoundingBoxWidget extends StatelessWidget {
|
||||||
|
final Size deviceSize;
|
||||||
|
final NormalizedFaceBox? facebox;
|
||||||
|
final VideoFrameInfo? videoFrameInfo;
|
||||||
|
|
||||||
|
const BoundingBoxWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.deviceSize,
|
||||||
|
required this.facebox,
|
||||||
|
required this.videoFrameInfo,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (facebox == null || videoFrameInfo == null) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a map with left, top, width, and height of the bounding box for the face
|
||||||
|
Map boundingBox = computeBoundingBox();
|
||||||
|
|
||||||
|
return Positioned(
|
||||||
|
left: boundingBox['left'],
|
||||||
|
top: boundingBox['top'],
|
||||||
|
child: Container(
|
||||||
|
width: boundingBox['width'],
|
||||||
|
height: boundingBox['height'],
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.red,
|
||||||
|
width: 2,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to compute the left, top, width, and height of the bounding box
|
||||||
|
Map computeBoundingBox() {
|
||||||
|
double width = videoFrameInfo!.width;
|
||||||
|
double height = videoFrameInfo!.height;
|
||||||
|
double windowWidth = deviceSize.width;
|
||||||
|
double windowHeight = deviceSize.height;
|
||||||
|
|
||||||
|
double screenAspectRatio = deviceSize.aspectRatio;
|
||||||
|
double frameAspectRatio = width / height;
|
||||||
|
|
||||||
|
int xTransform = 1;
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
xTransform = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double scale = 1;
|
||||||
|
if (screenAspectRatio < frameAspectRatio) {
|
||||||
|
scale = windowHeight / height;
|
||||||
|
} else {
|
||||||
|
scale = windowWidth / width;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map scaledFrameBounds = {
|
||||||
|
'x': (windowWidth - scale * width) / 2,
|
||||||
|
'y': (windowHeight - scale * height) / 2,
|
||||||
|
'width': scale * width,
|
||||||
|
'height': scale * height,
|
||||||
|
};
|
||||||
|
|
||||||
|
double x = facebox!.xCenter - facebox!.width / 2;
|
||||||
|
double y = facebox!.yCenter - facebox!.height / 2;
|
||||||
|
|
||||||
|
if (xTransform < 0) {
|
||||||
|
x = 1 - x - facebox!.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map result = {
|
||||||
|
'left': scaledFrameBounds['x'] + x * scaledFrameBounds['width'],
|
||||||
|
'top': scaledFrameBounds['y'] + y * scaledFrameBounds['height'],
|
||||||
|
'width': facebox!.width * scaledFrameBounds['width'],
|
||||||
|
'height': facebox!.height * scaledFrameBounds['height'],
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Button extends StatelessWidget {
|
||||||
|
final void Function() onPressed;
|
||||||
|
final Size deviceSize;
|
||||||
|
final String title;
|
||||||
|
final Alignment alignment;
|
||||||
|
final EdgeInsets margin;
|
||||||
|
|
||||||
|
const Button({
|
||||||
|
Key? key,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.deviceSize,
|
||||||
|
required this.title,
|
||||||
|
required this.alignment,
|
||||||
|
required this.margin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: alignment,
|
||||||
|
child: Container(
|
||||||
|
margin: margin,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Error extends StatelessWidget {
|
||||||
|
final dynamic error;
|
||||||
|
final int? errorCode;
|
||||||
|
|
||||||
|
const Error({
|
||||||
|
Key? key,
|
||||||
|
this.error,
|
||||||
|
this.errorCode,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
"$error $errorCode",
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,222 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
||||||
|
import 'vital_sign_widget.dart';
|
||||||
|
|
||||||
|
class HealthResultWidget extends StatelessWidget {
|
||||||
|
final Size deviceSize;
|
||||||
|
final void Function() onTap;
|
||||||
|
final Health? healthResult;
|
||||||
|
|
||||||
|
late final VitalSign? _vitalSign;
|
||||||
|
late final HolisticAnalysis? _holisticAnalysis;
|
||||||
|
late final CardiovascularRisks? _cardiovascularRisks;
|
||||||
|
late final CovidRisk? _covidRisk;
|
||||||
|
late final ScanParameters? _scanParameters;
|
||||||
|
|
||||||
|
HealthResultWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.deviceSize,
|
||||||
|
required this.onTap,
|
||||||
|
required this.healthResult,
|
||||||
|
}) : super(key: key) {
|
||||||
|
_vitalSign = healthResult?.vitalSigns;
|
||||||
|
_holisticAnalysis = healthResult?.holisticHealth;
|
||||||
|
_cardiovascularRisks = healthResult?.risks?.cardiovascularRisks;
|
||||||
|
_covidRisk = healthResult?.risks?.covidRisk;
|
||||||
|
_scanParameters = healthResult?.scanParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Positioned(
|
||||||
|
top: deviceSize.height * 0.07,
|
||||||
|
left: deviceSize.width * 0.05,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: SizedBox(
|
||||||
|
width: deviceSize.width * 0.9,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black38,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Vital Signs",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Heart Rate",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.heartRate)),
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "SPO2",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.spo2)),
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "IBI",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.ibi)),
|
||||||
|
if (_vitalSign?.stress != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Stress",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.stress)),
|
||||||
|
if (_vitalSign?.respiratoryRate != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Respiratory Rate",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.vitalSigns.respiratoryRate)),
|
||||||
|
if (_vitalSign?.hrvRmssd != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "HRV RMSSD",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.hrvRmssd)),
|
||||||
|
if (_vitalSign?.hrvSdnn != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "HRV SDNN",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.hrvSdnn)),
|
||||||
|
if (_vitalSign?.temperature != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Temperature",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.temperature)),
|
||||||
|
if (_vitalSign?.bloodPressure != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Blood Pressure",
|
||||||
|
vitalSignValue:
|
||||||
|
healthResult?.vitalSigns.bloodPressure ??
|
||||||
|
""),
|
||||||
|
if (_vitalSign?.bloodPressureSystolic != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Blood Pressure Systolic",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.vitalSigns.bloodPressureSystolic)),
|
||||||
|
if (_vitalSign?.bloodPressureDiastolic != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Blood Pressure Diastolic",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.vitalSigns.bloodPressureDiastolic)),
|
||||||
|
if (_holisticAnalysis != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Holistic Analysis",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.bmi != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "BMI",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.holisticHealth?.bmi)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.generalWellness != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "General Wellness",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.holisticHealth?.generalWellness)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Cardiovascular Risks",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "General",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.cardiovascularRisks
|
||||||
|
?.generalRisk)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Congestive Heart Failure",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.cardiovascularRisks
|
||||||
|
?.congestiveHeartFailure)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Coronary Heart Disease",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.cardiovascularRisks
|
||||||
|
?.coronaryHeartDisease)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Intermittent Claudication",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.cardiovascularRisks
|
||||||
|
?.intermittentClaudication)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Stroke",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.cardiovascularRisks
|
||||||
|
?.stroke)),
|
||||||
|
if (_covidRisk != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Covid Risks",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_covidRisk != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Risk",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.risks?.covidRisk?.covidRisk)),
|
||||||
|
if (_scanParameters != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Scan Parameters",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_scanParameters != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
vitalSignName: "Signal Quality",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult
|
||||||
|
?.scanParameters?.signalQuality)),
|
||||||
|
])))),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatValueToTwoDp(double? value) {
|
||||||
|
return (value != null) ? value.toStringAsFixed(2) : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ScanCondition extends StatelessWidget {
|
||||||
|
final String scanConditionName;
|
||||||
|
final bool isConditionSatisfied;
|
||||||
|
|
||||||
|
const ScanCondition({
|
||||||
|
Key? key,
|
||||||
|
required this.scanConditionName, // condition
|
||||||
|
required this.isConditionSatisfied, // status
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 5, 0, 0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
(isConditionSatisfied)
|
||||||
|
? Image.asset('assets/images/checkmark.png', width: 14)
|
||||||
|
: Image.asset('assets/images/crossmark.png', width: 14),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
|
||||||
|
child: Text(
|
||||||
|
scanConditionName,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
||||||
|
import 'scan_condition.dart';
|
||||||
|
|
||||||
|
class ScanConditionChecklist extends StatelessWidget {
|
||||||
|
final ScanConditions conditions; // ScanCondition
|
||||||
|
|
||||||
|
const ScanConditionChecklist({
|
||||||
|
Key? key,
|
||||||
|
required this.deviceSize,
|
||||||
|
required this.conditions, // ScanCondition
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Size deviceSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Positioned(
|
||||||
|
top: deviceSize.height * 0.12,
|
||||||
|
left: deviceSize.width * 0.05,
|
||||||
|
child: SizedBox(
|
||||||
|
width: deviceSize.width * 0.9,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black38,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
"Scanning Conditions",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "Lighting",
|
||||||
|
isConditionSatisfied: conditions.lighting,
|
||||||
|
),
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "Distance",
|
||||||
|
isConditionSatisfied: conditions.distance,
|
||||||
|
),
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "Centered",
|
||||||
|
isConditionSatisfied: conditions.centered,
|
||||||
|
),
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "Movement",
|
||||||
|
isConditionSatisfied: conditions.movement,
|
||||||
|
),
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "FrameRate",
|
||||||
|
isConditionSatisfied: conditions.frameRate,
|
||||||
|
),
|
||||||
|
ScanCondition(
|
||||||
|
scanConditionName: "Server Ready",
|
||||||
|
isConditionSatisfied: conditions.serverReady,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
||||||
|
|
||||||
|
class ScanStatus extends StatelessWidget {
|
||||||
|
final GetHealthStage? stage;
|
||||||
|
final double? remainingTime;
|
||||||
|
|
||||||
|
const ScanStatus({
|
||||||
|
Key? key,
|
||||||
|
required this.stage,
|
||||||
|
required this.remainingTime,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
getRemainingTime(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
getScanStage(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(height: 100),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getScanStage() {
|
||||||
|
if (stage == GetHealthStage.waitingData) {
|
||||||
|
return 'Waiting Data...';
|
||||||
|
} else if (stage == GetHealthStage.collectingData) {
|
||||||
|
return 'Collecting Data...';
|
||||||
|
} else if (stage == GetHealthStage.analyzingData) {
|
||||||
|
return 'Analyzing Data...';
|
||||||
|
}
|
||||||
|
return ''; // idle
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRemainingTime() {
|
||||||
|
return (remainingTime != null && remainingTime != double.infinity)
|
||||||
|
? remainingTime!.toStringAsFixed(0)
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class StartButton extends StatelessWidget {
|
||||||
|
final void Function() onPressed;
|
||||||
|
final bool disabled;
|
||||||
|
|
||||||
|
const StartButton({
|
||||||
|
Key? key,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.disabled,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 0, 0, 50),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: (disabled) ? () {} : onPressed,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
fixedSize: const Size(100, 100),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
backgroundColor: (disabled) ? Colors.grey : Colors.orangeAccent,
|
||||||
|
elevation: 8,
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
"START",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class VitalSignWidget extends StatelessWidget {
|
||||||
|
final String vitalSignName;
|
||||||
|
final String vitalSignValue;
|
||||||
|
final Color? textColor;
|
||||||
|
|
||||||
|
const VitalSignWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.vitalSignName,
|
||||||
|
required this.vitalSignValue,
|
||||||
|
this.textColor,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 0, 0, 2),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
vitalSignName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: textColor ?? Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
vitalSignValue,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: textColor ?? Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
||||||
|
|
||||||
|
import 'components/vital_sign_widget.dart';
|
||||||
|
|
||||||
|
class ResultScreen extends StatelessWidget {
|
||||||
|
final Health? healthResult;
|
||||||
|
|
||||||
|
late final VitalSign? _vitalSign;
|
||||||
|
late final HolisticAnalysis? _holisticAnalysis;
|
||||||
|
late final CardiovascularRisks? _cardiovascularRisks;
|
||||||
|
late final CovidRisk? _covidRisk;
|
||||||
|
late final ScanParameters? _scanParameters;
|
||||||
|
|
||||||
|
ResultScreen({
|
||||||
|
Key? key,
|
||||||
|
required this.healthResult,
|
||||||
|
}) : super(key: key) {
|
||||||
|
_vitalSign = healthResult?.vitalSigns;
|
||||||
|
_holisticAnalysis = healthResult?.holisticHealth;
|
||||||
|
_cardiovascularRisks = healthResult?.risks?.cardiovascularRisks;
|
||||||
|
_covidRisk = healthResult?.risks?.covidRisk;
|
||||||
|
_scanParameters = healthResult?.scanParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final deviceSize = MediaQuery.of(context).size;
|
||||||
|
const textColor = Color.fromARGB(255, 0, 0, 0);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Result'),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: deviceSize.width * 0.9,
|
||||||
|
child: Container(
|
||||||
|
color: const Color.fromARGB(0, 255, 255, 255),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Vital Signs",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Heart Rate",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.heartRate)),
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "SPO2",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.spo2)),
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "IBI",
|
||||||
|
vitalSignValue:
|
||||||
|
formatValueToTwoDp(healthResult?.vitalSigns.ibi)),
|
||||||
|
if (_vitalSign?.stress != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Stress",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.stress)),
|
||||||
|
if (_vitalSign?.respiratoryRate != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Respiratory Rate",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.respiratoryRate)),
|
||||||
|
if (_vitalSign?.hrvRmssd != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "HRV RMSSD",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.hrvRmssd)),
|
||||||
|
if (_vitalSign?.hrvSdnn != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "HRV SDNN",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.hrvSdnn)),
|
||||||
|
if (_vitalSign?.temperature != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Temperature",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.vitalSigns.temperature)),
|
||||||
|
if (_vitalSign?.bloodPressure != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Blood Pressure",
|
||||||
|
vitalSignValue:
|
||||||
|
healthResult?.vitalSigns.bloodPressure ?? ""),
|
||||||
|
if (_vitalSign?.bloodPressureSystolic != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Blood Pressure Systolic",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.vitalSigns.bloodPressureSystolic)),
|
||||||
|
if (_vitalSign?.bloodPressureDiastolic != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Blood Pressure Diastolic",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.vitalSigns.bloodPressureDiastolic)),
|
||||||
|
if (_holisticAnalysis != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Holistic Analysis",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.bmi != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "BMI",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.holisticHealth?.bmi)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.generalWellness != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "General Wellness",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.holisticHealth?.generalWellness)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.absi != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "ABSI",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.holisticHealth?.absi)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.cardiacWorkload != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Cardiac Workload",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.holisticHealth?.cardiacWorkload)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.pulseRespiratoryQuotient != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Pulse Respiratory Quotient",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.holisticHealth?.pulseRespiratoryQuotient)),
|
||||||
|
if (_holisticAnalysis != null &&
|
||||||
|
_holisticAnalysis?.waistToHeightRatio != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Waist to Height Ratio",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.holisticHealth?.waistToHeightRatio)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Cardiovascular Risks",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "General",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.risks?.cardiovascularRisks?.generalRisk)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Congestive Heart Failure",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.risks
|
||||||
|
?.cardiovascularRisks
|
||||||
|
?.congestiveHeartFailure)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Coronary Heart Disease",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.risks
|
||||||
|
?.cardiovascularRisks
|
||||||
|
?.coronaryHeartDisease)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Intermittent Claudication",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.risks
|
||||||
|
?.cardiovascularRisks
|
||||||
|
?.intermittentClaudication)),
|
||||||
|
if (_cardiovascularRisks != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Stroke",
|
||||||
|
vitalSignValue: formatValueToTwoDp(healthResult
|
||||||
|
?.risks?.cardiovascularRisks?.stroke)),
|
||||||
|
if (_covidRisk != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Covid Risks",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_covidRisk != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Risk",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.risks?.covidRisk?.covidRisk)),
|
||||||
|
if (_scanParameters != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.fromLTRB(0, 25, 0, 10),
|
||||||
|
child: const Text(
|
||||||
|
"Scan Parameters",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: Colors.amber,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_scanParameters != null)
|
||||||
|
VitalSignWidget(
|
||||||
|
textColor: textColor,
|
||||||
|
vitalSignName: "Signal Quality",
|
||||||
|
vitalSignValue: formatValueToTwoDp(
|
||||||
|
healthResult?.scanParameters?.signalQuality)),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatValueToTwoDp(double? value) {
|
||||||
|
return (value != null) ? value.toStringAsFixed(2) : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue