login agnimation has been fixed + request holiday screen was created

This commit is contained in:
Daniah Ayad Al-sultani
2025-12-04 17:23:06 +03:00
parent 209080842a
commit 08132b52a9
11 changed files with 1200 additions and 443 deletions

3
assets/images/back.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="28" height="24" viewBox="0 0 28 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.999673 22.8524C4.15995 18.9947 6.96631 16.8057 9.41876 16.2856C11.8712 15.7655 14.2061 15.6869 16.4235 16.0499V22.9584L26.833 11.6853L16.4235 1.00003V7.56622C12.3233 7.59851 8.83751 9.0695 5.96613 11.9792C3.09519 14.8889 1.4397 18.5133 0.999673 22.8524Z" stroke="#008864" stroke-width="2" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

View File

@@ -0,0 +1,3 @@
<svg width="23" height="24" viewBox="0 0 23 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.9333 11.6367C20.4125 11.957 20.8398 12.3203 21.2151 12.7266C21.5905 13.1328 21.9139 13.5781 22.1854 14.0625C22.4569 14.5469 22.6566 15.0586 22.7844 15.5977C22.9122 16.1367 22.984 16.6875 23 17.25C23 18.1797 22.8203 19.0547 22.4609 19.875C22.1016 20.6953 21.6064 21.4102 20.9755 22.0195C20.3446 22.6289 19.6139 23.1094 18.7833 23.4609C17.9528 23.8125 17.0583 23.9922 16.1 24C15.3733 24 14.6705 23.8945 13.9917 23.6836C13.3128 23.4727 12.6899 23.168 12.1229 22.7695C11.5559 22.3711 11.0528 21.8945 10.6135 21.3398C10.1743 20.7852 9.8349 20.1719 9.59531 19.5H0V1.5H3.06667V0H4.6V1.5H15.3333V0H16.8667V1.5H19.9333V11.6367ZM1.53333 3V6H18.4V3H16.8667V4.5H15.3333V3H4.6V4.5H3.06667V3H1.53333ZM9.23594 18C9.21198 17.7578 9.2 17.5078 9.2 17.25C9.2 16.5781 9.29583 15.9258 9.4875 15.293C9.67917 14.6602 9.97066 14.0625 10.362 13.5H9.2V12H10.7333V13.0078C11.0608 12.6094 11.4241 12.2578 11.8234 11.9531C12.2227 11.6484 12.654 11.3867 13.1172 11.168C13.5804 10.9492 14.0635 10.7852 14.5667 10.6758C15.0698 10.5664 15.5809 10.5078 16.1 10.5C16.8986 10.5 17.6653 10.6289 18.4 10.8867V7.5H1.53333V18H9.23594ZM16.1 22.5C16.8427 22.5 17.5375 22.3633 18.1844 22.0898C18.8312 21.8164 19.3983 21.4414 19.8854 20.9648C20.3726 20.4883 20.7559 19.9336 21.0354 19.3008C21.3149 18.668 21.4587 17.9844 21.4667 17.25C21.4667 16.5234 21.3269 15.8438 21.0474 15.2109C20.7679 14.5781 20.3845 14.0234 19.8974 13.5469C19.4102 13.0703 18.8432 12.6953 18.1964 12.4219C17.5495 12.1484 16.8507 12.0078 16.1 12C15.3573 12 14.6625 12.1367 14.0156 12.4102C13.3687 12.6836 12.8017 13.0586 12.3146 13.5352C11.8274 14.0117 11.4441 14.5664 11.1646 15.1992C10.8851 15.832 10.7413 16.5156 10.7333 17.25C10.7333 17.9766 10.8731 18.6562 11.1526 19.2891C11.4321 19.9219 11.8155 20.4766 12.3026 20.9531C12.7898 21.4297 13.3568 21.8047 14.0036 22.0781C14.6505 22.3516 15.3493 22.4922 16.1 22.5ZM16.8667 16.5H19.1667V18H15.3333V13.5H16.8667V16.5ZM3.06667 12H4.6V13.5H3.06667V12ZM6.13333 12H7.66667V13.5H6.13333V12ZM6.13333 9H7.66667V10.5H6.13333V9ZM3.06667 15H4.6V16.5H3.06667V15ZM6.13333 15H7.66667V16.5H6.13333V15ZM10.7333 10.5H9.2V9H10.7333V10.5ZM13.8 10.5H12.2667V9H13.8V10.5ZM16.8667 10.5H15.3333V9H16.8667V10.5Z" fill="#008864"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,3 +1,3 @@
<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.9103 5.01331L17.5035 9.7675L18.2543 10.1786L24.8439 5.94877L36.021 12.2799L29.61 16.3949L29.9223 16.5658V17.0875L34.9438 13.8642L35.9977 14.4612L29.9222 18.3608V19.2538L34.8454 16.0936L35.9977 16.7464L29.9222 20.6461V21.539L34.8454 18.3789L35.9977 19.0317L29.9222 22.9313V23.8243L37.5419 18.9333L35.6507 17.8621L37.5418 16.6482L35.6507 15.5769L37.5418 14.3629L35.7489 13.3473L37.565 12.1815L24.9103 5.01331ZM27.2767 8.42124C25.6742 9.22531 23.5397 9.36135 22.0368 8.7551L19.0989 10.6409L28.7653 15.9325L31.5518 14.144C30.4542 13.2775 30.5456 11.9846 31.7759 10.9699L27.2767 8.42124ZM17.3294 11.2353L11.945 14.7464L22.2746 20.6928L22.376 20.6281C22.5664 20.5054 22.8353 20.3318 23.1561 20.1244C23.7975 19.7098 24.6463 19.16 25.4926 18.6116C26.7115 17.8217 27.3396 17.4142 27.9237 17.0353L17.3294 11.2353ZM26.1248 11.3245H26.125C26.3686 11.3259 26.5919 11.3782 26.7587 11.4726C26.8558 11.5276 26.9311 11.5954 26.9803 11.6721C27.0295 11.7489 27.0517 11.8331 27.0455 11.92C27.0394 12.0069 27.005 12.0947 26.9444 12.1785C26.8839 12.2622 26.7982 12.3403 26.6925 12.4081C26.5867 12.476 26.4629 12.5324 26.3281 12.5741C26.1932 12.6158 26.05 12.6419 25.9066 12.651C25.7632 12.6602 25.6224 12.6521 25.4923 12.6273C25.3622 12.6024 25.2453 12.5614 25.1482 12.5064C24.9523 12.3954 24.8491 12.2345 24.8616 12.0591C24.874 11.8836 25.0009 11.7081 25.2145 11.571C25.3358 11.4932 25.4806 11.4305 25.6382 11.3878C25.7959 11.3452 25.9622 11.3235 26.1248 11.3245ZM9.48706 14.9133L1.45817 20.0667L3.25103 21.0823L1.43494 22.2481L3.32613 23.3193L1.43494 24.5333L3.32613 25.6045L1.43494 26.8186L14.0898 33.9868L21.7301 29.0824V28.1897L14.1561 33.0513L2.97909 26.7201L4.06492 26.0231L14.0898 31.7015L21.7301 26.7972V25.9045L14.1561 30.7661L2.97909 24.4349L4.06492 23.738L14.0898 29.4163L21.7301 24.5121V23.6194L14.1561 28.4809L2.97909 22.1498L3.98989 21.5008L14.1131 27.235L21.7301 22.3457V21.961L21.3127 21.7208L14.1792 26.2995L3.00248 19.9684L10.2205 15.3353L9.48699 14.9131L9.48706 14.9133ZM11.0455 15.8106L7.47156 18.1045C8.56919 18.9709 8.47779 20.2638 7.24746 21.2786L11.7468 23.8273C13.3492 23.0232 15.4835 22.8872 16.9864 23.4934L20.4877 21.246L11.0454 15.8105L11.0455 15.8106ZM28.5511 18.2626C27.9384 18.66 27.398 19.0107 26.2383 19.7622C25.4593 20.2672 24.6801 20.7718 23.9006 21.276C23.5794 21.4836 23.3099 21.6576 23.1177 21.7812C23.1109 21.7857 23.108 21.7873 23.1013 21.7916V28.3366L28.5512 24.6736L28.5511 18.2626ZM13.2413 19.594C13.485 19.5956 13.7083 19.6478 13.8751 19.7424C14.0711 19.8534 14.1742 20.0143 14.1618 20.1897C14.1493 20.3651 14.0224 20.5407 13.8088 20.6777C13.5953 20.8148 13.3127 20.9021 13.0231 20.9206C12.7336 20.939 12.4608 20.887 12.2648 20.776C12.0689 20.665 11.9657 20.5041 11.9782 20.3287C11.9906 20.1532 12.1175 19.9777 12.3311 19.8406C12.4524 19.7628 12.5972 19.7002 12.7548 19.6575C12.9125 19.6148 13.0788 19.5931 13.2413 19.5941V19.594Z" fill="#008864"/>
<path d="M24.9099 5.01331L17.5032 9.7675L18.2539 10.1786L24.8436 5.94877L36.0206 12.2799L29.6096 16.3949L29.9219 16.5658V17.0875L34.9434 13.8642L35.9973 14.4612L29.9219 18.3608V19.2538L34.8451 16.0936L35.9973 16.7464L29.9219 20.6461V21.539L34.8451 18.3789L35.9973 19.0317L29.9219 22.9313V23.8243L37.5416 18.9333L35.6504 17.8621L37.5415 16.6482L35.6503 15.5769L37.5414 14.3629L35.7485 13.3473L37.5646 12.1815L24.9099 5.01331ZM27.2763 8.42124C25.6738 9.22531 23.5393 9.36135 22.0365 8.7551L19.0985 10.6409L28.765 15.9325L31.5515 14.144C30.4538 13.2775 30.5452 11.9846 31.7756 10.9699L27.2763 8.42124ZM17.329 11.2353L11.9446 14.7464L22.2743 20.6928L22.3756 20.6281C22.566 20.5054 22.8349 20.3318 23.1557 20.1244C23.7971 19.7098 24.6459 19.16 25.4922 18.6116C26.7111 17.8217 27.3393 17.4142 27.9233 17.0353L17.329 11.2353ZM26.1244 11.3245H26.1246C26.3682 11.3259 26.5916 11.3782 26.7584 11.4726C26.8554 11.5276 26.9307 11.5954 26.9799 11.6721C27.0291 11.7489 27.0513 11.8331 27.0452 11.92C27.039 12.0069 27.0046 12.0947 26.9441 12.1785C26.8835 12.2622 26.7979 12.3403 26.6921 12.4081C26.5863 12.476 26.4625 12.5324 26.3277 12.5741C26.1929 12.6158 26.0496 12.6419 25.9062 12.651C25.7628 12.6602 25.6221 12.6521 25.4919 12.6273C25.3618 12.6024 25.2449 12.5614 25.1479 12.5064C24.9519 12.3954 24.8488 12.2345 24.8612 12.0591C24.8736 11.8836 25.0006 11.7081 25.2141 11.571C25.3354 11.4932 25.4802 11.4305 25.6379 11.3878C25.7955 11.3452 25.9618 11.3235 26.1244 11.3245ZM9.4867 14.9133L1.4578 20.0667L3.25066 21.0823L1.43457 22.2481L3.32577 23.3193L1.43457 24.5333L3.32577 25.6045L1.43457 26.8186L14.0894 33.9868L21.7297 29.0824V28.1897L14.1557 33.0513L2.97873 26.7201L4.06456 26.0231L14.0894 31.7015L21.7297 26.7972V25.9045L14.1557 30.7661L2.97873 24.4349L4.06456 23.738L14.0894 29.4163L21.7297 24.5121V23.6194L14.1557 28.4809L2.97873 22.1498L3.98953 21.5008L14.1128 27.235L21.7297 22.3457V21.961L21.3123 21.7208L14.1788 26.2995L3.00211 19.9684L10.2202 15.3353L9.48662 14.9131L9.4867 14.9133ZM11.0452 15.8106L7.47119 18.1045C8.56883 18.9709 8.47742 20.2638 7.24709 21.2786L11.7464 23.8273C13.3488 23.0232 15.4832 22.8872 16.986 23.4934L20.4873 21.246L11.045 15.8105L11.0452 15.8106ZM28.5508 18.2626C27.938 18.66 27.3977 19.0107 26.238 19.7622C25.459 20.2672 24.6797 20.7718 23.9002 21.276C23.579 21.4836 23.3095 21.6576 23.1173 21.7812C23.1105 21.7857 23.1077 21.7873 23.101 21.7916V28.3366L28.5508 24.6736L28.5508 18.2626ZM13.241 19.594C13.4846 19.5956 13.708 19.6478 13.8747 19.7424C14.0707 19.8534 14.1738 20.0143 14.1614 20.1897C14.149 20.3651 14.022 20.5407 13.8085 20.6777C13.5949 20.8148 13.3123 20.9021 13.0227 20.9206C12.7332 20.939 12.4604 20.887 12.2645 20.776C12.0685 20.665 11.9654 20.5041 11.9778 20.3287C11.9902 20.1532 12.1172 19.9777 12.3307 19.8406C12.452 19.7628 12.5969 19.7002 12.7545 19.6575C12.9121 19.6148 13.0784 19.5931 13.241 19.5941V19.594Z" fill="#008864"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

