dev_v3.13.6_vital_sign
haroon amjad 1 year ago
parent ff7afe5997
commit a144d5fafe

@ -1,6 +1,6 @@
include ':app'
include ':vital-sign-engine'
project(':vital-sign-engine').projectDir = new File('C:\\Users\\taha.alam\\AndroidStudioProjects\\diplomatic-quarter\\packages\\vital_sign_camera\\android\\libs')
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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

@ -28,7 +28,8 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
# use_frameworks!
use_frameworks! :linkage => :static
use_modular_headers!
pod 'OpenTok', '~> 2.22.0'

@ -21,8 +21,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidamergeuat.cloudsolutions.com.sa/';

@ -230,10 +230,12 @@ class ServicesView extends StatelessWidget {
} else if (hmgServices.action == 1) {
openLiveCare(context);
} else if (hmgServices.action == 2) {
//todo for temporary basis
Navigator.push(context, FadePage(page: VitalSigns()));
// initPenguinSDK();
// if (projectViewModel.isIndoorNavigationEnabled) {
// if (!isLocked) openNavigationProjectSelection(context);
// } else {

@ -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) : "";
}
}

@ -1,14 +1,40 @@
import 'package:diplomaticquarterapp/vital_signs/result_screen.dart';
import 'package:flutter/material.dart';
import 'package:vital_sign_camera/vital_sign_camera.dart';
// import 'package:wakelock/wakelock.dart';
import 'components/bounding_box_widget.dart';
import 'components/error.dart';
import 'components/scan_condition_checklist.dart';
import 'components/scan_status.dart';
import 'components/start_button.dart';
import 'components/back_button.dart';
import 'components/button.dart';
// import 'result_screen.dart';
// import 'main.dart';
final UserInfo userInfo = UserInfo(
age: 30,
gender: Gender.male,
weight: 60,
// kg, Optional
height: 170,
// cm, Optional
waistCircumference: 71,
// cm Optional
userId: 'dbd13e86-47f4-4a43-85f6-cf62fa750117');
final VitalSignCameraConfig config = VitalSignCameraConfig(apiKey: 'nIsZO45woSXSfIxsL1t79MWeIpsnGQr6B941MSF2', serverId: ServerId.awsEnterpriseProd);
class VitalSigns extends StatefulWidget {
const VitalSigns({super.key});
@override
State<VitalSigns> createState() => _VitalSignState();
State<VitalSigns> createState() => _VitalSignsState();
}
class _VitalSignState extends State<VitalSigns> {
class _VitalSignsState extends State<VitalSigns> with RouteAware {
late final VitalSignCameraController _vitalSignCameraController;
late Future<CameraDevice?> cameraDevice;
@ -16,10 +42,9 @@ class _VitalSignState extends State<VitalSigns> {
void initState() {
super.initState();
cameraDevice = getFrontCamera();
// Wakelock.enable(); // keep the screen awake
}
double? _heartRate;
Future<CameraDevice?> getFrontCamera() async {
if (CameraPermissionStatus.authorized != await requestCameraPermission()) {
return null;
@ -27,44 +52,162 @@ class _VitalSignState extends State<VitalSigns> {
return queryCameraDevice(CameraPosition.front);
}
bool startedScanning = false; // set true when start button is pressed, set false when health result is tapped or error occurs
bool isAllConditionsMet = false; // check for the 6 scan conditions before enabling the start button
ScanConditions? _conditions;
GetHealthStage? _scanningStage;
double? _remainingTime;
Health? _healthResult;
NormalizedFaceBox? _normalizedFaceBox;
VideoFrameInfo? _videoFrameInfo;
dynamic _error;
int? _errorCode;
bool isCameraActive = true;
bool _showingResultPage = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
// routeObserver.subscribe(this, ModalRoute.of(context)!);
}
@override
void dispose() {
// routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPushNext() {
setState(() {
_showingResultPage = true;
isCameraActive = false;
});
}
@override
void didPopNext() {
setState(() {
isCameraActive = true;
startedScanning = false;
_healthResult = null;
_showingResultPage = false;
});
}
@override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return Scaffold(
body: Stack(children: [
VitalSignCamera(
onCreated: _onVitalSignCameraCreated,
isActive: true,
userInfo: UserInfo(
age: 30, gender: Gender.male, userId: '__YOUR_USER_ID__'),
config: VitalSignCameraConfig(apiKey: '__YOUR_API_KEY__'),
device: cameraDevice,
onVideoFrameProcessed: _onVideoFrameProcessed),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_vitalSignCameraController.startScanning();
});
},
child: const Text('start')),
Text('Heart rate: $_heartRate'),
],
),
),
]),
body: Stack(
children: [
VitalSignCamera(onCreated: _onVitalSignCameraCreated, isActive: isCameraActive, userInfo: userInfo, config: config, device: cameraDevice, onVideoFrameProcessed: _onVideoFrameProcessed),
// A back button to navigate back to previous screen
if (_scanningStage == GetHealthStage.idle && !startedScanning)
BackHomeButton(
onPressed: () {
setState(() {
isCameraActive = false;
});
Navigator.pop(context);
},
deviceSize: deviceSize),
// A button to toggle isCameraActive
if (_scanningStage == GetHealthStage.idle && !startedScanning)
Button(
onPressed: () {
setState(() {
isCameraActive = !isCameraActive;
});
},
deviceSize: deviceSize,
title: "Toggle isCameraActive",
alignment: Alignment.topRight,
margin: EdgeInsets.fromLTRB(0, deviceSize.height * 0.05, deviceSize.width * 0.05, 0)),
if (!isCameraActive)
const Center(
child: Text(
"Camera is not active.",
textAlign: TextAlign.center,
))
else
Stack(children: [
if (_scanningStage == GetHealthStage.idle && isCameraActive) // show start button only when it is not scanning
StartButton(
onPressed: () {
setState(() {
_vitalSignCameraController.startScanning();
startedScanning = true;
_healthResult = null;
});
},
disabled: !isAllConditionsMet,
),
if (_conditions != null && _scanningStage == GetHealthStage.idle && !startedScanning) // show scan conditions checking only when it is not scanning or showing health result
ScanConditionChecklist(deviceSize: deviceSize, conditions: _conditions!),
if (_scanningStage != GetHealthStage.idle) // show remaining time count down during scan
ScanStatus(stage: _scanningStage, remainingTime: _remainingTime),
if (!startedScanning || _healthResult == null) // show bounding box before and during scan, but not when health result is shown
BoundingBoxWidget(deviceSize: deviceSize, facebox: _normalizedFaceBox, videoFrameInfo: _videoFrameInfo),
if (_error != null) Error(error: _error, errorCode: _errorCode),
])
],
),
);
}
void _onVideoFrameProcessed(VideoFrameProcessedEvent event) {
// setState(() {
// _heartRate = event.healthResult?.health?.vitalSigns.heartRate;
// });
if (_showingResultPage) {
return;
}
if (_healthResult == null && event.healthResult?.health != null && event.healthResult?.stage == GetHealthStage.analyzingData) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultScreen(
healthResult: _healthResult,
),
),
);
}
setState(() {
_conditions = event.scanConditions;
isAllConditionsMet = checkIfAllScanConditionsMet();
_scanningStage = event.healthResult?.stage;
_remainingTime = event.healthResult?.remainingTime;
_healthResult = event.healthResult?.health;
_normalizedFaceBox = event.faceBox;
_videoFrameInfo = event.videoFrameInfo;
_error = event.healthResult?.error;
_errorCode = event.healthResult?.errorCode;
if (_error != null) {
// when error occurs, reshow condition checklist & disabled start button
startedScanning = false;
isAllConditionsMet = false;
}
});
}
bool checkIfAllScanConditionsMet() {
if (_conditions?.centered == true &&
_conditions?.distance == true &&
_conditions?.frameRate == true &&
_conditions?.lighting == true &&
_conditions?.movement == true &&
_conditions?.serverReady == true) {
return true;
} else {
return false;
}
}
// load default
void _onVitalSignCameraCreated(VitalSignCameraController controller) {
_vitalSignCameraController = controller;
}
}
}

