platformViewLink method

Widget platformViewLink({
  1. dynamic onSafetyDetection(
    1. dynamic
    )?,
  2. dynamic onDetectionComplete(
    1. dynamic
    )?,
  3. dynamic onBoundingBox(
    1. dynamic
    )?,
  4. dynamic fps(
    1. dynamic
    )?,
  5. dynamic error(
    1. dynamic
    )?,
  6. dynamic cameraStatus(
    1. dynamic
    )?,
  7. dynamic loading(
    1. dynamic
    )?,
  8. dynamic disposed(
    1. dynamic
    )?,
  9. dynamic filterUpdated(
    1. dynamic
    )?,
  10. dynamic cameraSwitch(
    1. dynamic
    )?,
  11. Widget? onLoadingWidget,
  12. Widget? faceMaskOverlay,
  13. required Widget builder(
    1. Widget,
    2. FlutterSafetyPlatformCameraController
    ),
  14. Widget customPermissionBuilder(
    1. Future (
      1. BuildContext
      )
    )?,
  15. Map<String, dynamic> creationParams = const {},
  16. required String viewType,
})

Creates a platform view link widget with various callback functions for handling different events and optional widgets for loading and face mask overlay.

The following parameters are available:

  • onIdentify: Callback function triggered when identification occurs.
  • onPercentageChanged: Callback function triggered when the percentage changes.
  • onPushing: Callback function triggered when pushing occurs.
  • onCompleted: Callback function triggered when the process is completed.
  • onFailed: Callback function triggered when the process fails.
  • onCountdownStarted: Callback function triggered when the countdown starts.
  • onLiveness: Callback function triggered for liveness detection events.
  • onFps: Callback function triggered for FPS (frames per second) updates.
  • onLoadingWidget: Optional widget to display while loading.
  • faceMaskOverlay: Optional widget to display as a face mask overlay.
  • required Widget Function: A required function that returns a widget.

Implementation

Widget platformViewLink({
  Function(dynamic)? onSafetyDetection,
  Function(dynamic)? onDetectionComplete,
  Function(dynamic)? onBoundingBox,
  Function(dynamic)? fps,
  Function(dynamic)? error,
  Function(dynamic)? cameraStatus,
  Function(dynamic)? loading,
  Function(dynamic)? disposed,
  Function(dynamic)? filterUpdated,
  Function(dynamic)? cameraSwitch,
  Widget? onLoadingWidget,
  Widget? faceMaskOverlay,
  required Widget Function(
    Widget,
    FlutterSafetyPlatformCameraController,
  ) builder,
  Widget Function(
    Future<dynamic> Function(BuildContext),
  )? customPermissionBuilder,
  Map<String, dynamic> creationParams = const {},
  required String viewType,
}) {
  MethodChannel methodChannel = MethodChannel('${viewType}_method_channel');

  // Register all callbacks
  registerCallback("error", error ?? (_) {});
  registerCallback("fps", fps ?? (_) {});
  registerCallback("detectionResult", onSafetyDetection ?? (_) {});
  registerCallback("boundingBoxes", onBoundingBox ?? (_) {});
  registerCallback("detectionComplete", onDetectionComplete ?? (_) {});
  registerCallback("cameraStatus", cameraStatus ?? (_) {});
  registerCallback("loading", loading ?? (_) {});
  registerCallback("disposed", disposed ?? (_) {});
  registerCallback("filterUpdated", filterUpdated ?? (_) {});
  registerCallback("cameraSwitch", cameraSwitch ?? (_) {});

  final isLoadingValueNotifier = ValueNotifier<bool>(true);
  registerCallback("loading", (isLoading) {
    isLoadingValueNotifier.value = isLoading as bool;
  });

  final isDetachedValueNotifier = ValueNotifier<bool>(false);

  return FutureBuilder<bool>(
    future: Permission.camera.status.isGranted,
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return SizedBox.expand(
          child: Container(color: Colors.black),
        );
      }
      if (snapshot.data == true) {
        final widgetPlatformViewLink = PlatformViewLink(
          viewType: viewType,
          surfaceFactory: (
            BuildContext context,
            PlatformViewController controller,
          ) {
            return ValueListenableBuilder(
                valueListenable: isDetachedValueNotifier,
                builder: (context, isDetached, _) {
                  return ValueListenableBuilder(
                    valueListenable: isLoadingValueNotifier,
                    builder: (context, isLoading, child) {
                      return Stack(
                        children: [
                          isDetached
                              ? const SizedBox.shrink()
                              : PlatformViewSurface(
                                  controller: controller,
                                  gestureRecognizers: const <Factory<
                                      OneSequenceGestureRecognizer>>{},
                                  hitTestBehavior:
                                      PlatformViewHitTestBehavior.opaque,
                                ),
                          faceMaskOverlay ?? const SizedBox.shrink(),
                          isLoading || isDetached
                              ? onLoadingWidget ??
                                  const Center(
                                      child: CircularProgressIndicator())
                              : const SizedBox.shrink(),
                        ],
                      );
                    },
                  );
                });
          },
          onCreatePlatformView: (PlatformViewCreationParams params) {
            return PlatformViewsService.initExpensiveAndroidView(
              id: params.id,
              viewType: viewType,
              layoutDirection: TextDirection.ltr,
              creationParams: creationParams,
              creationParamsCodec: const StandardMessageCodec(),
              onFocus: () {
                params.onFocusChanged(true);
              },
            )
              ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
              ..create();
          },
        );

        return builder(
            widgetPlatformViewLink,
            FlutterSafetyPlatformCameraController(
              setLivenessThreshold: ({double? threshold}) async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                try {
                  return await methodChannel
                      .invokeMethod('setLivenessThreshold', {
                    'threshold': threshold,
                  });
                } catch (e) {
                  debugPrint('Error setting liveness threshold: $e');
                  return null;
                }
              },
              closeAndPop: () async {
                if (isDetachedValueNotifier.value) {
                  return false;
                }
                isDetachedValueNotifier.value = true;
                try {
                  await methodChannel.invokeMethod('detach');
                  await Future.delayed(const Duration(seconds: 1));
                  return true;
                } catch (e) {
                  debugPrint('Error detaching camera: $e');
                  return false;
                }
              },
              switchCamera: () async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                try {
                  return await methodChannel.invokeMethod('switchCamera');
                } catch (e) {
                  debugPrint('Error switching camera: $e');
                  return null;
                }
              },
              updateFilters: (Map<String, dynamic> filters) async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                try {
                  return await methodChannel.invokeMethod(
                      'updateFilters', filters);
                } catch (e) {
                  debugPrint('Error updating filters: $e');
                  return null;
                }
              },
            ));
      } else {
        Future<dynamic> requestPermission(BuildContext context) async {
          final status = await Permission.camera.request();
          if (status.isGranted) {
            (context as Element).markNeedsBuild();
          }
          return status;
        }

        if (customPermissionBuilder != null) {
          return customPermissionBuilder(requestPermission);
        } else {
          return defaultPermissionUI(context, requestPermission);
        }
      }
    },
  );
}