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
@@ -1511,8 +1511,9 @@ class _LogbookTab extends ConsumerWidget {
if (logScheduleIds.contains(s.id)) return false;
if (!s.endTime.isBefore(now)) return false;
if (s.startTime.isBefore(range.start) ||
!s.startTime.isBefore(range.end))
!s.startTime.isBefore(range.end)) {
return false;
}
// If the user rendered overtime on this calendar day, don't
// mark the normal/night shift schedule as absent.
final d = s.startTime;
+12 -17
View File
@@ -9,6 +9,7 @@ import '../../providers/profile_provider.dart';
import '../../providers/tickets_provider.dart';
import '../../providers/user_offices_provider.dart';
import '../../services/face_verification.dart' as face;
import '../../widgets/face_verification_overlay.dart';
import '../../widgets/multi_select_picker.dart';
import '../../widgets/qr_verification_dialog.dart';
import '../../widgets/responsive_body.dart';
@@ -525,25 +526,19 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
}
try {
final result = await face.runFaceLiveness(context);
if (result == null || !mounted) return;
final result = await showFaceVerificationOverlay(
context: context,
ref: ref,
// Profile test mode: no attendance record should be uploaded/skipped.
attendanceLogId: null,
uploadAttendanceResult: false,
maxAttempts: 3,
);
// Download enrolled photo via Supabase (authenticated, private bucket)
final enrolledBytes = await ref
.read(profileControllerProvider)
.downloadFacePhoto(profile.id);
if (enrolledBytes == null || !mounted) {
if (mounted) {
showErrorSnackBar(context, 'Could not load enrolled face photo.');
}
return;
}
if (!mounted || result == null) return;
// Compare captured vs enrolled
final score = await face.compareFaces(result.imageBytes, enrolledBytes);
if (!mounted) return;
if (score >= 0.60) {
final score = result.matchScore ?? 0.0;
if (result.verified) {
showSuccessSnackBar(
context,
'Face matched! Similarity: ${(score * 100).toStringAsFixed(1)}%',