From 4123e434171f763d9647feff7b22949ccff93c74 Mon Sep 17 00:00:00 2001
From: Daniah Ayad Al-sultani <148902945+Cactuskiller@users.noreply.github.com>
Date: Wed, 3 Dec 2025 14:59:45 +0300
Subject: [PATCH] chnages were made for the attandence screen
---
assets/images/error.svg | 11 +
assets/images/finger_print.svg | 6 +
assets/images/tick.svg | 11 +
lib/screens/attendence_screen.dart | 93 ++++---
lib/widgets/login_animation_screen.dart | 354 ++++++++++++++++++++++++
pubspec.yaml | 6 +
6 files changed, 449 insertions(+), 32 deletions(-)
create mode 100644 assets/images/error.svg
create mode 100644 assets/images/finger_print.svg
create mode 100644 assets/images/tick.svg
create mode 100644 lib/widgets/login_animation_screen.dart
diff --git a/assets/images/error.svg b/assets/images/error.svg
new file mode 100644
index 0000000..f594fa0
--- /dev/null
+++ b/assets/images/error.svg
@@ -0,0 +1,11 @@
+
diff --git a/assets/images/finger_print.svg b/assets/images/finger_print.svg
new file mode 100644
index 0000000..cfdc0c3
--- /dev/null
+++ b/assets/images/finger_print.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/tick.svg b/assets/images/tick.svg
new file mode 100644
index 0000000..f202eae
--- /dev/null
+++ b/assets/images/tick.svg
@@ -0,0 +1,11 @@
+
diff --git a/lib/screens/attendence_screen.dart b/lib/screens/attendence_screen.dart
index be9fee9..40b13d1 100644
--- a/lib/screens/attendence_screen.dart
+++ b/lib/screens/attendence_screen.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import '../widgets/login_animation_screen.dart'; // Import the LoginAnimationScreen
class AttendanceScreen extends StatelessWidget {
const AttendanceScreen({super.key});
@@ -88,10 +89,6 @@ class AttendanceScreen extends StatelessWidget {
top: 70,
left: 24,
child: _ShadowedCard(
- child: _FingerButton(
- icon: "assets/images/login.svg",
- label: "تسجيل الدخول",
- ),
shadow: const [
// Strong BLACK shadow bottom-right
BoxShadow(
@@ -101,6 +98,21 @@ class AttendanceScreen extends StatelessWidget {
offset: Offset(5, 5),
),
],
+ child: _FingerButton(
+ icon: "assets/images/login.svg",
+ label: "تسجيل الدخول",
+ onTap: () {
+ // Navigate to LoginAnimationScreen with success state
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => LoginAnimationScreen(
+ isLogin: true,
+ isSuccess: true, // Set to true for success, false for error
+ ),
+ ),
+ );
+ },
+ ),
),
),
@@ -109,12 +121,6 @@ class AttendanceScreen extends StatelessWidget {
bottom: 10,
right: 24,
child: _ShadowedCard(
- child: _FingerButton(
- icon: "assets/images/logout.svg",
- label: "تسجيل خروج",
- ),
-
- // GREEN glow top-left
shadow: const [
// Strong BLACK shadow bottom-right
BoxShadow(
@@ -137,6 +143,21 @@ class AttendanceScreen extends StatelessWidget {
offset: Offset(5, 5),
),
],
+ child: _FingerButton(
+ icon: "assets/images/logout.svg",
+ label: "تسجيل خروج",
+ onTap: () {
+ // Navigate to LoginAnimationScreen with success state
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => LoginAnimationScreen(
+ isLogin: false,
+ isSuccess: true, // Set to true for success, false for error
+ ),
+ ),
+ );
+ },
+ ),
),
),
],
@@ -176,33 +197,41 @@ class _ShadowedCard extends StatelessWidget {
class _FingerButton extends StatelessWidget {
final String icon;
final String label;
+ final VoidCallback onTap; // Added onTap callback
- const _FingerButton({required this.icon, required this.label});
+ const _FingerButton({
+ required this.icon,
+ required this.label,
+ required this.onTap, // Added this parameter
+ });
@override
Widget build(BuildContext context) {
- return Container(
- height: 160,
- width: 160,
- decoration: BoxDecoration(
- color: const Color(0xFFEAFBF3),
- borderRadius: BorderRadius.circular(32),
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SvgPicture.asset(icon, width: 62, height: 62),
- const SizedBox(height: 10),
- Text(
- label,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Colors.black,
+ return GestureDetector(
+ onTap: onTap, // Added gesture detection
+ child: Container(
+ height: 160,
+ width: 160,
+ decoration: BoxDecoration(
+ color: const Color(0xFFEAFBF3),
+ borderRadius: BorderRadius.circular(32),
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset(icon, width: 62, height: 62),
+ const SizedBox(height: 10),
+ Text(
+ label,
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Colors.black,
+ ),
),
- ),
- ],
+ ],
+ ),
),
);
}
-}
+}
\ No newline at end of file
diff --git a/lib/widgets/login_animation_screen.dart b/lib/widgets/login_animation_screen.dart
new file mode 100644
index 0000000..812dda7
--- /dev/null
+++ b/lib/widgets/login_animation_screen.dart
@@ -0,0 +1,354 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import '../widgets/app_background.dart';
+
+class LoginAnimationScreen extends StatefulWidget {
+ final bool isLogin;
+ final bool isSuccess;
+
+ const LoginAnimationScreen({
+ super.key,
+ required this.isLogin,
+ required this.isSuccess,
+ });
+
+ @override
+ State createState() => _LoginAnimationScreenState();
+}
+
+class _LoginAnimationScreenState extends State
+ with TickerProviderStateMixin {
+ late AnimationController ringController;
+ late AnimationController progressController;
+ late Animation ringAnimation;
+ late Animation progressAnimation;
+
+ bool showResult = false;
+
+ @override
+ void initState() {
+ super.initState();
+
+ ringController = AnimationController(
+ vsync: this,
+ duration: const Duration(seconds: 1),
+ );
+
+ progressController = AnimationController(
+ vsync: this,
+ duration: const Duration(seconds: 2),
+ );
+
+ ringAnimation = Tween(
+ begin: 0.0,
+ end: 1.0,
+ ).animate(CurvedAnimation(parent: ringController, curve: Curves.easeOut));
+
+ progressAnimation = Tween(begin: 0.0, end: 1.0).animate(
+ CurvedAnimation(parent: progressController, curve: Curves.linear),
+ );
+
+ startAnimations();
+
+ Future.delayed(const Duration(seconds: 2), () {
+ if (mounted) {
+ setState(() {
+ showResult = true;
+ });
+
+ ringController.stop();
+ progressController.stop();
+
+ if (widget.isSuccess) {
+ Future.delayed(const Duration(seconds: 2), () {
+ if (mounted) Navigator.of(context).pop();
+ });
+ }
+ }
+ });
+ }
+
+ void startAnimations() {
+ progressController.repeat();
+ startPulseAnimation();
+ }
+
+ void startPulseAnimation() {
+ ringController.forward().then((_) {
+ ringController.reset();
+ if (!showResult) startPulseAnimation();
+ });
+ }
+
+ @override
+ void dispose() {
+ ringController.dispose();
+ progressController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: AppBackground(
+ child: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ widget.isLogin ? "تسجيل الدخول" : "تسجيل خروج",
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 30,
+ fontWeight: FontWeight.bold,
+ decoration: TextDecoration.none,
+ ),
+ ),
+
+ const SizedBox(height: 100),
+
+ Container(
+ width: 280,
+ height: 400,
+ decoration: BoxDecoration(
+ color: const Color(0xFFEEFFFA),
+ borderRadius: BorderRadius.circular(38),
+ boxShadow: [
+ BoxShadow(
+ color: const Color(0x34000000),
+ blurRadius: 20,
+ offset: const Offset(0, 10),
+ ),
+ ],
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SizedBox(
+ width: 160,
+ height: 160,
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ Container(
+ width: 120,
+ height: 120,
+ decoration: const BoxDecoration(
+ color: Color(0xFFE5E5E5),
+ shape: BoxShape.circle,
+ ),
+ ),
+ AnimatedSwitcher(
+ duration: const Duration(milliseconds: 500),
+ child: _buildIcon(
+ showResult,
+ widget.isSuccess,
+ key: ValueKey(
+ "icon_${showResult}_${widget.isSuccess}",
+ ),
+ ),
+ ),
+ if (!showResult)
+ Positioned.fill(
+ child: AnimatedBuilder(
+ animation: progressAnimation,
+ builder: (_, __) {
+ return CustomPaint(
+ painter: _ProgressRingPainter(
+ progress: progressAnimation.value,
+ ),
+ );
+ },
+ ),
+ ),
+
+ if (!showResult)
+ Positioned.fill(
+ child: AnimatedBuilder(
+ animation: ringAnimation,
+ builder: (_, __) {
+ return CustomPaint(
+ painter: _PulseRingsPainter(
+ progress: ringAnimation.value,
+ ),
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+
+ const SizedBox(height: 40),
+
+ AnimatedSwitcher(
+ duration: const Duration(milliseconds: 500),
+ child:
+ showResult
+ ? Text(
+ widget.isSuccess
+ ? "تم تسجيل دخولك بنجاح"
+ : "تم رفض تسجيل الدخول ",
+ key: ValueKey("text_${widget.isSuccess}"),
+ style: const TextStyle(
+ fontSize: 17,
+ fontWeight: FontWeight.w600,
+ decoration: TextDecoration.none,
+ ),
+ )
+ : const Text(
+ "يتم تسجيل الدخول ...",
+ key: ValueKey("loading_text"),
+ style: TextStyle(
+ fontSize: 16,
+ decoration: TextDecoration.none,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ /// >>> FIXED ICON BUILDER <<<
+ Widget _buildIcon(bool showResult, bool isSuccess, {required Key key}) {
+ if (showResult) {
+ if (isSuccess) {
+ return SizedBox(
+ key: key,
+ width: 70,
+ height: 70,
+ child: SvgPicture.asset(
+ "assets/images/tick.svg",
+ width: 70,
+ height: 70,
+ placeholderBuilder:
+ (context) => Icon(
+ Icons.check_circle,
+ size: 70,
+ color: const Color(0xFF32C59A),
+ ),
+ ),
+ );
+ } else {
+ return SizedBox(
+ key: key,
+ width: 70,
+ height: 70,
+ child: SvgPicture.asset(
+ "assets/images/error.svg",
+ width: 70,
+ height: 70,
+ placeholderBuilder:
+ (context) => Icon(Icons.error, size: 70, color: Colors.red),
+ ),
+ );
+ }
+ }
+
+ return SizedBox(
+ key: key,
+ width: 70,
+ height: 70,
+ child: SvgPicture.asset(
+ "assets/images/finger_print.svg",
+ width: 70,
+ height: 70,
+ placeholderBuilder:
+ (context) => Icon(
+ Icons.fingerprint,
+ size: 70,
+ color: const Color(0xFF102D25),
+ ),
+ ),
+ );
+ }
+}
+
+class _ProgressRingPainter extends CustomPainter {
+ final double progress;
+
+ _ProgressRingPainter({required this.progress});
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final center = size.center(Offset.zero);
+
+ // radius slightly bigger than gray circle (60)
+ final radius = 50.0;
+
+ // background circle (very subtle)
+ final bgPaint =
+ Paint()
+ ..color = const Color(0x0032C599)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 3.0;
+
+ canvas.drawCircle(center, radius, bgPaint);
+
+ // foreground arc that animates
+ final progressPaint =
+ Paint()
+ ..color = const Color(0xC40A4433)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 3.0
+ ..strokeCap = StrokeCap.round;
+
+ const startAngle = -1.5708; // -90° in radians
+ final sweepAngle = 2 * 3.1415926 * progress;
+
+ canvas.drawArc(
+ Rect.fromCircle(center: center, radius: radius),
+ startAngle,
+ sweepAngle,
+ false,
+ progressPaint,
+ );
+ }
+
+ @override
+ bool shouldRepaint(_ProgressRingPainter oldDelegate) =>
+ oldDelegate.progress != progress;
+}
+
+class _PulseRingsPainter extends CustomPainter {
+ final double progress;
+
+ _PulseRingsPainter({required this.progress});
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final center = size.center(Offset.zero);
+
+ final baseRadius = 60.0; // start at grey circle
+ final maxRadius = 130.0; // outermost ripple
+
+ final ringPhases = [0.0, 0.25, 0.5];
+
+ for (final phase in ringPhases) {
+ final ringProgress = (progress - phase).clamp(0.0, 1.0);
+
+ if (ringProgress > 0) {
+ final radius = baseRadius + (maxRadius - baseRadius) * ringProgress;
+ final opacity = (1.0 - ringProgress) * 0.45;
+
+ final paint =
+ Paint()
+ ..color = const Color(0xFF32C59A).withOpacity(opacity)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 2;
+
+ canvas.drawCircle(center, radius, paint);
+ }
+ }
+ }
+
+ @override
+ bool shouldRepaint(_PulseRingsPainter oldDelegate) =>
+ oldDelegate.progress != progress;
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index ba1a38a..211081e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -22,6 +22,12 @@ flutter:
uses-material-design: true
assets:
- assets/images/
+ - assets/images/finger_print.svg
+ - assets/images/login.svg
+ - assets/images/logout.svg
+ - assets/images/tick.svg
+ - assets/images/error.svg
+
fonts:
- family: AbdElRady
fonts: