import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'camera_permission.dart'; import 'camera_position.dart'; import 'query_camera_device.dart'; import 'user_info.dart'; import 'video_frame_processed_event.dart'; import 'vital_sign_camera_config.dart'; import 'preview_layer_gravity.dart'; import 'camera_device.dart'; import 'vital_sign_camera_platform_interface.dart'; import 'vital_sign_camera_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Get the available camera devices supported by the hardware. Future> availableCameraDevices() { return VitalSignCameraPlatform.instance.availableCameraDevices(); } /// Request the permission to access the camera. Future requestCameraPermission() { return VitalSignCameraPlatform.instance.requestCameraPermission(); } /// Get the current status of the camera permission. Future getCameraPermissionStatus() { return VitalSignCameraPlatform.instance.getCameraPermissionStatus(); } typedef VitalSignCameraCreatedCallback = void Function( VitalSignCameraController controller); typedef OnVideoFrameProcessed = void Function(VideoFrameProcessedEvent event); /// The main component to perform vital sign scanning of a user. class VitalSignCamera extends StatefulWidget { /// The callback function to obtain an instance of [VitalSignCameraController], /// which is used to start a scan. final VitalSignCameraCreatedCallback? onCreated; /// To state wheather the component should be active or not. final bool isActive; /// The camera deivice obtained by the method [availableCameraDevices]. final Future? device; /// The information of the user doing the scan. final UserInfo userInfo; /// The gravity of the camera preview layer. final PreviewLayerGravity? previewLayerGravity; /// The configuration of the component. final VitalSignCameraConfig config; /// The callback function for obtaining the information of a video frame and /// the result of the scan such as the vital signs. /// This callback function is called at the rate up to 30Hz, the actual /// rate depends on the the hardware capability. final OnVideoFrameProcessed? onVideoFrameProcessed; const VitalSignCamera( {super.key, this.onCreated, required this.isActive, this.device, required this.userInfo, required this.config, this.previewLayerGravity, this.onVideoFrameProcessed}); @override State createState() => _VitalSignCameraState(); } class _VitalSignCameraState extends State { late Future cameraDevice; late MethodChannel channel; @override void initState() { super.initState(); if (widget.device == null) { cameraDevice = getFrontCamera(); } else { cameraDevice = widget.device!; } } @override void didUpdateWidget(VitalSignCamera oldWidget) { super.didUpdateWidget(oldWidget); if (widget.config != oldWidget.config || widget.userInfo != oldWidget.userInfo || widget.isActive != oldWidget.isActive) { channel.invokeMethod("configure", { 'isActive': widget.isActive, 'userInfo': widget.userInfo.map, 'config': widget.config.map, 'previewLayterGravity': widget.previewLayerGravity?.toInt(), }); } } Future getFrontCamera() async { var status = await getCameraPermissionStatus(); if (CameraPermissionStatus.authorized != status) { status = await requestCameraPermission(); } if (CameraPermissionStatus.authorized != status) { return null; } return queryCameraDevice(CameraPosition.front); } @override Widget build(BuildContext context) { return FutureBuilder( future: cameraDevice, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Container(); } if (!snapshot.hasData) { return const Center( child: Text( "Camera permission denied.", textAlign: TextAlign.center, )); } final creationParams = { 'isActive': widget.isActive, 'id': snapshot.data?.id, 'userInfo': widget.userInfo.map, 'config': widget.config.map, 'previewLayerGravity': widget.previewLayerGravity?.toInt(), }; switch (defaultTargetPlatform) { case TargetPlatform.android: return PlatformViewLink( viewType: 'ai.panoptic/flutter_vital_sign_camera', surfaceFactory: (context, controller) { return AndroidViewSurface( controller: controller as AndroidViewController, gestureRecognizers: const >{}, hitTestBehavior: PlatformViewHitTestBehavior.opaque, ); }, onCreatePlatformView: (params) { return PlatformViewsService.initExpensiveAndroidView( id: params.id, viewType: 'ai.panoptic/flutter_vital_sign_camera', layoutDirection: TextDirection.ltr, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), onFocus: () { params.onFocusChanged(true); }, ) ..addOnPlatformViewCreatedListener( (id) { params.onPlatformViewCreated(id); _onPlatformViewCreated(id); }, ) ..create(); }, ); case TargetPlatform.iOS: return UiKitView( viewType: 'ai.panoptic/flutter_vital_sign_camera', onPlatformViewCreated: _onPlatformViewCreated, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); default: return Text( '$defaultTargetPlatform is not yet supported by the web_view plugin'); } }); } // Callback method when platform view is created void _onPlatformViewCreated(int id) { final controller = VitalSignCameraController._(id); widget.onCreated?.call(controller); channel = MethodChannel('ai.panoptic/channel/flutter_vital_sign_camera_$id'); if (widget.onVideoFrameProcessed != null) { controller.videoFrameProcessedChannel .receiveBroadcastStream() .listen((event) { if (!widget.isActive) { return; } widget.onVideoFrameProcessed!(VideoFrameProcessedEvent.fromMap(event)); }); } } } // VitalSignCamera Controller class class VitalSignCameraController { VitalSignCameraController._(int id) : _channel = MethodChannel('ai.panoptic/channel/flutter_vital_sign_camera_$id'), videoFrameProcessedChannel = EventChannel( 'ai.panoptic/channel/flutter_vital_sign_camera_processed_frame_event_$id'); final MethodChannel _channel; final EventChannel videoFrameProcessedChannel; Future startScanning() async { return _channel.invokeMethod('startScanning'); } Future stopScanning() async { return _channel.invokeMethod('stopScanning'); } }