From 2ac504754e9e3c09b25220bd530158a9896e35db Mon Sep 17 00:00:00 2001 From: Daniah Ayad Al-sultani <148902945+Cactuskiller@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:19:30 +0300 Subject: [PATCH] navbar and setting bar is done --- assets/images/attendance.svg | 3 + assets/images/ball.svg | 5 + assets/images/finance.svg | 12 ++ assets/images/holiday.svg | 11 + assets/images/user.svg | 3 + lib/screens/attendence_screen.dart | 24 +++ lib/screens/auth_screen.dart | 16 +- lib/screens/finance_screen.dart | 19 ++ lib/screens/holiday_screen.dart | 19 ++ lib/screens/main_screen.dart | 79 +++++++ lib/widgets/FloatingNavBar.dart | 106 +++++++++ lib/widgets/app_background.dart | 12 +- lib/widgets/auth_form.dart | 331 ++++++++++++++++++----------- lib/widgets/settings_bar.dart | 126 +++++++++++ pubspec.yaml | 6 +- 15 files changed, 620 insertions(+), 152 deletions(-) create mode 100644 assets/images/attendance.svg create mode 100644 assets/images/ball.svg create mode 100644 assets/images/finance.svg create mode 100644 assets/images/holiday.svg create mode 100644 assets/images/user.svg create mode 100644 lib/screens/attendence_screen.dart create mode 100644 lib/screens/finance_screen.dart create mode 100644 lib/screens/holiday_screen.dart create mode 100644 lib/screens/main_screen.dart create mode 100644 lib/widgets/FloatingNavBar.dart create mode 100644 lib/widgets/settings_bar.dart diff --git a/assets/images/attendance.svg b/assets/images/attendance.svg new file mode 100644 index 0000000..6420442 --- /dev/null +++ b/assets/images/attendance.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ball.svg b/assets/images/ball.svg new file mode 100644 index 0000000..c9695d4 --- /dev/null +++ b/assets/images/ball.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/finance.svg b/assets/images/finance.svg new file mode 100644 index 0000000..c2a4a92 --- /dev/null +++ b/assets/images/finance.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/images/holiday.svg b/assets/images/holiday.svg new file mode 100644 index 0000000..1b3486c --- /dev/null +++ b/assets/images/holiday.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/user.svg b/assets/images/user.svg new file mode 100644 index 0000000..d244b44 --- /dev/null +++ b/assets/images/user.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/screens/attendence_screen.dart b/lib/screens/attendence_screen.dart new file mode 100644 index 0000000..2376fc0 --- /dev/null +++ b/lib/screens/attendence_screen.dart @@ -0,0 +1,24 @@ +// lib/screens/attendance_screen.dart +import 'package:flutter/material.dart'; + +class AttendanceScreen extends StatelessWidget { + const AttendanceScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + // This is where your attendance content will go + // For now, it's just a placeholder + child: const Center( + child: Text( + 'Attendance Content', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart index e7772a4..28a5233 100644 --- a/lib/screens/auth_screen.dart +++ b/lib/screens/auth_screen.dart @@ -8,29 +8,21 @@ class AuthScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: false, body: AppBackground( child: SafeArea( child: Column( children: [ const SizedBox(height: 60), // Logo - Center( - child: Image.asset("assets/images/logo2.png", width: 200), - ), + Center(child: Image.asset("assets/images/logo2.png", width: 200)), // const SizedBox(height: 15), // Form - taking remaining space and centered - Expanded( - child: Center( - child: SingleChildScrollView( - child: const AuthForm(), - ), - ), - ), + Expanded(child: Center(child: const AuthForm())), ], ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/screens/finance_screen.dart b/lib/screens/finance_screen.dart new file mode 100644 index 0000000..3f046e1 --- /dev/null +++ b/lib/screens/finance_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class FinanceScreen extends StatelessWidget { + const FinanceScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: Text( + 'المالية', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/holiday_screen.dart b/lib/screens/holiday_screen.dart new file mode 100644 index 0000000..4b88207 --- /dev/null +++ b/lib/screens/holiday_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class HolidayScreen extends StatelessWidget { + const HolidayScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: Text( + 'الإجازة', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart new file mode 100644 index 0000000..c7cc6c1 --- /dev/null +++ b/lib/screens/main_screen.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import '../widgets/app_background.dart'; +import '../widgets/floatingnavbar.dart'; +import '../widgets/settings_bar.dart'; +import '../screens/attendence_screen.dart'; +import '../screens/finance_screen.dart'; +import '../screens/holiday_screen.dart'; + +class MainPage extends StatefulWidget { + const MainPage({super.key}); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + int _currentIndex = 0; + int _settingsIndex = 0; + + final List _screens = [ + const AttendanceScreen(), + const FinanceScreen(), + const HolidayScreen(), + ]; + + final List _navItems = [ + NavBarItem(iconPath: 'assets/images/attendance.svg', label: 'الحضور'), + NavBarItem(iconPath: 'assets/images/finance.svg', label: 'المالية'), + NavBarItem(iconPath: 'assets/images/holiday.svg', label: 'الإجازة'), + ]; + + final List _settingsIconPaths = [ + 'assets/images/user.svg', + 'assets/images/ball.svg', + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: AppBackground( + child: SafeArea( + child: Column( + children: [ + SettingsBar( + selectedIndex: _settingsIndex, + onTap: (index) { + setState(() { + _settingsIndex = index; + }); + }, + showBackButton: false, + iconPaths: _settingsIconPaths, + ), + + // Main content area + Expanded( + child: IndexedStack(index: _currentIndex, children: _screens), + ), + + const SizedBox(height: 20), + + Floatingnavbar( + items: _navItems, + selectedIndex: _currentIndex, + onTap: (index) { + setState(() { + _currentIndex = index; + }); + }, + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/FloatingNavBar.dart b/lib/widgets/FloatingNavBar.dart new file mode 100644 index 0000000..77c2713 --- /dev/null +++ b/lib/widgets/FloatingNavBar.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class NavBarItem { + final String iconPath; + final String label; + + NavBarItem({required this.iconPath, required this.label}); +} + +class Floatingnavbar extends StatelessWidget { + final List items; + final int selectedIndex; + final ValueChanged onTap; + + const Floatingnavbar({ + super.key, + required this.items, + required this.selectedIndex, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + // Get the bottom padding to account for system navigation bar + final bottomPadding = MediaQuery.of(context).padding.bottom; + + return Container( + height: 85, + margin: EdgeInsets.only( + bottom: 10 + bottomPadding, // Add system padding to our margin + left: 25, + right: 25, + ), + decoration: BoxDecoration( + color: (const Color(0xFFE9E9E9)), + borderRadius: BorderRadius.circular(45), + + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: items.asMap().entries.map((entry) { + final index = entry.key; + final item = entry.value; + final isSelected = selectedIndex == index; + + return _NavBarItemTile( + item: item, + isSelected: isSelected, + onTap: () => onTap(index), + ); + }).toList(), + ), + ); + } +} + +class _NavBarItemTile extends StatelessWidget { + final NavBarItem item; + final bool isSelected; + final VoidCallback onTap; + + const _NavBarItemTile({ + Key? key, + required this.item, + required this.isSelected, + required this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Expanded( + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(45), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + item.iconPath, + width: 37, + height: 37, + colorFilter: ColorFilter.mode( + isSelected ? const Color(0xFF177046) : Colors.black, + BlendMode.srcIn, + ), + ), + const SizedBox(height: 6), + Text( + item.label, + style: TextStyle( + color: isSelected ? const Color(0xFF177046) : Colors.black, + fontSize: 15, + fontFamily: 'AbdEriady', // Using the custom font family + fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400, // Using specific weights + ), + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/app_background.dart b/lib/widgets/app_background.dart index 3cb3cef..2b09954 100644 --- a/lib/widgets/app_background.dart +++ b/lib/widgets/app_background.dart @@ -9,7 +9,6 @@ class AppBackground extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - /// 1️⃣ BASE GRADIENT (Exact Figma: #434343 -> #00382A) Container( decoration: const BoxDecoration( gradient: LinearGradient( @@ -23,7 +22,6 @@ class AppBackground extends StatelessWidget { ), ), - Positioned( top: -250, left: 100, @@ -33,11 +31,11 @@ class AppBackground extends StatelessWidget { decoration: const BoxDecoration( shape: BoxShape.circle, // very soft inner fill - color: Color.fromARGB(0, 62, 254, 203), + color: Color.fromARGB(0, 62, 254, 203), boxShadow: [ BoxShadow( // wide soft bloom - color: Color.fromARGB(69, 62, 254, 190), + color: Color.fromARGB(69, 62, 254, 190), blurRadius: 140, spreadRadius: 160, ), @@ -54,10 +52,10 @@ class AppBackground extends StatelessWidget { height: 320, decoration: const BoxDecoration( shape: BoxShape.circle, - color: Color.fromARGB(0, 62, 254, 203), + color: Color.fromARGB(0, 62, 254, 203), boxShadow: [ BoxShadow( - color: Color.fromARGB(83, 62, 254, 190), + color: Color.fromARGB(83, 62, 254, 190), blurRadius: 180, spreadRadius: 60, ), @@ -65,8 +63,6 @@ class AppBackground extends StatelessWidget { ), ), ), - - /// 4️⃣ CONTENT LAYER child, ], ); diff --git a/lib/widgets/auth_form.dart b/lib/widgets/auth_form.dart index 1bebaf9..d97a03a 100644 --- a/lib/widgets/auth_form.dart +++ b/lib/widgets/auth_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../screens/main_screen.dart'; import 'onboarding_button.dart'; class AuthForm extends StatefulWidget { @@ -13,120 +14,190 @@ class AuthForm extends StatefulWidget { class _AuthFormState extends State { bool _obscure = true; + // Focus nodes for text fields + late FocusNode _usernameFocusNode; + late FocusNode _passwordFocusNode; + + void _handleLogin() { + // Unfocus any focused text field + _usernameFocusNode.unfocus(); + _passwordFocusNode.unfocus(); + + // Call the onSubmit callback if provided (for any other logic you might have) + if (widget.onSubmit != null) { + widget.onSubmit!(); + } + + // Navigate to the AttendancePage + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const MainPage()), + ); + } + + @override + void initState() { + super.initState(); + // Initialize focus nodes + _usernameFocusNode = FocusNode(); + _passwordFocusNode = FocusNode(); + } + + @override + void dispose() { + // Clean up focus nodes when widget is disposed + _usernameFocusNode.dispose(); + _passwordFocusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + // Get screen dimensions + final screenSize = MediaQuery.of(context).size; + final screenWidth = screenSize.width; + final screenHeight = screenSize.height; + + // Calculate responsive dimensions + final formWidth = screenWidth > 600 ? screenWidth * 0.5 : screenWidth * 0.9; + final formHeight = + screenHeight > 800 ? screenHeight * 0.6 : screenHeight * 0.8; + final borderWidth = formWidth + 20; + final titleFontSize = screenWidth > 600 ? 28.0 : 24.0; + final labelFontSize = screenWidth > 600 ? 18.0 : 16.0; + final fieldFontSize = screenWidth > 600 ? 18.0 : 16.0; + final verticalSpacing = screenHeight > 800 ? 34.0 : 24.0; + final fieldSpacing = screenHeight > 800 ? 30.0 : 20.0; + final buttonSpacing = screenHeight > 800 ? 100.0 : 60.0; + final bottomSpacing = screenHeight > 800 ? 40.0 : 20.0; + final horizontalPadding = screenWidth > 600 ? 30.0 : 25.0; + final verticalPadding = screenHeight > 800 ? 38.0 : 28.0; + return Directionality( textDirection: TextDirection.rtl, - child: Stack( - alignment: Alignment.center, - children: [ - // Border container - decorative element behind the form - Container( - width: 360, - constraints: const BoxConstraints( - minHeight: 500, - maxHeight: 580, // Allows shrinking when keyboard opens - ), - decoration: BoxDecoration( - color: Colors.transparent, - borderRadius: BorderRadius.circular(32), - border: Border.all( - color: const Color(0xDD00C28E), - width: 1, + child: FocusScope( + child: Stack( + alignment: Alignment.center, + children: [ + // Border container - decorative element behind the form + Container( + width: borderWidth, + constraints: BoxConstraints( + minHeight: formHeight + 40, + maxHeight: + formHeight + 80, // Allows shrinking when keyboard opens + ), + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(32), + border: Border.all(color: const Color(0xDD00C28E), width: 1), ), ), - ), - - // Main form container - Container( - width: 340, - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 38), - decoration: BoxDecoration( - color: const Color(0xFFEEFFFA), - borderRadius: BorderRadius.circular(28), - boxShadow: const [ - BoxShadow( - color: Colors.black26, - blurRadius: 10, - offset: Offset(0, 5), - ), - ], + + // Main form container + Container( + width: formWidth, + padding: EdgeInsets.symmetric( + horizontal: horizontalPadding, + vertical: verticalPadding, + ), + decoration: BoxDecoration( + color: const Color(0xFFEEFFFA), + borderRadius: BorderRadius.circular(28), + boxShadow: const [ + BoxShadow( + color: Colors.black26, + blurRadius: 10, + offset: Offset(0, 5), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + /// Title + Center( + child: Text( + "تسجيل دخول", + style: TextStyle( + fontSize: titleFontSize, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ), + + SizedBox(height: verticalSpacing), + + /// Username Label + Align( + alignment: Alignment.centerRight, + child: Text( + "اسم المستخدم", + style: TextStyle( + fontSize: labelFontSize, + color: Colors.black87, + ), + ), + ), + const SizedBox(height: 8), + + _buildField( + hint: "اسم المستخدم", + obscure: false, + focusNode: _usernameFocusNode, + textInputAction: TextInputAction.next, + onSubmitted: (_) { + // Move focus to password field when next is pressed + FocusScope.of(context).requestFocus(_passwordFocusNode); + }, + fontSize: fieldFontSize, + ), + + SizedBox(height: fieldSpacing), + + /// Password Label + Align( + alignment: Alignment.centerRight, + child: Text( + "كلمة المرور", + style: TextStyle( + fontSize: labelFontSize, + color: Colors.black87, + ), + ), + ), + const SizedBox(height: 8), + + _buildField( + hint: "كلمة المرور", + obscure: _obscure, + hasEye: true, + focusNode: _passwordFocusNode, + textInputAction: TextInputAction.done, + onSubmitted: + (_) => + _handleLogin(), // Added parentheses to call the method + fontSize: fieldFontSize, + ), + + SizedBox(height: buttonSpacing), // Responsive spacing + + Center( + child: OnboardingButton( + text: "تسجيل دخول", + backgroundColor: const Color.fromARGB(239, 35, 87, 74), + onPressed: _handleLogin, + ), + ), + + SizedBox(height: bottomSpacing), + ], + ), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - /// Title - const Center( - child: Text( - "تسجيل دخول", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ), - - const SizedBox(height: 34), - - /// Username Label - const Align( - alignment: Alignment.centerRight, - child: Text( - "اسم المستخدم", - style: TextStyle( - fontSize: 16, - color: Colors.black87, - ), - ), - ), - const SizedBox(height: 8), - - _buildField( - hint: "اسم المستخدم", - obscure: false, - ), - - const SizedBox(height: 30), - - /// Password Label - const Align( - alignment: Alignment.centerRight, - child: Text( - "كلمة المرور", - style: TextStyle( - fontSize: 16, - color: Colors.black87, - ), - ), - ), - const SizedBox(height: 8), - - _buildField( - hint: "كلمة المرور", - obscure: _obscure, - hasEye: true, - ), - - const SizedBox(height: 100), // Restored original spacing - - Center( - child: OnboardingButton( - text: "تسجيل دخول", - backgroundColor: const Color.fromARGB(239, 35, 87, 74), - onPressed: widget.onSubmit, - - - ), - - ), - - const SizedBox(height: 40), - ], - ), - ), - ], + ], + ), ), ); } @@ -135,42 +206,48 @@ class _AuthFormState extends State { required String hint, required bool obscure, bool hasEye = false, + FocusNode? focusNode, + TextInputAction? textInputAction, + Function(String)? onSubmitted, + required double fontSize, }) { return Container( decoration: BoxDecoration( color: const Color(0xDEDEDEDE), borderRadius: BorderRadius.circular(7), boxShadow: const [ - BoxShadow( - color: Colors.black26, - blurRadius: 6, - offset: Offset(0, 2), - ), + BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 2)), ], ), child: TextField( + focusNode: focusNode, obscureText: obscure, textAlign: TextAlign.right, - style: const TextStyle(fontSize: 16), + textInputAction: textInputAction, + onSubmitted: onSubmitted, + style: TextStyle(fontSize: fontSize), decoration: InputDecoration( hintText: hint, hintStyle: const TextStyle(color: Colors.black54), border: InputBorder.none, - contentPadding: - const EdgeInsets.symmetric(vertical: 16, horizontal: 16), - suffixIcon: hasEye - ? IconButton( - icon: Icon( - obscure ? Icons.visibility_off : Icons.visibility, - color: Colors.black54, - ), - onPressed: () { - setState(() => _obscure = !obscure); - }, - ) - : null, + contentPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + suffixIcon: + hasEye + ? IconButton( + icon: Icon( + obscure ? Icons.visibility_off : Icons.visibility, + color: Colors.black54, + ), + onPressed: () { + setState(() => _obscure = !obscure); + }, + ) + : null, ), ), ); } -} \ No newline at end of file +} diff --git a/lib/widgets/settings_bar.dart b/lib/widgets/settings_bar.dart new file mode 100644 index 0000000..ea38677 --- /dev/null +++ b/lib/widgets/settings_bar.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class SettingsBar extends StatelessWidget { + final int selectedIndex; + final ValueChanged onTap; + final bool showBackButton; + final VoidCallback? onBackTap; + final List iconPaths; + + const SettingsBar({ + super.key, + required this.selectedIndex, + required this.onTap, + this.showBackButton = false, + this.onBackTap, + required this.iconPaths, + }); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.transparent, + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + 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, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: const Color(0x10000000), + blurRadius: 5, + offset: const Offset(0, 2), + ), + ], + ), + child: const Center( + child: Icon( + Icons.arrow_back, + color: Color(0xFF006838), + ), + ), + ), + ), + + // 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, + + ), + if (index == 1) + Positioned( + top: 0, + right: 0, + child: Container( + width: 10, + height: 10, + ), + ), + ], + ), + ), + ), + ), + ); + }).toList(), + ], + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 3d18b21..ba1a38a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,11 +21,7 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/images/splash.png - - assets/images/logo.png - - assets/images/logo2.png - - assets/images/Onboarding1.svg - - assets/images/Onboarding2.svg + - assets/images/ fonts: - family: AbdElRady fonts: