From 1e39210a29f78c9b5717b6fb433867aad996e565 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Fri, 31 Oct 2025 16:05:43 +0530 Subject: [PATCH] feat: Implement expense by status skeleton loader for improved loading experience --- .../dashbaord/expense_by_status_widget.dart | 3 +- lib/helpers/widgets/my_custom_skeleton.dart | 135 ++++++++++++++++++ lib/view/dashboard/dashboard_screen.dart | 2 +- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart b/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart index 264ab34..7c67c7f 100644 --- a/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart +++ b/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:marco/controller/dashboard/dashboard_controller.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/utils.dart'; +import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; class ExpenseByStatusWidget extends StatelessWidget { final DashboardController controller; @@ -49,7 +50,7 @@ class ExpenseByStatusWidget extends StatelessWidget { final data = controller.pendingExpensesData.value; if (controller.isPendingExpensesLoading.value) { - return const Center(child: CircularProgressIndicator()); + return SkeletonLoaders.expenseByStatusSkeletonLoader(); } if (data == null) { diff --git a/lib/helpers/widgets/my_custom_skeleton.dart b/lib/helpers/widgets/my_custom_skeleton.dart index 1d48e89..7578b54 100644 --- a/lib/helpers/widgets/my_custom_skeleton.dart +++ b/lib/helpers/widgets/my_custom_skeleton.dart @@ -45,6 +45,141 @@ class SkeletonLoaders { ); } +// Expense By Status Skeleton Loader + static Widget expenseByStatusSkeletonLoader() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.05), + blurRadius: 6, + spreadRadius: 1, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title + Container( + height: 16, + width: 160, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(6), + ), + ), + const SizedBox(height: 16), + + // 4 Status Rows + ...List.generate(4, (index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + children: [ + // Icon placeholder + Container( + height: 44, + width: 44, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 12), + + // Title + Amount + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 12, + width: 100, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + const SizedBox(height: 6), + Container( + height: 12, + width: 60, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + ], + ), + ), + + // Count + arrow placeholder + Container( + height: 12, + width: 30, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + const SizedBox(width: 6), + Icon(Icons.chevron_right, + color: Colors.grey.shade300, size: 24), + ], + ), + ); + }), + + const SizedBox(height: 16), + Divider(color: Colors.grey.shade300), + const SizedBox(height: 12), + + // Bottom Row (Project Spendings) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 12, + width: 120, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + const SizedBox(height: 4), + Container( + height: 10, + width: 140, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + ], + ), + Container( + height: 16, + width: 80, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + ], + ), + ], + ), + ); + } + // Chart Skeleton Loader static Widget chartSkeletonLoader() { return MyCard.bordered( diff --git a/lib/view/dashboard/dashboard_screen.dart b/lib/view/dashboard/dashboard_screen.dart index 5cb2e71..89f8d5d 100644 --- a/lib/view/dashboard/dashboard_screen.dart +++ b/lib/view/dashboard/dashboard_screen.dart @@ -16,7 +16,7 @@ import 'package:marco/view/layouts/layout.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); - + static const String employeesRoute = "/dashboard/employees"; static const String attendanceRoute = "/dashboard/attendance"; static const String directoryMainPageRoute = "/dashboard/directory-main-page";