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.
228 lines
7.6 KiB
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');
|
|
}
|
|
}
|