diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f948eaf..ebada8e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -15,7 +15,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 + flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5 diff --git a/lib/screens/attendence_screen.dart b/lib/screens/attendence_screen.dart index 08c0762..9aa76b9 100644 --- a/lib/screens/attendence_screen.dart +++ b/lib/screens/attendence_screen.dart @@ -8,29 +8,30 @@ class AttendanceScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final screenWidth = MediaQuery.sizeOf(context).width; + final screenHeight = MediaQuery.sizeOf(context).height; return Directionality( - textDirection: TextDirection.rtl, + textDirection: TextDirection.ltr, child: Stack( children: [ - + SizedBox(height: MediaQuery.of(context).size.height + + ), /// ------------------------------ /// SETTINGS BAR (STATIC) /// ------------------------------ SafeArea( - child: Padding( - padding: const EdgeInsets.only(top: 10), - child: SettingsBar( - selectedIndex: 0, - showBackButton: false, - iconPaths: [ - 'assets/images/user.svg', - 'assets/images/ball.svg', - ], - onTap: (index) { - // Keep static, no animations - // You can navigate or add your logic later - }, - ), + child: SettingsBar( + selectedIndex: 0, + showBackButton: false, + iconPaths: [ + 'assets/images/user.svg', + 'assets/images/ball.svg', + ], + onTap: (index) { + // Keep static, no animations + // You can navigate or add your logic later + }, ), ), @@ -38,7 +39,7 @@ class AttendanceScreen extends StatelessWidget { /// GREETING TEXT /// ------------------------------ Positioned( - top: 70, // moved down because settings bar now exists + top: screenHeight * 0.14, // moved down because settings bar now exists left: 0, right: 0, child: Center( @@ -60,45 +61,48 @@ class AttendanceScreen extends StatelessWidget { /// MAIN CARD AREA /// ------------------------------ Positioned( - top: 160, // pushed down because of settings bar + greeting + top: screenHeight * 0.2, // pushed down because of settings bar + greeting left: 0, right: 0, child: Center( - child: Stack( - children: [ - Container( - height: 420, - width: 260, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(32), - boxShadow: [ - BoxShadow( - color: Color(0x1F2B2B2B), - blurRadius: 5, - offset: Offset(10, -10), - ), - BoxShadow( - color: Color(0xABCECECE), - blurRadius: 5, - offset: Offset(-2, 5), - ), - BoxShadow( - color: Color.fromARGB(148, 2, 70, 35), - blurRadius: 80, - offset: Offset(0, 10), - ), - ], + child: Padding( + padding: EdgeInsets.symmetric(vertical: screenHeight * 0.05), + child: Stack( + children: [ + Container( + height: screenHeight * 0.5, + width: screenWidth * 0.7, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(32), + boxShadow: [ + BoxShadow( + color: Color(0x1F2B2B2B), + blurRadius: 5, + offset: Offset(10, -10), + ), + BoxShadow( + color: Color(0xABCECECE), + blurRadius: 5, + offset: Offset(-2, 5), + ), + BoxShadow( + color: Color.fromARGB(148, 2, 70, 35), + blurRadius: 80, + offset: Offset(0, 10), + ), + ], + ), ), - ), - Container( - height: 420, - width: 260, - decoration: BoxDecoration( - color: Color(0x92757575), - borderRadius: BorderRadius.circular(32), + Container( + height: screenHeight * 0.5, + width: screenWidth * 0.7, + decoration: BoxDecoration( + color: Color(0x92757575), + borderRadius: BorderRadius.circular(32), + ), ), - ), - ], + ], + ), ), ), ), @@ -107,8 +111,8 @@ class AttendanceScreen extends StatelessWidget { /// LOGIN BUTTON /// ------------------------------ Positioned( - top: 130, - left: 24, + top: screenHeight * 0.21, + left: screenWidth * 0.05, child: _ShadowedCard( shadow: [ BoxShadow( @@ -139,8 +143,8 @@ class AttendanceScreen extends StatelessWidget { /// LOGOUT BUTTON /// ------------------------------ Positioned( - bottom: 60, - right: 24, + bottom: screenHeight * 0.16, + right: screenWidth * 0.1, child: _ShadowedCard( shadow: [ BoxShadow( diff --git a/lib/screens/finance_screen.dart b/lib/screens/finance_screen.dart index 7eed5fb..63c7576 100644 --- a/lib/screens/finance_screen.dart +++ b/lib/screens/finance_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import '../widgets/finance_summary_card.dart'; import '../widgets/work_day_card.dart'; import '../widgets/settings_bar.dart'; @@ -15,25 +14,31 @@ class FinanceScreen extends StatefulWidget { class _FinanceScreenState extends State { String dropdownValue = "الكل"; - bool _showSettings = true; // local control of settings bar + late ScrollController scrollController; + + @override + void initState() { + super.initState(); + scrollController = ScrollController(); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return Directionality( - textDirection: TextDirection.rtl, - child: Stack( - children: [ - /// -------------------------------------------------- - /// SETTINGS BAR (STATIC, INSIDE THE SCREEN) - /// -------------------------------------------------- - AnimatedPositioned( - duration: const Duration(milliseconds: 250), - top: _showSettings ? 0 : -80, - left: 0, - right: 0, - child: SafeArea( - child: Padding( - padding: const EdgeInsets.only(top: 8), + textDirection: TextDirection.ltr, + child: SafeArea( + child: CustomScrollView( + + controller: scrollController, + physics: const BouncingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( child: SettingsBar( selectedIndex: 0, showBackButton: false, @@ -46,55 +51,38 @@ class _FinanceScreenState extends State { }, ), ), - ), - ), - - /// -------------------------------------------------- - /// MAIN CONTENT - LIST - /// -------------------------------------------------- - Positioned.fill( - top: 70, // space for SettingsBar - child: NotificationListener( - onNotification: (notif) { - if (notif.direction == ScrollDirection.reverse) { - setState(() => _showSettings = false); - widget.onScrollEvent?.call(true); - } else if (notif.direction == ScrollDirection.forward) { - setState(() => _showSettings = true); - widget.onScrollEvent?.call(false); - } - return true; - }, - - child: ListView( - padding: const EdgeInsets.only( - top: 0, - bottom: 120), - children: [ - /// SUMMARY CARD - FinanceSummaryCard( - totalAmount: "333,000", - dropdownValue: dropdownValue, - onCalendarTap: () => showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(2020), - lastDate: DateTime(2030), - ), - onDropdownChanged: (value) { - setState(() => dropdownValue = value!); - }, + const SliverToBoxAdapter(child: SizedBox(height: 5)), + + /// SUMMARY CARD + SliverToBoxAdapter( + child: FinanceSummaryCard( + totalAmount: "333,000", + dropdownValue: dropdownValue, + onCalendarTap: () => showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(2020), + lastDate: DateTime(2030), ), - - /// WORK DAY CARDS - const WorkDayCard(), - const WorkDayCard(), - const WorkDayCard(), - ], + onDropdownChanged: (value) { + setState(() => dropdownValue = value!); + }, + ), ), - ), - ), - ], + + /// WORK DAY CARDS + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return const WorkDayCard(); + }, + childCount: 3, + ), + ), + + const SliverToBoxAdapter(child: SizedBox(height: 120)), + ], + ), ), ); } diff --git a/lib/screens/holiday_screen.dart b/lib/screens/holiday_screen.dart index d048d3d..5cfa8d2 100644 --- a/lib/screens/holiday_screen.dart +++ b/lib/screens/holiday_screen.dart @@ -27,7 +27,6 @@ class _HolidayScreenState extends State { final ScrollController _scrollController = ScrollController(); bool _showActions = true; - bool _showSettings = true; // NEW — local control for SettingsBar @override void initState() { @@ -39,24 +38,27 @@ class _HolidayScreenState extends State { if (direction == ScrollDirection.reverse) { if (_showActions) setState(() => _showActions = false); - setState(() => _showSettings = false); widget.onScrollEvent?.call(true); } else if (direction == ScrollDirection.forward) { if (!_showActions) setState(() => _showActions = true); - setState(() => _showSettings = true); widget.onScrollEvent?.call(false); } if (_scrollController.position.pixels <= 5) { setState(() { _showActions = true; - _showSettings = true; }); widget.onScrollEvent?.call(false); } }); } + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + void _initializeData() async { _leaveRequests = await _requestService.getLeaveRequests(); _advanceRequests = await _requestService.getAdvanceRequests(); @@ -73,118 +75,114 @@ class _HolidayScreenState extends State { @override Widget build(BuildContext context) { return Directionality( - textDirection: TextDirection.rtl, + textDirection: TextDirection.ltr, child: Stack( children: [ // --------------------------------------------------------- - // ⭐ SETTINGS BAR (now inside Holiday screen) + // ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW // --------------------------------------------------------- - AnimatedPositioned( - duration: const Duration(milliseconds: 250), - top: _showSettings ? 0 : -80, - left: 0, - right: 0, - child: SafeArea( - child: Padding( - padding: const EdgeInsets.only(top: 8), - child: SettingsBar( - selectedIndex: 0, - showBackButton: false, - iconPaths: [ - 'assets/images/user.svg', - 'assets/images/ball.svg', - ], - onTap: (index) { - // do your navigation logic - }, - ), - ), - ), - ), - - // --------------------------------------------------------- - // ⭐ TABS SECTION - // Directly under SettingsBar, NO GAP - // --------------------------------------------------------- - Positioned( - top: 70, - left: 0, - right: 0, - height: 55, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // الأجازات - 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), - ), - ], + SafeArea( + child: CustomScrollView( + controller: _scrollController, + physics: const BouncingScrollPhysics(), + slivers: [ + // SETTINGS BAR + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(top: 8), + child: SettingsBar( + selectedIndex: 0, + showBackButton: false, + iconPaths: [ + 'assets/images/user.svg', + 'assets/images/ball.svg', + ], + onTap: (index) { + // do your navigation logic + }, + ), ), ), - const SizedBox(width: 70), + const SliverToBoxAdapter(child: SizedBox(height: 5)), - // السلف - 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), + // TABS SECTION + SliverToBoxAdapter( + child: SizedBox( + height: 55, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // الأجازات + 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), + ), + ], + ), ), - ), - if (activeTab == 0) - Container( - width: 60, - height: 2, - margin: const EdgeInsets.only(top: 4), - color: const Color(0xFF8EFDC2), + + const SizedBox(width: 70), + + // السلف + 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), + ), + ], + ), ), - ], + ], + ), ), ), + + const SliverToBoxAdapter(child: SizedBox(height: 20)), + + // CONTENT LISTS + activeTab == 1 + ? _buildLeaveRequestsSliver() + : _buildAdvanceRequestsSliver(), + + const SliverToBoxAdapter(child: SizedBox(height: 120)), ], ), ), - // --------------------------------------------------------- - // ⭐ CONTENT AREA (LISTS) - // Starts right under the TABS - // --------------------------------------------------------- - Positioned.fill( - top: 130, - child: - activeTab == 1 - ? _buildLeaveRequestsTab() - : _buildAdvanceRequestsTab(), - ), - // --------------------------------------------------------- // ⭐ FLOATING ACTION BUTTONS // --------------------------------------------------------- @@ -239,42 +237,68 @@ class _HolidayScreenState extends State { } // ---------------------------------------------------------------- - // LISTS + // SLIVERS FOR LISTS // ---------------------------------------------------------------- - Widget _buildLeaveRequestsTab() { + Widget _buildLeaveRequestsSliver() { if (_leaveRequests.isEmpty) { - return const Center( - child: - Text("لا توجد طلبات أجازة", style: TextStyle(color: Colors.white)), + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Center( + child: Text( + "لا توجد طلبات أجازة", + style: const TextStyle(color: Colors.white), + ), + ), + ), ); } - return ListView.builder( - controller: _scrollController, - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - itemCount: _leaveRequests.length, - itemBuilder: (context, index) { - return _buildLeaveRequestCard(_leaveRequests[index]); - }, + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 25), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: _buildLeaveRequestCard(_leaveRequests[index]), + ); + }, + childCount: _leaveRequests.length, + ), + ), ); } - Widget _buildAdvanceRequestsTab() { + Widget _buildAdvanceRequestsSliver() { if (_advanceRequests.isEmpty) { - return const Center( - child: - Text("لا توجد طلبات سلف", style: TextStyle(color: Colors.white)), + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Center( + child: Text( + "لا توجد طلبات سلف", + style: const TextStyle(color: Colors.white), + ), + ), + ), ); } - return ListView.builder( - controller: _scrollController, - padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), - itemCount: _advanceRequests.length, - itemBuilder: (context, index) { - return _buildAdvanceRequestCard(_advanceRequests[index]); - }, + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 25), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: _buildAdvanceRequestCard(_advanceRequests[index]), + ); + }, + childCount: _advanceRequests.length, + ), + ), ); } @@ -297,7 +321,6 @@ class _HolidayScreenState extends State { textDirection: TextDirection.rtl, child: Container( width: double.infinity, - margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), decoration: BoxDecoration( color: bgColor, diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 93fc486..b18b962 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -15,38 +15,36 @@ class MainPage extends StatefulWidget { class _MainPageState extends State { int _currentIndex = 0; - Widget _buildScreen() { - switch (_currentIndex) { - case 0: - return const AttendanceScreen(); - case 1: - return const FinanceScreen(); // no need for scroll callback anymore - case 2: - return const HolidayScreen(); // SettingsBar is inside the screen now - default: - return const AttendanceScreen(); - } - } + @override Widget build(BuildContext context) { + final screenHeight = MediaQuery.sizeOf(context).height; return Scaffold( body: Stack( children: [ + /// BACKGROUND const AppBackground(child: SizedBox()), - /// ACTIVE SCREEN (fills everything except navbar area) + /// ACTIVE SCREEN (fills entire screen - content will extend behind navbar) + /// Positioned.fill( - bottom: 100, // space for floating navbar - child: _buildScreen(), + child: IndexedStack( + index: _currentIndex, + children: [ + const AttendanceScreen(), + const FinanceScreen(), + const HolidayScreen(), + ], + ), ), - /// FLOATING NAVBAR + /// FLOATING NAVBAR (positioned above content) Positioned( left: 0, right: 0, - bottom: 20, + bottom: 0, child: Floatingnavbar( items: [ NavBarItem( diff --git a/lib/widgets/FloatingNavBar.dart b/lib/widgets/FloatingNavBar.dart index f4db02d..f1b126f 100644 --- a/lib/widgets/FloatingNavBar.dart +++ b/lib/widgets/FloatingNavBar.dart @@ -44,6 +44,14 @@ class Floatingnavbar extends StatelessWidget { color: const Color(0x4DFFFFFF), // subtle glass border width: 1.2, ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + blurRadius: 20, + offset: const Offset(0, -5), + spreadRadius: 0, + ), + ], ), child: Row( diff --git a/pubspec.lock b/pubspec.lock index ed985c9..e42a4d5 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: @@ -85,10 +85,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: @@ -172,10 +172,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: @@ -361,10 +361,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: