From 405916bb48bc46aa3e2a993a1589835b676a03de Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Thu, 19 Jun 2025 16:42:24 +0530 Subject: [PATCH] feat: Add custom skeleton loaders for employee list and daily progress report screens --- lib/helpers/widgets/my_custom_skeleton.dart | 197 ++++++++++++++++++ .../Attendence/attendance_screen.dart | 81 +------ lib/view/employees/employees_screen.dart | 4 +- lib/view/taskPlaning/daily_progress.dart | 3 +- lib/view/taskPlaning/daily_task_planing.dart | 3 +- 5 files changed, 207 insertions(+), 81 deletions(-) create mode 100644 lib/helpers/widgets/my_custom_skeleton.dart diff --git a/lib/helpers/widgets/my_custom_skeleton.dart b/lib/helpers/widgets/my_custom_skeleton.dart new file mode 100644 index 0000000..241ca2a --- /dev/null +++ b/lib/helpers/widgets/my_custom_skeleton.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; +import 'package:marco/helpers/widgets/my_card.dart'; +import 'package:marco/helpers/widgets/my_spacing.dart'; +import 'package:marco/helpers/utils/my_shadow.dart'; + +class SkeletonLoaders { + // Employee List - Card Style + static Widget employeeListSkeletonLoader() { + return Column( + children: List.generate(4, (index) { + return MyCard.bordered( + borderRadiusAll: 12, + paddingAll: 10, + margin: MySpacing.bottom(12), + shadow: MyShadow(elevation: 3), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Avatar + Container( + width: 41, + height: 41, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + ), + MySpacing.width(16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container(height: 14, width: 100, color: Colors.grey.shade300), + MySpacing.width(8), + Container(height: 12, width: 60, color: Colors.grey.shade300), + ], + ), + MySpacing.height(8), + Row( + children: [ + Icon(Icons.email, size: 16, color: Colors.grey.shade300), + MySpacing.width(4), + Container(height: 10, width: 140, color: Colors.grey.shade300), + ], + ), + MySpacing.height(8), + Row( + children: [ + Icon(Icons.phone, size: 16, color: Colors.grey.shade300), + MySpacing.width(4), + Container(height: 10, width: 100, color: Colors.grey.shade300), + ], + ), + ], + ), + ), + ], + ), + ); + }), + ); + } + + // Employee List - Compact Collapsed Style + static Widget employeeListCollapsedSkeletonLoader() { + return MyCard.bordered( + borderRadiusAll: 4, + paddingAll: 8, + child: Column( + children: List.generate(4, (index) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + // Avatar + Container( + width: 31, + height: 31, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + ), + MySpacing.width(16), + // Name, Designation & Buttons + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container(height: 12, width: 100, color: Colors.grey.shade300), + MySpacing.height(8), + Container(height: 10, width: 80, color: Colors.grey.shade300), + MySpacing.height(12), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container(height: 28, width: 60, color: Colors.grey.shade300), + MySpacing.width(8), + Container(height: 28, width: 60, color: Colors.grey.shade300), + ], + ), + ], + ), + ), + ], + ), + ), + if (index != 3) + Divider( + color: Colors.grey.withOpacity(0.3), + thickness: 1, + height: 1, + ), + ], + ); + }), + ), + ); + } + + // Daily Progress Report Header Loader + static Widget dailyProgressReportSkeletonLoader() { + return MyCard.bordered( + borderRadiusAll: 4, + border: Border.all(color: Colors.grey.withOpacity(0.2)), + shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom), + paddingAll: 8, + child: Column( + children: List.generate(3, (index) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container(height: 14, width: 120, color: Colors.grey.shade300), + Icon(Icons.add_circle, color: Colors.grey.shade300), + ], + ), + if (index != 2) ...[ + MySpacing.height(12), + Divider(color: Colors.grey.withOpacity(0.3), thickness: 1), + MySpacing.height(12), + ], + ], + ); + }), + ), + ); + } + + // Daily Progress Planning (Collapsed View) + static Widget dailyProgressPlanningSkeletonCollapsedOnly() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate(3, (index) { + return MyCard.bordered( + borderRadiusAll: 12, + paddingAll: 16, + margin: MySpacing.bottom(12), + shadow: MyShadow(elevation: 3), + child: Row( + children: [ + // Icon placeholder + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + ), + MySpacing.width(12), + // Text line + Expanded( + child: Container(height: 16, color: Colors.grey.shade300), + ), + MySpacing.width(12), + // Expand button placeholder + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey.shade300, + ), + ), + ], + ), + ); + }), + ); + } +} diff --git a/lib/view/dashboard/Attendence/attendance_screen.dart b/lib/view/dashboard/Attendence/attendance_screen.dart index a1c55b0..3974484 100644 --- a/lib/view/dashboard/Attendence/attendance_screen.dart +++ b/lib/view/dashboard/Attendence/attendance_screen.dart @@ -18,6 +18,7 @@ import 'package:marco/model/attendance/attendence_action_button.dart'; import 'package:marco/model/attendance/regualrize_action_button.dart'; import 'package:marco/model/attendance/attendence_filter_sheet.dart'; import 'package:marco/controller/project_controller.dart'; +import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; class AttendanceScreen extends StatefulWidget { AttendanceScreen({super.key}); @@ -304,7 +305,7 @@ class _AttendanceScreenState extends State with UIMixin { ), ), if (isLoading) - employeeListSkeletonLoader() + SkeletonLoaders.employeeListSkeletonLoader() else if (employees.isEmpty) SizedBox( height: 120, @@ -450,80 +451,6 @@ class _AttendanceScreenState extends State with UIMixin { }); } - Widget employeeListSkeletonLoader() { - return MyCard.bordered( - borderRadiusAll: 4, - paddingAll: 8, - child: Column( - children: List.generate(4, (index) { - return Column( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 8), - child: Row( - children: [ - // Avatar placeholder - Container( - width: 31, - height: 31, - decoration: BoxDecoration( - color: Colors.grey.shade300, - shape: BoxShape.circle, - ), - ), - MySpacing.width(16), - // Employee name/designation & buttons - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 12, - width: 100, - color: Colors.grey.shade300, - ), - MySpacing.height(8), - Container( - height: 10, - width: 80, - color: Colors.grey.shade300, - ), - MySpacing.height(12), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Container( - height: 28, - width: 60, - color: Colors.grey.shade300, - ), - MySpacing.width(8), - Container( - height: 28, - width: 60, - color: Colors.grey.shade300, - ), - ], - ), - ], - ), - ), - ], - ), - ), - if (index != 3) - Divider( - color: Colors.grey.withOpacity(0.3), - thickness: 1, - height: 1, - ), - ], - ); - }), - ), - ); - } - Widget employeeLog() { return Obx(() { final logs = List.of(attendanceController.attendanceLogs); @@ -574,7 +501,7 @@ class _AttendanceScreenState extends State with UIMixin { ), ), if (attendanceController.isLoadingAttendanceLogs.value) - employeeListSkeletonLoader() + SkeletonLoaders.employeeListSkeletonLoader() else if (logs.isEmpty) SizedBox( height: 120, @@ -759,7 +686,7 @@ class _AttendanceScreenState extends State with UIMixin { Obx(() { final employees = attendanceController.regularizationLogs; if (attendanceController.isLoadingRegularizationLogs.value) { - return employeeListSkeletonLoader(); + return SkeletonLoaders.employeeListSkeletonLoader(); } if (employees.isEmpty) { diff --git a/lib/view/employees/employees_screen.dart b/lib/view/employees/employees_screen.dart index 6cc3960..37bbc28 100644 --- a/lib/view/employees/employees_screen.dart +++ b/lib/view/employees/employees_screen.dart @@ -14,7 +14,7 @@ import 'package:marco/controller/dashboard/employees_screen_controller.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/model/employees/employee_detail_bottom_sheet.dart'; import 'package:marco/controller/project_controller.dart'; - +import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; class EmployeesScreen extends StatefulWidget { const EmployeesScreen({super.key}); @@ -292,7 +292,7 @@ class _EmployeesScreenState extends State with UIMixin { final isLoading = employeeScreenController.isLoading.value; final employees = employeeScreenController.employees; if (isLoading) { - return const Center(child: CircularProgressIndicator()); + return SkeletonLoaders.employeeListSkeletonLoader(); } if (employees.isEmpty) { return Padding( diff --git a/lib/view/taskPlaning/daily_progress.dart b/lib/view/taskPlaning/daily_progress.dart index 8ffaf22..7076987 100644 --- a/lib/view/taskPlaning/daily_progress.dart +++ b/lib/view/taskPlaning/daily_progress.dart @@ -14,6 +14,7 @@ import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/model/dailyTaskPlaning/task_action_buttons.dart'; +import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; class DailyProgressReportScreen extends StatefulWidget { const DailyProgressReportScreen({super.key}); @@ -297,7 +298,7 @@ class _DailyProgressReportScreenState extends State final groupedTasks = dailyTaskController.groupedDailyTasks; if (isLoading) { - return const Center(child: CircularProgressIndicator()); + return SkeletonLoaders.dailyProgressReportSkeletonLoader(); } if (groupedTasks.isEmpty) { diff --git a/lib/view/taskPlaning/daily_task_planing.dart b/lib/view/taskPlaning/daily_task_planing.dart index eb37766..2b1c2d2 100644 --- a/lib/view/taskPlaning/daily_task_planing.dart +++ b/lib/view/taskPlaning/daily_task_planing.dart @@ -11,6 +11,7 @@ import 'package:marco/controller/task_planing/daily_task_planing_controller.dart import 'package:marco/controller/project_controller.dart'; import 'package:percent_indicator/percent_indicator.dart'; import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart'; +import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; class DailyTaskPlaningScreen extends StatefulWidget { DailyTaskPlaningScreen({super.key}); @@ -170,7 +171,7 @@ class _DailyTaskPlaningScreenState extends State final dailyTasks = dailyTaskPlaningController.dailyTasks; if (isLoading) { - return Center(child: CircularProgressIndicator()); + return SkeletonLoaders.dailyProgressPlanningSkeletonCollapsedOnly(); } if (dailyTasks.isEmpty) {