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.
diplomatic-quarter/packages/vital_sign_camera/lib/src/vital_sign_camera.dart

228 lines
7.6 KiB
Dart

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<List<CameraDevice>> availableCameraDevices() {
return VitalSignCameraPlatform.instance.availableCameraDevices();
}
/// Request the permission to access the camera.
Future<CameraPermissionStatus> requestCameraPermission() {
return VitalSignCameraPlatform.instance.requestCameraPermission();
}
/// Get the current status of the camera permission.
Future<CameraPermissionStatus> 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<CameraDevice?>? 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<VitalSignCamera> createState() => _VitalSignCameraState();
}
class _VitalSignCameraState extends State<VitalSignCamera> {
late Future<CameraDevice?> 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<CameraDevice?> 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<CameraDevice?>(
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 <Factory<
OneSequenceGestureRecognizer>>{},
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<void> startScanning() async {
return _channel.invokeMethod('startScanning');
}
Future<void> stopScanning() async {
return _channel.invokeMethod('stopScanning');
}
}