platformViewLink method

Widget platformViewLink({
  1. dynamic onIdentify(
    1. dynamic
    )?,
  2. dynamic onPercentageChanged(
    1. dynamic
    )?,
  3. dynamic onPushing(
    1. dynamic
    )?,
  4. dynamic onCompleted(
    1. dynamic
    )?,
  5. dynamic onFailed(
    1. dynamic
    )?,
  6. dynamic onCountdownStarted(
    1. dynamic
    )?,
  7. dynamic onLiveness(
    1. dynamic
    )?,
  8. dynamic onFps(
    1. dynamic
    )?,
  9. Widget? onLoadingWidget,
  10. Widget? faceMaskOverlay,
  11. SmartfaceTransparentEllipseOverlay? rectInsideEllipseOverlay,
  12. dynamic onRectInsideEllipseChanged(
    1. bool
    )?,
  13. SmartfaceCameraPosition? cameraPosition = SmartfaceCameraPosition.front,
  14. required Widget builder(
    1. Widget,
    2. FlutterSmartfacePlatformCameraController
    ),
  15. Widget customPermissionBuilder(
    1. Future (
      1. BuildContext
      )
    )?,
  16. Map<String, dynamic> creationParams = const {},
  17. 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)? onIdentify,
  Function(dynamic)? onPercentageChanged,
  Function(dynamic)? onPushing,
  Function(dynamic)? onCompleted,
  Function(dynamic)? onFailed,
  Function(dynamic)? onCountdownStarted,
  Function(dynamic)? onLiveness,
  Function(dynamic)? onFps,
  Widget? onLoadingWidget,
  Widget? faceMaskOverlay,
  SmartfaceTransparentEllipseOverlay? rectInsideEllipseOverlay,
  Function(bool)? onRectInsideEllipseChanged,
  SmartfaceCameraPosition? cameraPosition = SmartfaceCameraPosition.front,
  required Widget Function(
    Widget,
    FlutterSmartfacePlatformCameraController,
  ) builder,
  Widget Function(
    Future<dynamic> Function(BuildContext),
  )? customPermissionBuilder,
  Map<String, dynamic> creationParams = const {},
  required String viewType,
}) {
  MethodChannel methodChannel = MethodChannel('${viewType}_method_channel');

  creationParams['cameraPosition'] =
      cameraPosition == SmartfaceCameraPosition.front ? 0 : 1;

  // Validate parameter usage
  assert(
    rectInsideEllipseOverlay == null || faceMaskOverlay == null,
    'Cannot use both rectInsideEllipseOverlay and faceMaskOverlay simultaneously. '
    'Use rectInsideEllipseOverlay for better integration.',
  );

  // Register callback for backward compatibility and optional additional handling
  registerCallback("onRectInsideEllipseChanged", (isInside) {
    onRectInsideEllipseChanged?.call(isInside as bool);
    rectInsideEllipseOverlay?.updateFromPlatform(isInside as bool);
  });

  // If rectInsideEllipseOverlay is provided, merge its parameters
  if (rectInsideEllipseOverlay != null) {
    creationParams.addAll(rectInsideEllipseOverlay.creationParams);
  }

  registerCallback("onIdentify", onIdentify ?? (_) {});
  registerCallback("onPercentageChanged", onPercentageChanged ?? (_) {});
  registerCallback("onPushing", onPushing ?? (_) {});
  registerCallback("onCompleted", onCompleted ?? (_) {});
  registerCallback("onFailed", onFailed ?? (_) {});
  registerCallback("onCountdownStarted", onCountdownStarted ?? (_) {});
  registerCallback("onLiveness", onLiveness ?? (_) {});
  registerCallback("fps", onFps ?? (_) {});

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

  final isDetachedValueNotifier = ValueNotifier<bool>(false);

  // Use rectInsideEllipseOverlay if provided, otherwise use faceMaskOverlay
  final Widget? effectiveOverlay =
      rectInsideEllipseOverlay ?? faceMaskOverlay;

  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 = Platform.isIOS
            ? iosPlatformView(
                viewType,
                isDetachedValueNotifier,
                isLoadingValueNotifier,
                effectiveOverlay,
                onLoadingWidget,
                creationParams,
              )
            : androidPlatformView(
                viewType,
                isDetachedValueNotifier,
                isLoadingValueNotifier,
                effectiveOverlay,
                onLoadingWidget,
                creationParams,
              );
        return builder(
            widgetPlatformViewLink,
            FlutterSmartfacePlatformCameraController(
              setLivenessThreshold: (threshold) async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                await methodChannel.invokeMethod('setLivenessThreshold', {
                  'threshold': threshold,
                });
              },
              closeAndPop: () async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                isDetachedValueNotifier.value = true;
                await methodChannel.invokeMethod('detach');
                await Future.delayed(const Duration(seconds: 1));
              },
              switchCamera: () async {
                if (isDetachedValueNotifier.value) {
                  return;
                }
                return await methodChannel.invokeMethod('switchCamera');
              },
            ));
      } 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);
        }
      }
    },
  );
}