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 createState() => _VitalSignsState(); } class _VitalSignsState extends State with RouteAware { late final VitalSignCameraController _vitalSignCameraController; late Future cameraDevice; @override void initState() { super.initState(); cameraDevice = getFrontCamera(); // Wakelock.enable(); // keep the screen awake } Future 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; } }