3
assets/images/money2.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="37" height="29" viewBox="0 0 37 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.4753 0L16.0686 4.75419L16.8194 5.16529L23.409 0.935467L34.5861 7.26657L28.1751 11.3816L28.4874 11.5525V12.0742L33.5088 8.85087L34.5628 9.4479L28.4873 13.3475V14.2405L33.4105 11.0803L34.5628 11.7331L28.4873 15.6328V16.5257L33.4105 13.3656L34.5628 14.0184L28.4873 17.918V18.8109L36.107 13.92L34.2158 12.8488L36.1069 11.6349L34.2157 10.5636L36.1068 9.34964L34.314 8.33404L36.1301 7.16823L23.4753 0ZM25.8418 3.40793C24.2393 4.212 22.1048 4.34804 20.6019 3.74179L17.664 5.62758L27.3304 10.9192L30.1169 9.13072C29.0193 8.26419 29.1107 6.97125 30.341 5.95664L25.8418 3.40793ZM15.8945 6.22202L10.51 9.73309L20.8397 15.6795L20.941 15.6148C21.1314 15.4921 21.4003 15.3185 21.7212 15.1111C22.3625 14.6964 23.2113 14.1467 24.0577 13.5983C25.2766 12.8084 25.9047 12.4009 26.4888 12.022L15.8945 6.22202ZM24.6898 6.31114H24.69C24.9336 6.31263 25.157 6.36485 25.3238 6.4593C25.4209 6.51427 25.4962 6.58207 25.5454 6.65884C25.5946 6.73561 25.6167 6.81983 25.6106 6.90671C25.6044 6.99359 25.5701 7.08141 25.5095 7.16517C25.4489 7.24892 25.3633 7.32696 25.2575 7.39484C25.1518 7.46272 25.028 7.5191 24.8931 7.56078C24.7583 7.60245 24.6151 7.6286 24.4717 7.63772C24.3283 7.64684 24.1875 7.63877 24.0574 7.61396C23.9273 7.58914 23.8103 7.54808 23.7133 7.4931C23.5173 7.38209 23.4142 7.22118 23.4266 7.04576C23.4391 6.87034 23.566 6.69479 23.7796 6.55771C23.9008 6.47985 24.0457 6.41724 24.2033 6.37454C24.3609 6.33185 24.5272 6.31017 24.6898 6.31114ZM8.05213 9.89998L0.0232324 15.0534L1.81609 16.069L0 17.2348L1.8912 18.306L0 19.52L1.8912 20.5912L0 21.8053L12.6548 28.9735L20.2952 24.0691V23.1764L12.7212 28.038L1.54416 21.7068L2.62999 21.0098L12.6548 26.6882L20.2952 21.7839V20.8912L12.7212 25.7528L1.54416 19.4216L2.62999 18.7246L12.6548 24.403L20.2952 19.4988V18.606L12.7212 23.4676L1.54416 17.1365L2.55496 16.4875L12.6782 22.2217L20.2952 17.3324V16.9477L19.8777 16.7075L12.7442 21.2862L1.56754 14.9551L8.78559 10.322L8.05205 9.89975L8.05213 9.89998ZM9.6106 10.7973L6.03662 13.0912C7.13426 13.9576 7.04285 15.2505 5.81252 16.2653L10.3118 18.814C11.9143 18.0098 14.0486 17.8739 15.5515 18.4801L19.0527 16.2327L9.61045 10.7972L9.6106 10.7973ZM27.1162 13.2493C26.5035 13.6466 25.9631 13.9974 24.8034 14.7489C24.0244 15.2539 23.2452 15.7585 22.4657 16.2627C22.1445 16.4703 21.875 16.6443 21.6828 16.7679C21.6759 16.7724 21.6731 16.774 21.6664 16.7783V23.3233L27.1163 19.6603L27.1162 13.2493ZM11.8064 14.5807C12.05 14.5823 12.2734 14.6345 12.4402 14.7291C12.6361 14.8401 12.7392 15.001 12.7268 15.1764C12.7144 15.3518 12.5874 15.5274 12.3739 15.6644C12.1603 15.8015 11.8777 15.8888 11.5882 15.9072C11.2986 15.9257 11.0259 15.8737 10.8299 15.7627C10.6339 15.6517 10.5308 15.4908 10.5432 15.3154C10.5557 15.1399 10.6826 14.9644 10.8962 14.8273C11.0174 14.7495 11.1623 14.6869 11.3199 14.6442C11.4775 14.6015 11.6438 14.5798 11.8064 14.5808V14.5807Z" fill="#008864"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/login_animation_screen.dart'; // Import the LoginAnimationScreen
import '../widgets/login_animation.dart';
class AttendanceScreen extends StatelessWidget {
const AttendanceScreen({super.key});
@@ -153,8 +153,8 @@ class AttendanceScreen extends StatelessWidget {
MaterialPageRoute(
builder:
(context) => LoginAnimationScreen(
isLogin: true,
isSuccess: false,
isLogin: false,
isSuccess: true,
),
),
);

View File

@@ -1,9 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../screens/request_leave_screen.dart';
class HolidayScreen extends StatelessWidget {
class HolidayScreen extends StatefulWidget {
const HolidayScreen({super.key});
@override
State<HolidayScreen> createState() => _HolidayScreenState();
}
class _HolidayScreenState extends State<HolidayScreen> {
int activeTab = 0; // 0 = السلف | 1 = الأجازات
@override
Widget build(BuildContext context) {
return Directionality(
@@ -11,20 +19,99 @@ class HolidayScreen extends StatelessWidget {
child: Stack(
children: [
Positioned(
bottom: 40,
top: 40,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// الأجازات TAB
GestureDetector(
onTap: () => setState(() => activeTab = 1),
child: Column(
children: [
Text(
"الأجازات",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color:
activeTab == 1
? const Color(0xFF8EFDC2)
: const Color(0x9EFFFFFF),
),
),
if (activeTab == 1)
Container(
width: 60,
height: 2,
margin: const EdgeInsets.only(top: 4),
color: const Color(0xFF8EFDC2),
),
],
),
),
const SizedBox(width: 70),
// السلف TAB
GestureDetector(
onTap: () => setState(() => activeTab = 0),
child: Column(
children: [
Text(
"السلف",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color:
activeTab == 0
? const Color(0xFF8EFDC2)
: const Color(0x9EFFFFFF),
),
),
if (activeTab == 0)
Container(
width: 60,
height: 2,
margin: const EdgeInsets.only(top: 4),
color: const Color(0xFF8EFDC2),
),
],
),
),
],
),
),
Positioned(
bottom: 30,
right: 20,
child: Column(
children: [
// Money icon (custom size)
_HolidayActionButton(
label: "طلب سلفة",
svgPath: "assets/images/money.svg", // placeholder
svgPath: "assets/images/money2.svg",
iconWidth: 38,
iconHeight: 30,
onTap: () {},
),
const SizedBox(height: 18),
// Plus icon (custom size)
_HolidayActionButton(
label: "طلب إجازة",
svgPath: "assets/images/plus.svg", // placeholder
onTap: () {},
svgPath: "assets/images/plus.svg",
iconWidth: 30,
iconHeight: 30,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RequestLeaveScreen(),
),
);
},
),
],
),
@@ -39,11 +126,15 @@ class _HolidayActionButton extends StatelessWidget {
final String label;
final String svgPath;
final VoidCallback onTap;
final double iconWidth;
final double iconHeight;
const _HolidayActionButton({
required this.label,
required this.svgPath,
required this.onTap,
required this.iconWidth,
required this.iconHeight,
});
@override
@@ -51,28 +142,26 @@ class _HolidayActionButton extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
width: 75,
height: 75,
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
boxShadow: const [
BoxShadow(
color: const Color(0x4B00C68B),
color: Color(0x4B00C68B),
blurRadius: 20,
offset: const Offset(0, 6),
offset: Offset(0, 6),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
svgPath,
width: 32,
height: 32,
fit: BoxFit.contain, // 🔥 Ensures same icon size
alignment: Alignment.center,
SizedBox(
width: iconWidth,
height: iconHeight,
child: SvgPicture.asset(svgPath, fit: BoxFit.contain),
),
const SizedBox(height: 6),

View File

@@ -0,0 +1,547 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../widgets/app_background.dart';
import '../widgets/settings_bar.dart';
import '../widgets/onboarding_button.dart';
class RequestLeaveScreen extends StatefulWidget {
const RequestLeaveScreen({super.key});
@override
State<RequestLeaveScreen> createState() => _RequestLeaveScreenState();
}
class _RequestLeaveScreenState extends State<RequestLeaveScreen> {
// Dropdown value
String leaveType = "إجازة مرضية ";
// Toggle switch
bool isTimedLeave = false;
// Date & time selectors
DateTime? fromDate = DateTime.now();
DateTime? toDate = DateTime.now();
TimeOfDay? fromTime = const TimeOfDay(hour: 12, minute: 00);
TimeOfDay? toTime = const TimeOfDay(hour: 12, minute: 00);
// Text controller for reason
final TextEditingController reasonController = TextEditingController();
/// PICK DATE
Future<void> pickDate(bool isFrom) async {
DateTime initial = isFrom ? fromDate! : toDate!;
DateTime? newDate = await showDatePicker(
context: context,
initialDate: initial,
firstDate: DateTime(2020),
lastDate: DateTime(2035),
builder: (context, child) {
return Theme(data: ThemeData.dark(), child: child!);
},
);
if (newDate != null) {
setState(() {
if (isFrom) {
fromDate = newDate;
} else {
toDate = newDate;
}
});
}
}
/// PICK TIME
Future<void> pickTime(bool isFrom) async {
TimeOfDay initial = isFrom ? fromTime! : toTime!;
TimeOfDay? newTime = await showTimePicker(
context: context,
initialTime: initial,
builder: (context, child) {
return Theme(data: ThemeData.dark(), child: child!);
},
);
if (newTime != null) {
setState(() {
if (isFrom) {
fromTime = newTime;
} else {
toTime = newTime;
}
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AppBackground(
child: SafeArea(
child: Column(
children: [
// =============================
// TOP NAV BAR
// =============================
SettingsBar(
selectedIndex: -1,
onTap: (_) {},
showBackButton: true,
onBackTap: () => Navigator.pop(context),
iconPaths: const [
"assets/images/user.svg",
"assets/images/bell.svg",
],
),
// Title
const SizedBox(height: 30),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.topRight,
child: const Text(
"طلب أجازة ",
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 25),
//=============================
// DROPDOWN: نوع الإجازة
//=============================
Align(
alignment: Alignment.centerRight,
child: const Text(
"نوع الإجازة",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 6),
Directionality(
textDirection:
TextDirection.rtl, // <<< CHANGE DIRECTION HERE
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20),
height: 58,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
boxShadow: const [
BoxShadow(
color: Color(0x22000000),
blurRadius: 8,
offset: Offset(0, 3),
),
],
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: leaveType,
icon: const Icon(
Icons.keyboard_arrow_down_rounded,
color: Colors.black,
size: 28,
),
style: const TextStyle(
color: Colors.black,
fontSize: 17,
),
isExpanded: true,
onChanged: (value) {
setState(() => leaveType = value!);
},
items: [
DropdownMenuItem(
value: "إجازة مرضية ",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة مرضية "),
),
),
),
DropdownMenuItem(
value: "إجازة مدفوعة",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة مدفوعة"),
),
),
),
DropdownMenuItem(
value: "إجازة غير مدفوعة",
child: Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.centerRight,
child: Text("إجازة غير مدفوعة"),
),
),
),
],
),
),
),
),
const SizedBox(height: 25),
//=============================
// PERFECT CUSTOM TOGGLE (NEW)
//=============================
GestureDetector(
onTap:
() => setState(() => isTimedLeave = !isTimedLeave),
child: Row(
children: [
// ---------- TOGGLE ----------
AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: 95,
height: 42,
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
decoration: BoxDecoration(
color:
isTimedLeave
? const Color(
0xFF0A6B4A,
) // ON green track
: const Color(
0xFF9E9E9E,
), // OFF grey track
borderRadius: BorderRadius.circular(40),
),
child: Align(
alignment:
isTimedLeave
? Alignment.centerRight
: Alignment.centerLeft,
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: 40,
height: 40,
decoration: BoxDecoration(
color:
isTimedLeave
? const Color(0xFF12BE85) // ON knob
: Colors.white, // OFF knob
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0x2D000000),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
),
),
),
const SizedBox(width: 14),
// ---------- Dot (ALWAYS visible) ----------
Container(
width: 10,
height: 10,
decoration: const BoxDecoration(
color: Color(0xFF32C59A),
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
// ---------- Line (ALWAYS visible) ----------
Expanded(
child: Container(
height: 2,
color: const Color(0xFF32C59A),
),
),
const SizedBox(width: 12),
// ---------- Label ----------
const Text(
"إجازة زمنية",
style: TextStyle(
color: Colors.white,
fontSize: 19,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(height: 20),
// =============================
// DATE PICKER BOX
// =============================
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF4F4F4),
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.end, // ALIGN RIGHT
children: [
// TEXT aligned right
const Text(
"فترة الإجازة",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
// 🟢 YOUR CUSTOM SVG ICON
SvgPicture.asset(
"assets/images/calendar.svg", // <-- replace with your icon filename
width: 24,
height: 24,
color: Color(
0xFF007C46,
), // Optional: match your green
),
],
),
const SizedBox(height: 15),
// From date row
_dateRow(
label: "من",
date: fromDate!,
time: fromTime!,
onDateTap:
isTimedLeave ? null : () => pickDate(true),
onTimeTap: () => pickTime(true),
),
const SizedBox(height: 15),
// To date row
_dateRow(
label: "الى",
date: toDate!,
time: toTime!,
onDateTap:
isTimedLeave ? null : () => pickDate(false),
onTimeTap: () => pickTime(false),
),
],
),
),
const SizedBox(height: 25),
// =============================
// REASON TEXTFIELD (Two Containers)
// =============================
Align(
alignment: Alignment.centerRight,
child: Directionality(
textDirection: TextDirection.rtl,
child: const Text(
"السبب",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
// OUTER BORDER CONTAINER
Container(
padding: const EdgeInsets.all(
15,
), // border thickness space
decoration: BoxDecoration(
border: Border.all(
color: Color(0xFF00FFAA), // green border
width: 0.5,
),
borderRadius: BorderRadius.circular(14),
),
// INNER TEXTFIELD CONTAINER
child: Container(
height: 100,
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 10,
),
decoration: BoxDecoration(
color: const Color(0xFFEAEAEA),
borderRadius: BorderRadius.circular(12),
),
child: TextField(
controller: reasonController,
maxLines: 5,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: "",
),
),
),
),
const SizedBox(height: 40),
// CONFIRM BUTTON
Center(
child: OnboardingButton(
text: "تأكيد الطلب",
backgroundColor: const Color(0xFFD1FEF0),
onPressed: () {},
),
),
const SizedBox(height: 40),
],
),
),
),
],
),
),
),
);
}
// ===============================================================
// CUSTOM DATE ROW WIDGET
// ===============================================================
Widget _dateRow({
required String label,
required DateTime date,
required TimeOfDay time,
required VoidCallback? onDateTap,
required VoidCallback onTimeTap,
}) {
bool dateDisabled = onDateTap == null;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
height: 55,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
// -----------------------
// DATE PART (can be disabled)
// -----------------------
Opacity(
opacity: dateDisabled ? 0.45 : 1,
child: IgnorePointer(
ignoring: dateDisabled,
child: GestureDetector(
onTap: onDateTap,
child: Row(
children: [
const Icon(Icons.arrow_drop_down),
const SizedBox(width: 4),
Text(
"${date.year}-${date.month}-${date.day}",
style: const TextStyle(fontSize: 16),
),
const SizedBox(width: 10),
Text(
_weekday(date.weekday),
style: const TextStyle(fontSize: 16),
),
],
),
),
),
),
const Spacer(),
// -----------------------
// TIME PART (always active)
// -----------------------
GestureDetector(
onTap: onTimeTap,
child: Text(
"${time.hour}:${time.minute.toString().padLeft(2, '0')}",
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(color: Colors.black, fontSize: 15),
),
],
),
);
}
String _weekday(int day) {
switch (day) {
case 1:
return "الإثنين";
case 2:
return "الثلاثاء";
case 3:
return "الأربعاء";
case 4:
return "الخميس";
case 5:
return "الجمعة";
case 6:
return "السبت";
case 7:
return "الأحد";
default:
return "";
}
}
}

