From 4df24f5d8d841fa276d1219832995ec346f3a484 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Samarraie Date: Thu, 8 Jan 2026 16:18:37 +0300 Subject: [PATCH] =?UTF-8?q?=D9=A1=D9=A1=D9=A1=D9=A1=D9=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 3 + ios/Podfile.lock | 6 + ios/Runner/Info.plist | 10 +- lib/screens/attendence_screen.dart | 5 +- lib/screens/face_screen.dart | 260 +++++++++++++++++++++++ pubspec.lock | 92 +++++++- pubspec.yaml | 1 + 7 files changed, 361 insertions(+), 16 deletions(-) create mode 100644 lib/screens/face_screen.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e2ae159..62e0485 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,7 @@ + + + UIApplicationSupportsIndirectInputEvents - UIStatusBarHidden - - + UIStatusBarHidden + + NSCameraUsageDescription + We need access to your camera to capture photos for face recognition. + NSPhotoLibraryUsageDescription + We need access to your photo library to select photos. + diff --git a/lib/screens/attendence_screen.dart b/lib/screens/attendence_screen.dart index 9741590..3089069 100644 --- a/lib/screens/attendence_screen.dart +++ b/lib/screens/attendence_screen.dart @@ -1,3 +1,4 @@ +import 'package:coda_project/screens/face_screen.dart'; import 'package:coda_project/screens/notifications_screen.dart'; import 'package:coda_project/screens/user_settings_screen.dart'; import 'package:flutter/material.dart'; @@ -143,9 +144,7 @@ class AttendanceScreen extends StatelessWidget { onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => LoginAnimationScreen( - isLogin: true, - isSuccess: false, + builder: (_) => OvalCameraCapturePage( ), ), ); diff --git a/lib/screens/face_screen.dart b/lib/screens/face_screen.dart new file mode 100644 index 0000000..73913ab --- /dev/null +++ b/lib/screens/face_screen.dart @@ -0,0 +1,260 @@ +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; + +class OvalCameraCapturePage extends StatefulWidget { + const OvalCameraCapturePage({super.key}); + + @override + State createState() => _OvalCameraCapturePageState(); +} + +class _OvalCameraCapturePageState extends State { + CameraController? _cameraController; + bool _isCameraInitialized = false; + String? _errorMessage; + + @override + void initState() { + super.initState(); + _initializeCamera(); + } + + Future _initializeCamera() async { + try { + // Dispose existing controller if any + await _cameraController?.dispose(); + _cameraController = null; + + // Get available cameras + final cameras = await availableCameras(); + + // Check if cameras list is available + if (cameras.isEmpty) { + setState(() { + _errorMessage = "لا توجد كاميرات متاحة"; + _isCameraInitialized = false; + }); + return; + } + + // Try to find front camera, fallback to first available camera + CameraDescription? selectedCamera; + try { + selectedCamera = cameras.firstWhere( + (cam) => cam.lensDirection == CameraLensDirection.front, + ); + } catch (e) { + // If no front camera found, use the first available camera + if (cameras.isNotEmpty) { + selectedCamera = cameras.first; + } else { + setState(() { + _errorMessage = "لا توجد كاميرات متاحة"; + _isCameraInitialized = false; + }); + return; + } + } + + _cameraController = CameraController( + selectedCamera, + ResolutionPreset.medium, + enableAudio: false, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + await _cameraController!.initialize(); + + if (!mounted) return; + + setState(() { + _isCameraInitialized = true; + _errorMessage = null; + }); + } catch (e) { + if (!mounted) return; + + setState(() { + _errorMessage = "خطأ في تهيئة الكاميرا: $e"; + _isCameraInitialized = false; + }); + print("Error initializing camera: $e"); + } + } + + + + @override + void dispose() { + _cameraController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xff000000), + + body: _errorMessage != null + ? Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.camera_alt_outlined, + size: 64, + color: Colors.white70, + ), + SizedBox(height: 16), + Text( + _errorMessage!, + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 24), + ElevatedButton( + onPressed: _initializeCamera, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xffE8001A), + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 32, vertical: 12), + ), + child: Text("إعادة المحاولة"), + ), + ], + ), + ), + ) + : _isCameraInitialized && _cameraController != null + ? Stack( + children: [ + SizedBox(height: MediaQuery.of(context).size.height ), + // Camera Preview + Positioned( + child: Center(child: CameraPreview(_cameraController!)), + ), + // Oval overlay with dimmed background + Positioned.fill( + child: CustomPaint( + painter: _OvalOverlayPainter(), + ), + ), + + + // // Capture button + // Positioned( + // bottom: 60, + // left: 0, + // right: 0, + // child: Center( + // child: GestureDetector( + // onTap: (){}, + // child: Container( + // width: 72, + // height: 72, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // color: Colors.white, + // boxShadow: [ + // BoxShadow( + // color: Colors.black26, + // blurRadius: 8, + // offset: Offset(0, 4), + // ), + // ], + // ), + // child: Icon(Icons.camera_alt, color: Color(0xffE8001A), size: 36), + // ), + // ), + // ), + // ), + ], + ) + : Center(child: CircularProgressIndicator(color: Color(0xffE8001A))), + ); + } +} + +class _OvalOverlayPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final width = size.width * 0.75; + final height = size.height * 0.4; + final center = Offset(size.width / 2, size.height / 2); + final ovalRect = Rect.fromCenter( + center: center, + width: width, + height: height, + ); + + // Create a path for the whole screen + final screenPath = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height)); + // Create a path for the oval + final ovalPath = Path()..addOval(ovalRect); + // Subtract the oval from the screen path + final overlayPath = Path.combine(PathOperation.difference, screenPath, ovalPath); + + // Draw the dimmed area outside the oval with gradient + final gradient = LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color.fromARGB(255, 41, 41, 41), // top dark gray + Color.fromARGB(255, 0, 20, 15), // bottom deep green + ], + ); + final paint = Paint() + ..shader = gradient.createShader( + Rect.fromLTWH(0, 0, size.width, size.height), + ) + ..style = PaintingStyle.fill; + canvas.drawPath(overlayPath, paint); + + // Draw glowing circles effect (like AppBackground) - drawn after overlay + // Top circle - positioned similar to AppBackground (top: -250, left: 100, right: -200) + final topCircleCenter = Offset(size.width * 0.3, -250); + final topCircleRadius = 150.0; + // Draw multiple circles with different opacities for spread effect (spreadRadius: 160) + for (int i = 0; i < 5; i++) { + final spreadPaint = Paint() + ..color = Color.fromARGB(69 ~/ (i + 1), 62, 254, 190) + ..maskFilter = MaskFilter.blur(BlurStyle.normal, 140 - (i * 20)); + canvas.drawCircle( + topCircleCenter, + topCircleRadius + (i * 30), + spreadPaint, + ); + } + + // Bottom circle - positioned similar to AppBackground (bottom: 100, left: -140, right: -120) + final bottomCircleCenter = Offset(size.width * 0.2, size.height + 100); + final bottomCircleRadius = 160.0; + // Draw multiple circles with different opacities for spread effect (spreadRadius: 60) + for (int i = 0; i < 5; i++) { + final spreadPaint = Paint() + ..color = Color.fromARGB(83 ~/ (i + 1), 62, 254, 190) + ..maskFilter = MaskFilter.blur(BlurStyle.normal, 180 - (i * 25)); + canvas.drawCircle( + bottomCircleCenter, + bottomCircleRadius + (i * 40), + spreadPaint, + ); + } + + // Draw oval border + final borderPaint = Paint() + ..color = Colors.greenAccent + ..style = PaintingStyle.stroke + ..strokeWidth = 4; + canvas.drawOval(ovalRect, borderPaint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 50b7d1b..4d34b0b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" boolean_selector: dependency: transitive description: @@ -41,6 +41,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + camera: + dependency: "direct main" + description: + name: camera + sha256: "87a27e0553e3432119c1c2f6e4b9a1bbf7d2c660552b910bfa59185a9facd632" + url: "https://pub.dev" + source: hosted + version: "0.11.2+1" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: "58b8fe843a3c83fd1273c00cb35f5a8ae507f6cc9b2029bcf7e2abba499e28d8" + url: "https://pub.dev" + source: hosted + version: "0.6.19+1" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "951ef122d01ebba68b7a54bfe294e8b25585635a90465c311b2f875ae72c412f" + url: "https://pub.dev" + source: hosted + version: "0.9.21+2" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" + url: "https://pub.dev" + source: hosted + version: "2.12.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7" + url: "https://pub.dev" + source: hosted + version: "0.3.5+3" characters: dependency: transitive description: @@ -81,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608" + url: "https://pub.dev" + source: hosted + version: "0.3.5+1" crypto: dependency: transitive description: @@ -101,10 +149,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" ffi: dependency: transitive description: @@ -142,6 +190,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.6" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476 + url: "https://pub.dev" + source: hosted + version: "2.0.31" flutter_svg: dependency: "direct main" description: @@ -204,10 +260,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -280,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" posix: dependency: transitive description: @@ -317,6 +381,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -393,10 +465,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" web: dependency: transitive description: @@ -422,5 +494,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.32.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7cbff37..54c33ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: sdk: ^3.7.0 dependencies: + camera: ^0.11.2+1 flutter: sdk: flutter flutter_svg: ^2.0.9