You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
10 KiB
Dart
317 lines
10 KiB
Dart
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:vital_sign_camera/vital_sign_camera.dart';
|
|
|
|
// import 'package:wakelock/wakelock.dart';
|
|
|
|
import '../uitl/translations_delegate_base.dart';
|
|
import 'components/scan_condition_checklist.dart';
|
|
import 'components/scan_status.dart';
|
|
import 'components/start_button.dart';
|
|
import 'result_screen.dart';
|
|
|
|
final UserInfo userInfo = UserInfo(
|
|
age: 30,
|
|
gender: Gender.male,
|
|
weight: 75, // kg, Optional
|
|
height: 164, // cm, Optional
|
|
waistCircumference: 83, // 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() => _VitalSignsState();
|
|
}
|
|
|
|
class _VitalSignsState extends State<VitalSigns> with RouteAware {
|
|
late final VitalSignCameraController _vitalSignCameraController;
|
|
late Future<CameraDevice?> cameraDevice;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
cameraDevice = getFrontCamera();
|
|
// Wakelock.enable(); // keep the screen awake
|
|
}
|
|
|
|
Future<CameraDevice?> getFrontCamera() async {
|
|
if (CameraPermissionStatus.authorized != await requestCameraPermission()) {
|
|
return null;
|
|
}
|
|
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 _showedHealthResult = false;
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
// routeObserver.subscribe(this, ModalRoute.of(context)!);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
// routeObserver.unsubscribe(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didPushNext() {
|
|
setState(() {
|
|
isCameraActive = false;
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didPopNext() {
|
|
setState(() {
|
|
isCameraActive = true;
|
|
startedScanning = false;
|
|
_healthResult = null;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final deviceSize = MediaQuery.of(context).size;
|
|
return AppScaffold(
|
|
isShowAppBar: true,
|
|
appBarTitle: TranslationBase.of(context).scanning,
|
|
showNewAppBar: true,
|
|
showNewAppBarTitle: true,
|
|
overrideUserLogin: true,
|
|
backgroundColor: Color(0xffF8F8F8),
|
|
body: Column(
|
|
children: [
|
|
if (_conditions != null)
|
|
ScanConditionChecklist(
|
|
deviceSize: deviceSize, conditions: _conditions!),
|
|
Expanded(
|
|
child: Stack(
|
|
children: [
|
|
VitalSignCamera(
|
|
onCreated: _onVitalSignCameraCreated,
|
|
isActive: isCameraActive,
|
|
userInfo: userInfo,
|
|
config: config,
|
|
device: cameraDevice,
|
|
onVideoFrameProcessed: _onVideoFrameProcessed),
|
|
Positioned.fill(
|
|
// child: CustomPaint(
|
|
// painter: OvalOverlayPainter(),
|
|
// ),
|
|
child: SvgPicture.asset(
|
|
"assets/images/svg/overlay.svg",
|
|
),
|
|
),
|
|
if (_scanningStage !=
|
|
GetHealthStage
|
|
.idle) // show remaining time count down during scan
|
|
ScanStatus(
|
|
stage: _scanningStage, remainingTime: _remainingTime),
|
|
Column(children: [
|
|
ScanConditionChecklist(
|
|
deviceSize: deviceSize, conditions: _conditions!),
|
|
if (_scanningStage == GetHealthStage.idle &&
|
|
isCameraActive) // show start button only when it is not scanning
|
|
StartButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
_vitalSignCameraController.startScanning();
|
|
startedScanning = true;
|
|
_healthResult = null;
|
|
_showedHealthResult = false;
|
|
});
|
|
},
|
|
disabled: !isAllConditionsMet,
|
|
),
|
|
])
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
// return Scaffold(
|
|
// 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;
|
|
// _showedHealthResult = false;
|
|
// });
|
|
// },
|
|
// 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) {
|
|
if (!_showedHealthResult &&
|
|
_healthResult == null &&
|
|
event.healthResult?.health != null) {
|
|
_showedHealthResult = true;
|
|
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;
|
|
}
|
|
}
|
|
|
|
void _onVitalSignCameraCreated(VitalSignCameraController controller) {
|
|
_vitalSignCameraController = controller;
|
|
}
|
|
}
|
|
|
|
class OvalOverlayPainter extends CustomPainter {
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final Paint overlayPaint = Paint()
|
|
..color = Colors.white.withOpacity(0.7)
|
|
..style = PaintingStyle.fill;
|
|
|
|
canvas.drawRect(
|
|
Rect.fromLTWH(0, 0, size.width, size.height),
|
|
overlayPaint,
|
|
);
|
|
|
|
final Rect ovalRect = Rect.fromCenter(
|
|
center: Offset(size.width / 2, size.height / 2),
|
|
width: size.width * 0.9,
|
|
height: size.height * 0.7,
|
|
);
|
|
final Path ovalPath = Path()..addOval(ovalRect);
|
|
|
|
canvas.clipPath(ovalPath, doAntiAlias: true);
|
|
canvas.drawRect(
|
|
Rect.fromLTWH(0, 0, size.width, size.height),
|
|
Paint()..blendMode = BlendMode.clear,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
|
return false;
|
|
}
|
|
}
|