@ -0,0 +1 @@
TODO: Add your license here.

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

@ -1,89 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

@ -0,0 +1,19 @@
/// Represents the pixel format of a `Frame`.
/// * `v420`: 420 YpCbCr 8 Bi-Planar Video Range
/// * `f420`: 420 YpCbCr 8 Bi-Planar Full Range
/// * `x420`: 420 YpCbCr 10 Bi-Planar Video Range
enum PixelFormat { f420, v420, x420 }
PixelFormat pixelFormatFromString(String string) {
switch (string) {
case '420f':
return PixelFormat.f420;
case '420v':
return PixelFormat.v420;
case 'x420':
return PixelFormat.x420;
default:
// ignore: fixme
return PixelFormat.f420;
}
}

@ -0,0 +1,138 @@
/// The vital signs result of a scan.
class VitalSign {
final double heartRate;
final double spo2;
final double ibi;
final double? stress;
final double? stressScore;
final double? respiratoryRate;
final double? hrvSdnn;
final double? hrvRmssd;
final double? temperature;
final String? bloodPressure;
final double? bloodPressureSystolic;
final double? bloodPressureDiastolic;
final double? facialSkinAge;
final double? bloodAlcohol;
final double? bloodSugar;
final String? version;
const VitalSign(
this.heartRate,
this.spo2,
this.ibi,
this.stress,
this.stressScore,
this.respiratoryRate,
this.hrvSdnn,
this.hrvRmssd,
this.temperature,
this.bloodPressure,
this.bloodPressureSystolic,
this.bloodPressureDiastolic,
this.facialSkinAge,
this.bloodAlcohol,
this.bloodSugar,
this.version);
factory VitalSign.fromMap(Map map) {
return VitalSign(
map['heartRate'],
map['spo2'],
map['ibi'],
map['stress'],
map['stressScore'],
map['respiratoryRate'],
map['hrvSdnn'],
map['hrvRmssd'],
map['temperature'],
map['bloodPressure'],
map['bloodPressureSystolic'],
map['bloodPressureDiastolic'],
map['facialSkinAge'],
map['bloodAlcohol'],
map['bloodSugar'],
map['version']);
}
}
/// The cardiovascular risks result of a scan.
class CardiovascularRisks {
final double generalRisk;
final double coronaryHeartDisease;
final double congestiveHeartFailure;
final double intermittentClaudication;
final double stroke;
const CardiovascularRisks(this.generalRisk, this.coronaryHeartDisease,
this.congestiveHeartFailure, this.intermittentClaudication, this.stroke);
factory CardiovascularRisks.fromMap(Map map) {
return CardiovascularRisks(
map['generalRisk'],
map['coronaryHeartDisease'],
map['congestiveHeartFailure'],
map['intermittentClaudication'],
map['stroke']);
}
}
/// The covid risk result of a scan.
class CovidRisk {
final double covidRisk;
const CovidRisk(this.covidRisk);
factory CovidRisk.fromMap(Map map) {
return CovidRisk(map['covidRisk']);
}
}
/// The health risks result of a scan.
class HealthRisks {
final CardiovascularRisks? cardiovascularRisks;
final CovidRisk? covidRisk;
final String version;
const HealthRisks(this.cardiovascularRisks, this.covidRisk, this.version);
factory HealthRisks.fromMap(Map map) {
return HealthRisks(
map['cardiovascularRisks'] != null
? CardiovascularRisks.fromMap(map['cardiovascularRisks'])
: null,
map['covidRisk'] != null ? CovidRisk.fromMap(map['covidRisk']) : null,
map['version']);
}
}
/// The holistic analysis result of a scan.
class HolisticAnalysis {
final double? generalWellness;
final double? bmi;
final double? absi;
final double? cardiacWorkload;
final double? pulseRespiratoryQuotient;
final double? waistToHeightRatio;
final String? version;
const HolisticAnalysis(
this.generalWellness,
this.bmi,
this.absi,
this.cardiacWorkload,
this.pulseRespiratoryQuotient,
this.waistToHeightRatio,
this.version);
factory HolisticAnalysis.fromMap(Map map) {
return HolisticAnalysis(
map['generalWellness'],
map['bmi'],
map['absi'],
map['cardiacWorkload'],
map['pulseRespiratoryQuotient'],
map['waistToHeightRatio'],
map['version']);
}
}

@ -0,0 +1,42 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'camera_permission.dart';
import 'camera_device.dart';
import 'vital_sign_camera_method_channel.dart';
abstract class VitalSignCameraPlatform extends PlatformInterface {
/// Constructs a VitalSignCameraPlatform.
VitalSignCameraPlatform() : super(token: _token);
static final Object _token = Object();
static VitalSignCameraPlatform _instance = MethodChannelVitalSignCamera();
/// The default instance of [VitalSignCameraPlatform] to use.
///
/// Defaults to [MethodChannelVitalSignCamera].
static VitalSignCameraPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [VitalSignCameraPlatform] when
/// they register themselves.
static set instance(VitalSignCameraPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<List<CameraDevice>> availableCameraDevices() {
throw UnimplementedError(
'availableCameraDevices() has not been implemented.');
}
Future<CameraPermissionStatus> requestCameraPermission() {
throw UnimplementedError(
'availableCameraDevices() has not been implemented.');
}
Future<CameraPermissionStatus> getCameraPermissionStatus() {
throw UnimplementedError(
'getCameraPermissionStatus() has not been implemented.');
}
}
Loading…
Cancel
Save