Face Recognition with Liveness Detection for Web Support

This commit is contained in:
2026-03-08 10:19:03 +08:00
parent d3da8901a4
commit a8751ca728
7 changed files with 513 additions and 196 deletions
+21 -10
View File
@@ -26,8 +26,9 @@ class FaceVerificationResult {
Future<FaceVerificationResult?> showFaceVerificationOverlay({
required BuildContext context,
required WidgetRef ref,
required String attendanceLogId,
String? attendanceLogId,
int maxAttempts = 3,
bool uploadAttendanceResult = true,
}) {
return Navigator.of(context).push<FaceVerificationResult>(
PageRouteBuilder(
@@ -35,6 +36,7 @@ Future<FaceVerificationResult?> showFaceVerificationOverlay({
pageBuilder: (ctx, anim, secAnim) => _FaceVerificationOverlay(
attendanceLogId: attendanceLogId,
maxAttempts: maxAttempts,
uploadAttendanceResult: uploadAttendanceResult,
),
transitionsBuilder: (ctx, anim, secAnim, child) {
return FadeTransition(opacity: anim, child: child);
@@ -49,10 +51,12 @@ class _FaceVerificationOverlay extends ConsumerStatefulWidget {
const _FaceVerificationOverlay({
required this.attendanceLogId,
required this.maxAttempts,
required this.uploadAttendanceResult,
});
final String attendanceLogId;
final String? attendanceLogId;
final int maxAttempts;
final bool uploadAttendanceResult;
@override
ConsumerState<_FaceVerificationOverlay> createState() =>
@@ -108,12 +112,14 @@ class _FaceVerificationOverlayState
if (result == null) {
if (!mounted) return;
// Cancelled: on web offer QR, otherwise mark cancelled
if (kIsWeb) {
if (kIsWeb &&
widget.uploadAttendanceResult &&
widget.attendanceLogId != null) {
final completed = await showQrVerificationDialog(
context: context,
ref: ref,
type: 'verification',
contextId: widget.attendanceLogId,
contextId: widget.attendanceLogId!,
);
if (mounted) {
Navigator.of(
@@ -213,11 +219,14 @@ class _FaceVerificationOverlayState
}
Future<void> _uploadResult(Uint8List bytes, String status) async {
if (!widget.uploadAttendanceResult || widget.attendanceLogId == null) {
return;
}
try {
await ref
.read(attendanceControllerProvider)
.uploadVerification(
attendanceId: widget.attendanceLogId,
attendanceId: widget.attendanceLogId!,
bytes: bytes,
fileName: 'verification.jpg',
status: status,
@@ -226,11 +235,13 @@ class _FaceVerificationOverlayState
}
Future<void> _skipVerification(String reason) async {
try {
await ref
.read(attendanceControllerProvider)
.skipVerification(widget.attendanceLogId);
} catch (_) {}
if (widget.uploadAttendanceResult && widget.attendanceLogId != null) {
try {
await ref
.read(attendanceControllerProvider)
.skipVerification(widget.attendanceLogId!);
} catch (_) {}
}
if (!mounted) return;
setState(() {
_phase = _Phase.cancelled;