This commit is contained in:
Mohammed Al-Samarraie
2025-12-13 17:39:24 +03:00
parent 5cdfa102f3
commit 489a99a0a3
7 changed files with 289 additions and 268 deletions

View File

@@ -15,7 +15,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5 PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5

View File

@@ -8,29 +8,30 @@ class AttendanceScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.sizeOf(context).width;
final screenHeight = MediaQuery.sizeOf(context).height;
return Directionality( return Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.ltr,
child: Stack( child: Stack(
children: [ children: [
SizedBox(height: MediaQuery.of(context).size.height
),
/// ------------------------------ /// ------------------------------
/// SETTINGS BAR (STATIC) /// SETTINGS BAR (STATIC)
/// ------------------------------ /// ------------------------------
SafeArea( SafeArea(
child: Padding( child: SettingsBar(
padding: const EdgeInsets.only(top: 10), selectedIndex: 0,
child: SettingsBar( showBackButton: false,
selectedIndex: 0, iconPaths: [
showBackButton: false, 'assets/images/user.svg',
iconPaths: [ 'assets/images/ball.svg',
'assets/images/user.svg', ],
'assets/images/ball.svg', onTap: (index) {
], // Keep static, no animations
onTap: (index) { // You can navigate or add your logic later
// Keep static, no animations },
// You can navigate or add your logic later
},
),
), ),
), ),
@@ -38,7 +39,7 @@ class AttendanceScreen extends StatelessWidget {
/// GREETING TEXT /// GREETING TEXT
/// ------------------------------ /// ------------------------------
Positioned( Positioned(
top: 70, // moved down because settings bar now exists top: screenHeight * 0.14, // moved down because settings bar now exists
left: 0, left: 0,
right: 0, right: 0,
child: Center( child: Center(
@@ -60,45 +61,48 @@ class AttendanceScreen extends StatelessWidget {
/// MAIN CARD AREA /// MAIN CARD AREA
/// ------------------------------ /// ------------------------------
Positioned( Positioned(
top: 160, // pushed down because of settings bar + greeting top: screenHeight * 0.2, // pushed down because of settings bar + greeting
left: 0, left: 0,
right: 0, right: 0,
child: Center( child: Center(
child: Stack( child: Padding(
children: [ padding: EdgeInsets.symmetric(vertical: screenHeight * 0.05),
Container( child: Stack(
height: 420, children: [
width: 260, Container(
decoration: BoxDecoration( height: screenHeight * 0.5,
borderRadius: BorderRadius.circular(32), width: screenWidth * 0.7,
boxShadow: [ decoration: BoxDecoration(
BoxShadow( borderRadius: BorderRadius.circular(32),
color: Color(0x1F2B2B2B), boxShadow: [
blurRadius: 5, BoxShadow(
offset: Offset(10, -10), color: Color(0x1F2B2B2B),
), blurRadius: 5,
BoxShadow( offset: Offset(10, -10),
color: Color(0xABCECECE), ),
blurRadius: 5, BoxShadow(
offset: Offset(-2, 5), color: Color(0xABCECECE),
), blurRadius: 5,
BoxShadow( offset: Offset(-2, 5),
color: Color.fromARGB(148, 2, 70, 35), ),
blurRadius: 80, BoxShadow(
offset: Offset(0, 10), color: Color.fromARGB(148, 2, 70, 35),
), blurRadius: 80,
], offset: Offset(0, 10),
),
],
),
), ),
), Container(
Container( height: screenHeight * 0.5,
height: 420, width: screenWidth * 0.7,
width: 260, decoration: BoxDecoration(
decoration: BoxDecoration( color: Color(0x92757575),
color: Color(0x92757575), borderRadius: BorderRadius.circular(32),
borderRadius: BorderRadius.circular(32), ),
), ),
), ],
], ),
), ),
), ),
), ),
@@ -107,8 +111,8 @@ class AttendanceScreen extends StatelessWidget {
/// LOGIN BUTTON /// LOGIN BUTTON
/// ------------------------------ /// ------------------------------
Positioned( Positioned(
top: 130, top: screenHeight * 0.21,
left: 24, left: screenWidth * 0.05,
child: _ShadowedCard( child: _ShadowedCard(
shadow: [ shadow: [
BoxShadow( BoxShadow(
@@ -139,8 +143,8 @@ class AttendanceScreen extends StatelessWidget {
/// LOGOUT BUTTON /// LOGOUT BUTTON
/// ------------------------------ /// ------------------------------
Positioned( Positioned(
bottom: 60, bottom: screenHeight * 0.16,
right: 24, right: screenWidth * 0.1,
child: _ShadowedCard( child: _ShadowedCard(
shadow: [ shadow: [
BoxShadow( BoxShadow(

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../widgets/finance_summary_card.dart'; import '../widgets/finance_summary_card.dart';
import '../widgets/work_day_card.dart'; import '../widgets/work_day_card.dart';
import '../widgets/settings_bar.dart'; import '../widgets/settings_bar.dart';
@@ -15,25 +14,31 @@ class FinanceScreen extends StatefulWidget {
class _FinanceScreenState extends State<FinanceScreen> { class _FinanceScreenState extends State<FinanceScreen> {
String dropdownValue = "الكل"; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Directionality( return Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.ltr,
child: Stack( child: SafeArea(
children: [ child: CustomScrollView(
/// --------------------------------------------------
/// SETTINGS BAR (STATIC, INSIDE THE SCREEN) controller: scrollController,
/// -------------------------------------------------- physics: const BouncingScrollPhysics(),
AnimatedPositioned( slivers: [
duration: const Duration(milliseconds: 250), SliverToBoxAdapter(
top: _showSettings ? 0 : -80,
left: 0,
right: 0,
child: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: SettingsBar( child: SettingsBar(
selectedIndex: 0, selectedIndex: 0,
showBackButton: false, showBackButton: false,
@@ -46,55 +51,38 @@ class _FinanceScreenState extends State<FinanceScreen> {
}, },
), ),
), ),
), const SliverToBoxAdapter(child: SizedBox(height: 5)),
),
/// -------------------------------------------------- /// SUMMARY CARD
/// MAIN CONTENT - LIST SliverToBoxAdapter(
/// -------------------------------------------------- child: FinanceSummaryCard(
Positioned.fill( totalAmount: "333,000",
top: 70, // space for SettingsBar dropdownValue: dropdownValue,
child: NotificationListener<UserScrollNotification>( onCalendarTap: () => showDatePicker(
onNotification: (notif) { context: context,
if (notif.direction == ScrollDirection.reverse) { initialDate: DateTime.now(),
setState(() => _showSettings = false); firstDate: DateTime(2020),
widget.onScrollEvent?.call(true); lastDate: DateTime(2030),
} 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!);
},
), ),
onDropdownChanged: (value) {
/// WORK DAY CARDS setState(() => dropdownValue = value!);
const WorkDayCard(), },
const WorkDayCard(), ),
const WorkDayCard(),
],
), ),
),
), /// WORK DAY CARDS
], SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return const WorkDayCard();
},
childCount: 3,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 120)),
],
),
), ),
); );
} }

View File

@@ -27,7 +27,6 @@ class _HolidayScreenState extends State<HolidayScreen> {
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
bool _showActions = true; bool _showActions = true;
bool _showSettings = true; // NEW — local control for SettingsBar
@override @override
void initState() { void initState() {
@@ -39,24 +38,27 @@ class _HolidayScreenState extends State<HolidayScreen> {
if (direction == ScrollDirection.reverse) { if (direction == ScrollDirection.reverse) {
if (_showActions) setState(() => _showActions = false); if (_showActions) setState(() => _showActions = false);
setState(() => _showSettings = false);
widget.onScrollEvent?.call(true); widget.onScrollEvent?.call(true);
} else if (direction == ScrollDirection.forward) { } else if (direction == ScrollDirection.forward) {
if (!_showActions) setState(() => _showActions = true); if (!_showActions) setState(() => _showActions = true);
setState(() => _showSettings = true);
widget.onScrollEvent?.call(false); widget.onScrollEvent?.call(false);
} }
if (_scrollController.position.pixels <= 5) { if (_scrollController.position.pixels <= 5) {
setState(() { setState(() {
_showActions = true; _showActions = true;
_showSettings = true;
}); });
widget.onScrollEvent?.call(false); widget.onScrollEvent?.call(false);
} }
}); });
} }
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _initializeData() async { void _initializeData() async {
_leaveRequests = await _requestService.getLeaveRequests(); _leaveRequests = await _requestService.getLeaveRequests();
_advanceRequests = await _requestService.getAdvanceRequests(); _advanceRequests = await _requestService.getAdvanceRequests();
@@ -73,118 +75,114 @@ class _HolidayScreenState extends State<HolidayScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Directionality( return Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.ltr,
child: Stack( child: Stack(
children: [ children: [
// --------------------------------------------------------- // ---------------------------------------------------------
// ⭐ SETTINGS BAR (now inside Holiday screen) // ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW
// --------------------------------------------------------- // ---------------------------------------------------------
AnimatedPositioned( SafeArea(
duration: const Duration(milliseconds: 250), child: CustomScrollView(
top: _showSettings ? 0 : -80, controller: _scrollController,
left: 0, physics: const BouncingScrollPhysics(),
right: 0, slivers: [
child: SafeArea( // SETTINGS BAR
child: Padding( SliverToBoxAdapter(
padding: const EdgeInsets.only(top: 8), child: Padding(
child: SettingsBar( padding: const EdgeInsets.only(top: 8),
selectedIndex: 0, child: SettingsBar(
showBackButton: false, selectedIndex: 0,
iconPaths: [ showBackButton: false,
'assets/images/user.svg', iconPaths: [
'assets/images/ball.svg', 'assets/images/user.svg',
], 'assets/images/ball.svg',
onTap: (index) { ],
// do your navigation logic 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),
),
],
), ),
), ),
const SizedBox(width: 70), const SliverToBoxAdapter(child: SizedBox(height: 5)),
// السلف // TABS SECTION
GestureDetector( SliverToBoxAdapter(
onTap: () => setState(() => activeTab = 0), child: SizedBox(
child: Column( height: 55,
children: [ child: Row(
Text( mainAxisAlignment: MainAxisAlignment.center,
"السلف", children: [
style: TextStyle( // الأجازات
fontSize: 22, GestureDetector(
fontWeight: FontWeight.w600, onTap: () => setState(() => activeTab = 1),
color: child: Column(
activeTab == 0 children: [
? const Color(0xFF8EFDC2) Text(
: const Color(0x9EFFFFFF), "الأجازات",
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) const SizedBox(width: 70),
Container(
width: 60, // السلف
height: 2, GestureDetector(
margin: const EdgeInsets.only(top: 4), onTap: () => setState(() => activeTab = 0),
color: const Color(0xFF8EFDC2), 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 // ⭐ FLOATING ACTION BUTTONS
// --------------------------------------------------------- // ---------------------------------------------------------
@@ -239,42 +237,68 @@ class _HolidayScreenState extends State<HolidayScreen> {
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// LISTS // SLIVERS FOR LISTS
// ---------------------------------------------------------------- // ----------------------------------------------------------------
Widget _buildLeaveRequestsTab() { Widget _buildLeaveRequestsSliver() {
if (_leaveRequests.isEmpty) { if (_leaveRequests.isEmpty) {
return const Center( return SliverToBoxAdapter(
child: child: Padding(
Text("لا توجد طلبات أجازة", style: TextStyle(color: Colors.white)), padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
"لا توجد طلبات أجازة",
style: const TextStyle(color: Colors.white),
),
),
),
); );
} }
return ListView.builder( return SliverPadding(
controller: _scrollController, padding: const EdgeInsets.symmetric(horizontal: 25),
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), sliver: SliverList(
itemCount: _leaveRequests.length, delegate: SliverChildBuilderDelegate(
itemBuilder: (context, index) { (context, index) {
return _buildLeaveRequestCard(_leaveRequests[index]); return Padding(
}, padding: const EdgeInsets.only(bottom: 16),
child: _buildLeaveRequestCard(_leaveRequests[index]),
);
},
childCount: _leaveRequests.length,
),
),
); );
} }
Widget _buildAdvanceRequestsTab() { Widget _buildAdvanceRequestsSliver() {
if (_advanceRequests.isEmpty) { if (_advanceRequests.isEmpty) {
return const Center( return SliverToBoxAdapter(
child: child: Padding(
Text("لا توجد طلبات سلف", style: TextStyle(color: Colors.white)), padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
"لا توجد طلبات سلف",
style: const TextStyle(color: Colors.white),
),
),
),
); );
} }
return ListView.builder( return SliverPadding(
controller: _scrollController, padding: const EdgeInsets.symmetric(horizontal: 25),
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), sliver: SliverList(
itemCount: _advanceRequests.length, delegate: SliverChildBuilderDelegate(
itemBuilder: (context, index) { (context, index) {
return _buildAdvanceRequestCard(_advanceRequests[index]); return Padding(
}, padding: const EdgeInsets.only(bottom: 16),
child: _buildAdvanceRequestCard(_advanceRequests[index]),
);
},
childCount: _advanceRequests.length,
),
),
); );
} }
@@ -297,7 +321,6 @@ class _HolidayScreenState extends State<HolidayScreen> {
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: Container( child: Container(
width: double.infinity, width: double.infinity,
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
color: bgColor, color: bgColor,

View File

@@ -15,38 +15,36 @@ class MainPage extends StatefulWidget {
class _MainPageState extends State<MainPage> { class _MainPageState extends State<MainPage> {
int _currentIndex = 0; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenHeight = MediaQuery.sizeOf(context).height;
return Scaffold( return Scaffold(
body: Stack( body: Stack(
children: [ children: [
/// BACKGROUND /// BACKGROUND
const AppBackground(child: SizedBox()), const AppBackground(child: SizedBox()),
/// ACTIVE SCREEN (fills everything except navbar area) /// ACTIVE SCREEN (fills entire screen - content will extend behind navbar)
///
Positioned.fill( Positioned.fill(
bottom: 100, // space for floating navbar child: IndexedStack(
child: _buildScreen(), index: _currentIndex,
children: [
const AttendanceScreen(),
const FinanceScreen(),
const HolidayScreen(),
],
),
), ),
/// FLOATING NAVBAR /// FLOATING NAVBAR (positioned above content)
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
bottom: 20, bottom: 0,
child: Floatingnavbar( child: Floatingnavbar(
items: [ items: [
NavBarItem( NavBarItem(

View File

@@ -44,6 +44,14 @@ class Floatingnavbar extends StatelessWidget {
color: const Color(0x4DFFFFFF), // subtle glass border color: const Color(0x4DFFFFFF), // subtle glass border
width: 1.2, width: 1.2,
), ),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 20,
offset: const Offset(0, -5),
spreadRadius: 0,
),
],
), ),
child: Row( child: Row(

View File

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