View File

@@ -0,0 +1,486 @@
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<LoginAnimationScreen> createState() => _LoginAnimationScreenState();
}
class _LoginAnimationScreenState extends State<LoginAnimationScreen>
with TickerProviderStateMixin {
late AnimationController ringController;
late AnimationController progressController;
late Animation<double> ringAnimation;
late Animation<double> progressAnimation;
// ERROR pulse animation
late AnimationController errorPulseController;
late Animation<double> errorPulseAnimation;
// SUCCESS one-time pulse
late AnimationController successPulseController;
late Animation<double> successPulseAnimation;
bool showResult = false;
@override
void initState() {
super.initState();
// MAIN loading ripple animations
ringController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
progressController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
ringAnimation = Tween<double>(
begin: 0,
end: 1,
).animate(CurvedAnimation(parent: ringController, curve: Curves.easeOut));
progressAnimation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: progressController, curve: Curves.linear),
);
// ERROR pulse animation
errorPulseController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
errorPulseAnimation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: errorPulseController, curve: Curves.easeOut),
);
// SUCCESS one-time pulse
successPulseController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
successPulseAnimation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: successPulseController, curve: Curves.easeOut),
);
startAnimations();
// MAIN DELAY (same as before)
Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
setState(() => showResult = true);
ringController.stop();
progressController.stop();
if (!widget.isSuccess) {
startErrorPulse();
} else {
successPulseController.forward(); // ONE-TIME BEAT
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();
});
}
void startErrorPulse() {
errorPulseController.forward().then((_) {
errorPulseController.reset();
if (showResult && !widget.isSuccess) startErrorPulse();
});
}
@override
void dispose() {
ringController.dispose();
progressController.dispose();
errorPulseController.dispose();
successPulseController.dispose();
super.dispose();
}
Widget _buildIcon() {
if (!showResult) {
return SvgPicture.asset(
"assets/images/finger_print.svg",
key: const ValueKey("fingerprint"),
width: 70,
height: 70,
);
} else if (widget.isSuccess) {
return SizedBox(
width: 120,
height: 120,
child: Image.asset(
"assets/images/tick.png",
key: const ValueKey("success"),
fit: BoxFit.contain,
),
);
} else {
return SizedBox(
width: 120,
height: 120,
child: Image.asset(
"assets/images/error.png",
key: const ValueKey('error'),
fit: BoxFit.contain,
),
);
}
}
@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: [
// GREY CIRCLE (base)
Container(
width: 120,
height: 120,
decoration: const BoxDecoration(
color: Color(0xFFE5E5E5),
shape: BoxShape.circle,
),
),
// MAIN ICON SWITCHER
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: _buildIcon(),
),
// LOADING MODE
if (!showResult)
Positioned.fill(
child: AnimatedBuilder(
animation: progressAnimation,
builder:
(_, __) => CustomPaint(
painter: _ProgressRingPainter(
progress: progressAnimation.value,
),
),
),
),
if (!showResult)
Positioned.fill(
child: AnimatedBuilder(
animation: ringAnimation,
builder:
(_, __) => CustomPaint(
painter: _PulseRingsPainter(
progress: ringAnimation.value,
),
),
),
),
// ERROR ANIMATION
if (showResult && !widget.isSuccess)
Positioned.fill(
child: AnimatedBuilder(
animation: errorPulseAnimation,
builder:
(_, __) => CustomPaint(
painter: _ErrorPulsePainter(
progress: errorPulseAnimation.value,
),
),
),
),
// SUCCESS ONE-TIME PULSE
if (showResult && widget.isSuccess)
Positioned.fill(
child: AnimatedBuilder(
animation: successPulseAnimation,
builder:
(_, __) => CustomPaint(
painter: _SuccessPulsePainter(
progress: successPulseAnimation.value,
),
),
),
),
],
),
),
const SizedBox(height: 40),
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child:
showResult
? Text(
widget.isSuccess
? (widget.isLogin
? "تم تسجيل دخولك بنجاح"
: "تم تسجيل خروجك بنجاح")
: "تم رفض تسجيل الدخول",
key: ValueKey("text_${widget.isSuccess}"),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
)
: Text(
widget.isLogin
? "يتم تسجيل الدخول..."
: "يتم تسجيل الخروج...",
key: const ValueKey("loading_text"),
style: const TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
),
),
),
// RETRY BUTTON
if (showResult && !widget.isSuccess) ...[
const SizedBox(height: 20),
GestureDetector(
onTap: () {
setState(() => showResult = false);
errorPulseController.stop();
successPulseController.reset();
startAnimations();
Future.delayed(const Duration(seconds: 2), () {
if (!mounted) return;
setState(() => showResult = true);
ringController.stop();
progressController.stop();
if (!widget.isSuccess) {
startErrorPulse();
} else {
successPulseController.forward();
Future.delayed(const Duration(seconds: 2), () {
if (mounted) Navigator.of(context).pop();
});
}
});
},
child: const Text(
"أعد المحاولة",
style: TextStyle(
color: Color(0xFFB00020),
fontSize: 16,
fontWeight: FontWeight.w600,
decoration: TextDecoration.underline,
),
),
),
],
],
),
),
],
),
),
),
);
}
}
// ------------------------- PAINTERS -------------------------- //
class _ProgressRingPainter extends CustomPainter {
final double progress;
_ProgressRingPainter({required this.progress});
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
final radius = 50.0;
final bgPaint =
Paint()
..color = const Color(0x0032C599)
..style = PaintingStyle.stroke
..strokeWidth = 3;
canvas.drawCircle(center, radius, bgPaint);
final fgPaint =
Paint()
..color = const Color(0xC40A4433)
..style = PaintingStyle.stroke
..strokeWidth = 3
..strokeCap = StrokeCap.round;
final sweep = 2 * 3.1415926 * progress;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-1.5708,
sweep,
false,
fgPaint,
);
}
@override
bool shouldRepaint(_) => true;
}
class _PulseRingsPainter extends CustomPainter {
final double progress;
_PulseRingsPainter({required this.progress});
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
const baseRadius = 60.0;
const maxRadius = 130.0;
for (final phase in [0.0, 0.25, 0.5]) {
final rp = (progress - phase).clamp(0.0, 1.0);
if (rp > 0) {
final radius = baseRadius + (maxRadius - baseRadius) * rp;
final opacity = (1 - rp) * 0.45;
final paint =
Paint()
..color = const Color(0xFF32C59A).withOpacity(opacity)
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawCircle(center, radius, paint);
}
}
}
@override
bool shouldRepaint(_) => true;
}
// ERROR PAINTER
class _ErrorPulsePainter extends CustomPainter {
final double progress;
_ErrorPulsePainter({required this.progress});
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
// static ring
final staticPaint =
Paint()
..color = const Color(0xFFB00020).withOpacity(0.20)
..style = PaintingStyle.stroke
..strokeWidth = 3;
canvas.drawCircle(center, 70, staticPaint);
// pulse ring
final radius = 70 + (20 * progress);
final opacity = (1 - progress) * 0.45;
final pulsePaint =
Paint()
..color = const Color(0xFFB00020).withOpacity(opacity)
..style = PaintingStyle.stroke
..strokeWidth = 3;
canvas.drawCircle(center, radius, pulsePaint);
}
@override
bool shouldRepaint(_) => true;
}
// SUCCESS one-time pulse
class _SuccessPulsePainter extends CustomPainter {
final double progress;
_SuccessPulsePainter({required this.progress});
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
final radius = 70 + (20 * progress);
final opacity = (1 - progress) * 0.40;
final paint =
Paint()
..color = const Color(0xFF32C59A).withOpacity(opacity)
..style = PaintingStyle.stroke
..strokeWidth = 3;
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(_) => true;
}

