From 3a9e7ca8db4b4004688eae325a5ca1b4defa4b1e Mon Sep 17 00:00:00 2001 From: Daniah Ayad Al-sultani <148902945+Cactuskiller@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:19:53 +0300 Subject: [PATCH] loading indicater has been added --- lib/presentation/screens/finance_screen.dart | 285 ++++++++++--------- lib/presentation/screens/holiday_screen.dart | 212 +++++++------- 2 files changed, 260 insertions(+), 237 deletions(-) diff --git a/lib/presentation/screens/finance_screen.dart b/lib/presentation/screens/finance_screen.dart index c621642..6db3ddd 100644 --- a/lib/presentation/screens/finance_screen.dart +++ b/lib/presentation/screens/finance_screen.dart @@ -51,19 +51,24 @@ class _FinanceScreenState extends State { void _triggerLoad() { if (_employeeId != null && _employeeId!.isNotEmpty) { - if (_financeBloc.state is FinanceInitial) { - _financeBloc.add( - LoadFinanceDataEvent( - employeeId: _employeeId!, - category: currentCategory, - month: selectedDate.month, - year: selectedDate.year, - ), - ); - } + _financeBloc.add( + LoadFinanceDataEvent( + employeeId: _employeeId!, + category: currentCategory, + month: selectedDate.month, + year: selectedDate.year, + ), + ); } } + Future _onRefresh() async { + _triggerLoad(); + await _financeBloc.stream.firstWhere( + (state) => state is FinanceLoaded || state is FinanceError, + ); + } + @override Widget build(BuildContext context) { return BlocProvider.value( @@ -71,145 +76,153 @@ class _FinanceScreenState extends State { child: Directionality( textDirection: TextDirection.ltr, child: SafeArea( - child: CustomScrollView( - controller: scrollController, - physics: const BouncingScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: SettingsBar( - selectedIndex: 0, - showBackButton: false, - iconPaths: const [ - 'assets/images/user.svg', - 'assets/images/ball.svg', - ], - onTap: (index) { - if (index == 0) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const UserSettingsScreen(), - ), - ); - } else if (index == 1) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const NotificationsScreen(), - ), - ); - } - }, - ), + child: RefreshIndicator( + onRefresh: _onRefresh, + color: const Color(0xFF0A6B4A), + child: CustomScrollView( + controller: scrollController, + physics: const AlwaysScrollableScrollPhysics( + parent: BouncingScrollPhysics(), ), - const SliverToBoxAdapter(child: SizedBox(height: 5)), - - /// SUMMARY CARD - SliverToBoxAdapter( - child: BlocBuilder( - buildWhen: (previous, current) => current is FinanceLoaded, - builder: (context, state) { - String amount = "0"; - if (state is FinanceLoaded) { - amount = state.netSalary.toStringAsFixed(0); - amount = amount.replaceAllMapped( - RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), - (Match m) => '${m[1]},', - ); - } - - return FinanceSummaryCard( - totalAmount: amount, - currentCategory: currentCategory, - onCalendarTap: () async { - final date = await showDatePicker( - context: context, - initialDate: selectedDate, - firstDate: DateTime(2020), - lastDate: DateTime(2030), + slivers: [ + SliverToBoxAdapter( + child: SettingsBar( + selectedIndex: 0, + showBackButton: false, + iconPaths: const [ + 'assets/images/user.svg', + 'assets/images/ball.svg', + ], + onTap: (index) { + if (index == 0) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const UserSettingsScreen(), + ), ); - if (date != null && mounted) { - setState(() => selectedDate = date); - if (_employeeId != null && _employeeId!.isNotEmpty) { - _financeBloc.add( - LoadFinanceDataEvent( - employeeId: _employeeId!, - category: currentCategory, - month: selectedDate.month, - year: selectedDate.year, - ), - ); - } - } - }, - onCategoryChanged: (category) { - if (category != null) { - setState(() => currentCategory = category); - if (_employeeId != null && _employeeId!.isNotEmpty) { - _financeBloc.add( - LoadFinanceDataEvent( - employeeId: _employeeId!, - category: currentCategory, - month: selectedDate.month, - year: selectedDate.year, - ), - ); - } - } - }, - ); - }, + } else if (index == 1) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const NotificationsScreen(), + ), + ); + } + }, + ), ), - ), + const SliverToBoxAdapter(child: SizedBox(height: 5)), - /// DATA LIST - BlocBuilder( - builder: (context, state) { - if (state is FinanceLoading || state is FinanceInitial) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), - ), - ); - } else if (state is FinanceLoaded) { - if (state.records.isEmpty) { + /// SUMMARY CARD + SliverToBoxAdapter( + child: BlocBuilder( + buildWhen: (previous, current) => current is FinanceLoaded, + builder: (context, state) { + String amount = "0"; + if (state is FinanceLoaded) { + amount = state.netSalary.toStringAsFixed(0); + amount = amount.replaceAllMapped( + RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), + (Match m) => '${m[1]},', + ); + } + + return FinanceSummaryCard( + totalAmount: amount, + currentCategory: currentCategory, + onCalendarTap: () async { + final date = await showDatePicker( + context: context, + initialDate: selectedDate, + firstDate: DateTime(2020), + lastDate: DateTime(2030), + ); + if (date != null && mounted) { + setState(() => selectedDate = date); + if (_employeeId != null && + _employeeId!.isNotEmpty) { + _financeBloc.add( + LoadFinanceDataEvent( + employeeId: _employeeId!, + category: currentCategory, + month: selectedDate.month, + year: selectedDate.year, + ), + ); + } + } + }, + onCategoryChanged: (category) { + if (category != null) { + setState(() => currentCategory = category); + if (_employeeId != null && + _employeeId!.isNotEmpty) { + _financeBloc.add( + LoadFinanceDataEvent( + employeeId: _employeeId!, + category: currentCategory, + month: selectedDate.month, + year: selectedDate.year, + ), + ); + } + } + }, + ); + }, + ), + ), + + /// DATA LIST + BlocBuilder( + builder: (context, state) { + if (state is FinanceLoading || state is FinanceInitial) { return const SliverToBoxAdapter( child: Center( child: Padding( padding: EdgeInsets.all(20.0), - child: Text("لا توجد سجلات"), + child: CircularProgressIndicator(), + ), + ), + ); + } else if (state is FinanceLoaded) { + if (state.records.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text("لا توجد سجلات"), + ), + ), + ); + } + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return WorkDayCard(record: state.records[index]); + }, childCount: state.records.length), + ); + } else if (state is FinanceError) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: Text( + state.message, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ), ), ), ); } - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return WorkDayCard(record: state.records[index]); - }, childCount: state.records.length), - ); - } else if (state is FinanceError) { - return SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text( - state.message, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ), - ), - ), - ); - } - return const SliverToBoxAdapter(child: SizedBox()); - }, - ), + return const SliverToBoxAdapter(child: SizedBox()); + }, + ), - const SliverToBoxAdapter(child: SizedBox(height: 120)), - ], + const SliverToBoxAdapter(child: SizedBox(height: 120)), + ], + ), ), ), ), diff --git a/lib/presentation/screens/holiday_screen.dart b/lib/presentation/screens/holiday_screen.dart index 94e35bc..54dfbc7 100644 --- a/lib/presentation/screens/holiday_screen.dart +++ b/lib/presentation/screens/holiday_screen.dart @@ -223,6 +223,10 @@ class _HolidayScreenState extends State { ); } + Future _onRefresh() async { + await Future.wait([_loadVacationsFromAPI(), _loadAdvancesFromAPI()]); + } + AdvanceRequest _convertAdvanceToAdvanceRequest(AdvanceDataModel advance) { // Convert state (0=waiting, 1=approved, 2=denied) to status string String status = "waiting"; @@ -248,123 +252,129 @@ class _HolidayScreenState extends State { // ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW // --------------------------------------------------------- SafeArea( - child: CustomScrollView( - controller: _scrollController, - physics: const BouncingScrollPhysics(), - slivers: [ - // SETTINGS BAR - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.only(top: 8), - // First, make sure you have your screens defined + child: RefreshIndicator( + onRefresh: _onRefresh, + color: const Color(0xFF0A6B4A), + child: CustomScrollView( + controller: _scrollController, + physics: const AlwaysScrollableScrollPhysics( + parent: BouncingScrollPhysics(), + ), + slivers: [ + // SETTINGS BAR + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(top: 8), + // First, make sure you have your screens defined - // Then in your main widget: - child: SettingsBar( - selectedIndex: 0, - showBackButton: false, - iconPaths: [ - 'assets/images/user.svg', - 'assets/images/ball.svg', - ], - onTap: (index) { - if (index == 0) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserSettingsScreen(), - ), - ); - } else if (index == 1) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => NotificationsScreen(), - ), - ); - } - }, + // Then in your main widget: + child: SettingsBar( + selectedIndex: 0, + showBackButton: false, + iconPaths: [ + 'assets/images/user.svg', + 'assets/images/ball.svg', + ], + onTap: (index) { + if (index == 0) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserSettingsScreen(), + ), + ); + } else if (index == 1) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationsScreen(), + ), + ); + } + }, + ), ), ), - ), - const SliverToBoxAdapter(child: SizedBox(height: 5)), + const SliverToBoxAdapter(child: SizedBox(height: 5)), - // 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), + // 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 == 1) + Container( + width: 60, + height: 2, + margin: const EdgeInsets.only(top: 4), + color: const Color(0xFF8EFDC2), + ), + ], + ), ), - ), - const SizedBox(width: 70), + 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), + // السلف + 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), - ), - ], + if (activeTab == 0) + Container( + width: 60, + height: 2, + margin: const EdgeInsets.only(top: 4), + color: const Color(0xFF8EFDC2), + ), + ], + ), ), - ), - ], + ], + ), ), ), - ), - const SliverToBoxAdapter(child: SizedBox(height: 20)), + const SliverToBoxAdapter(child: SizedBox(height: 20)), - // CONTENT LISTS - activeTab == 1 - ? _buildLeaveRequestsSliver() - : _buildAdvanceRequestsSliver(), + // CONTENT LISTS + activeTab == 1 + ? _buildLeaveRequestsSliver() + : _buildAdvanceRequestsSliver(), - const SliverToBoxAdapter(child: SizedBox(height: 120)), - ], + const SliverToBoxAdapter(child: SizedBox(height: 120)), + ], + ), ), ),