View File

@@ -1,353 +0,0 @@
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<LoginAnimationScreen> createState() => _LoginAnimationScreenState();
}
class _LoginAnimationScreenState extends State<LoginAnimationScreen>
with TickerProviderStateMixin {
late AnimationController ringController;
late AnimationController progressController;
late Animation<double> ringAnimation;
late Animation<double> 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<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(parent: ringController, curve: Curves.easeOut));
progressAnimation = Tween<double>(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();
}
// FIXED: Simplified _buildIcon method that returns the appropriate SVG
Widget _buildIcon() {
if (!showResult) {
// Loading state - fingerprint
return SvgPicture.asset(
"assets/images/finger_print.svg",
key: const ValueKey('fingerprint'),
width: 70,
height: 70,
placeholderBuilder: (context) => Icon(
Icons.fingerprint,
size: 70,
color: const Color(0xFF102D25),
),
);
} else if (widget.isSuccess) {
// Success state - tick mark
return Image.asset(
"assets/images/tick.png",
key: const ValueKey('success'),
width: 120,
height: 120,
fit: BoxFit.contain,
// placeholderBuilder: (context) => Icon(
// Icons.check_circle,
// size: 70,
// color: const Color(0xFF32C59A),
// ),
);
} else {
// Error state - error icon
return Image.asset(
"assets/images/error.png",
key: const ValueKey('error'),
width: 120,
height: 120,
fit: BoxFit.contain,
);
}
}
@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,
),
),
// FIXED: AnimatedSwitcher with proper key handling
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: _buildIcon(),
layoutBuilder: (currentChild, previousChildren) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
...previousChildren,
if (currentChild != null) currentChild,
],
);
},
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
},
),
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),
// Text AnimatedSwitcher with proper key handling
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,
),
),
),
],
),
),
],
),
),
),
);
}
}
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;
}

View File

@@ -12,7 +12,7 @@ class SettingsBar extends StatelessWidget {
super.key,
required this.selectedIndex,
required this.onTap,
this.showBackButton = false,
this.showBackButton = false, //to swicth between back button and settings icons
this.onBackTap,
required this.iconPaths,
});
@@ -21,24 +21,18 @@ class SettingsBar extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
'assets/images/logo2.png',
width: 150,
height: 40,
),
Image.asset('assets/images/logo2.png', width: 150, height: 40),
],
),
// Navigation icons on the right
Row(
children: [
// Back button (only shown when showBackButton is true)
if (showBackButton)
GestureDetector(
onTap: onBackTap,
@@ -56,71 +50,56 @@ class SettingsBar extends StatelessWidget {
),
],
),
child: const Center(
child: Icon(
Icons.arrow_back,
color: Color(0xFF006838),
child: Center(
child: SvgPicture.asset(
"assets/images/back.svg",
width: 26,
height: 26,
),
),
),
),
// Add spacing if back button is shown
if (showBackButton) const SizedBox(width: 20),
// Settings and notification icons
...iconPaths.asMap().entries.map((entry) {
final index = entry.key;
final iconPath = entry.value;
final isSelected = selectedIndex == index;
return Padding(
padding: const EdgeInsets.only(left: 10),
child: GestureDetector(
onTap: () => onTap(index),
child: Container(
width: 43,
height: 43,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0x10000000),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Center(
child: Stack(
children: [
SvgPicture.asset(
iconPath,
width: 30,
height: 30,
// When back button is OFF → show user + settings icons
if (!showBackButton)
...iconPaths.asMap().entries.map((entry) {
final index = entry.key;
final iconPath = entry.value;
// final isSelected = selectedIndex == index;
return Padding(
padding: const EdgeInsets.only(left: 10),
child: GestureDetector(
onTap: () => onTap(index),
child: Container(
width: 43,
height: 43,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0x10000000),
blurRadius: 5,
offset: const Offset(0, 2),
),
if (index == 1)
Positioned(
top: 0,
right: 0,
child: Container(
width: 10,
height: 10,
),
),
],
),
child: Center(
child: Stack(
children: [
SvgPicture.asset(iconPath, width: 30, height: 30),
],
),
),
),
),
),
);
}).toList(),
);
}),
],
),
],
),
);
}
}
}

View File

@@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.13.0"
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
@@ -85,10 +85,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.2"
ffi:
dependency: transitive
description:
@@ -172,10 +172,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev"
source: hosted
version: "10.0.9"
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
@@ -361,10 +361,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev"
source: hosted
version: "15.0.0"
version: "14.3.1"
web:
dependency: transitive
description: