Refactor dashboard screen layout and improve loading state handling
- Simplified initialization of DynamicMenuController. - Added loading skeleton for employee quick action cards. - Removed daily task planning and daily progress report from card order. - Adjusted grid layout parameters for better responsiveness. - Cleaned up code formatting for improved readability.
This commit is contained in:
parent
3dfa6e5877
commit
8fb32c7c8e
@ -5,7 +5,8 @@ import 'package:on_field_work/helpers/utils/my_shadow.dart';
|
|||||||
|
|
||||||
class SkeletonLoaders {
|
class SkeletonLoaders {
|
||||||
static Widget buildLoadingSkeleton() {
|
static Widget buildLoadingSkeleton() {
|
||||||
return SizedBox(
|
return ShimmerEffect(
|
||||||
|
child: SizedBox(
|
||||||
height: 360,
|
height: 360,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: List.generate(5, (index) {
|
children: List.generate(5, (index) {
|
||||||
@ -30,10 +31,111 @@ class SkeletonLoaders {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget attendanceQuickCardSkeleton() {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.grey.shade300.withOpacity(0.3),
|
||||||
|
Colors.grey.shade300.withOpacity(0.6),
|
||||||
|
],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Row with avatar and texts
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
// Avatar
|
||||||
|
Container(
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MySpacing.width(10),
|
||||||
|
// Name + designation
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 12,
|
||||||
|
width: 100,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
MySpacing.height(6),
|
||||||
|
Container(
|
||||||
|
height: 10,
|
||||||
|
width: 70,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Status
|
||||||
|
Container(
|
||||||
|
height: 12,
|
||||||
|
width: 60,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// Description
|
||||||
|
Container(
|
||||||
|
height: 10,
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
MySpacing.height(6),
|
||||||
|
Container(
|
||||||
|
height: 10,
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// Action buttons
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 28,
|
||||||
|
width: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MySpacing.width(8),
|
||||||
|
Container(
|
||||||
|
height: 28,
|
||||||
|
width: 28,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inside SkeletonLoaders class
|
|
||||||
static Widget dashboardCardsSkeleton({double? maxWidth}) {
|
static Widget dashboardCardsSkeleton({double? maxWidth}) {
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
double width = maxWidth ?? constraints.maxWidth;
|
double width = maxWidth ?? constraints.maxWidth;
|
||||||
@ -50,6 +152,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 4,
|
paddingAll: 4,
|
||||||
borderRadiusAll: 5,
|
borderRadiusAll: 5,
|
||||||
border: Border.all(color: Colors.grey.withOpacity(0.15)),
|
border: Border.all(color: Colors.grey.withOpacity(0.15)),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -69,13 +172,13 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inside SkeletonLoaders class
|
|
||||||
static Widget paymentRequestListSkeletonLoader() {
|
static Widget paymentRequestListSkeletonLoader() {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
||||||
@ -83,7 +186,8 @@ class SkeletonLoaders {
|
|||||||
separatorBuilder: (_, __) =>
|
separatorBuilder: (_, __) =>
|
||||||
Divider(color: Colors.grey.shade300, height: 20),
|
Divider(color: Colors.grey.shade300, height: 20),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Container(
|
return ShimmerEffect(
|
||||||
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -163,12 +267,12 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this inside SkeletonLoaders class
|
|
||||||
static Widget paymentRequestDetailSkeletonLoader() {
|
static Widget paymentRequestDetailSkeletonLoader() {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 30),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 30),
|
||||||
@ -179,6 +283,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 16,
|
paddingAll: 16,
|
||||||
borderRadiusAll: 8,
|
borderRadiusAll: 8,
|
||||||
shadow: MyShadow(elevation: 3),
|
shadow: MyShadow(elevation: 3),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -265,7 +370,8 @@ class SkeletonLoaders {
|
|||||||
MySpacing.width(12),
|
MySpacing.width(12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 14,
|
height: 14,
|
||||||
@ -296,10 +402,10 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Employee Detail Skeleton Loader
|
|
||||||
static Widget employeeDetailSkeletonLoader() {
|
static Widget employeeDetailSkeletonLoader() {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 20, 12, 80),
|
padding: const EdgeInsets.fromLTRB(12, 20, 12, 80),
|
||||||
@ -312,6 +418,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 16,
|
paddingAll: 16,
|
||||||
margin: MySpacing.bottom(16),
|
margin: MySpacing.bottom(16),
|
||||||
shadow: MyShadow(elevation: 2),
|
shadow: MyShadow(elevation: 2),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// Avatar
|
// Avatar
|
||||||
@ -350,6 +457,7 @@ class SkeletonLoaders {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
// Sections skeleton
|
// Sections skeleton
|
||||||
...List.generate(
|
...List.generate(
|
||||||
@ -361,6 +469,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 16,
|
paddingAll: 16,
|
||||||
margin: MySpacing.bottom(16),
|
margin: MySpacing.bottom(16),
|
||||||
shadow: MyShadow(elevation: 2),
|
shadow: MyShadow(elevation: 2),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -387,8 +496,8 @@ class SkeletonLoaders {
|
|||||||
...List.generate(
|
...List.generate(
|
||||||
2,
|
2,
|
||||||
(_) => Padding(
|
(_) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding:
|
||||||
vertical: 12),
|
const EdgeInsets.symmetric(vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
CrossAxisAlignment.start,
|
CrossAxisAlignment.start,
|
||||||
@ -429,14 +538,15 @@ class SkeletonLoaders {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daily Progress Planning - Infra (Expanded) Skeleton Loader
|
|
||||||
static Widget dailyProgressPlanningInfraSkeleton() {
|
static Widget dailyProgressPlanningInfraSkeleton() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -446,6 +556,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 5,
|
paddingAll: 5,
|
||||||
margin: MySpacing.bottom(10),
|
margin: MySpacing.bottom(10),
|
||||||
shadow: MyShadow(elevation: 1.5),
|
shadow: MyShadow(elevation: 1.5),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -505,7 +616,8 @@ class SkeletonLoaders {
|
|||||||
height: 10,
|
height: 10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.shade300,
|
color: Colors.grey.shade300,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius:
|
||||||
|
BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -521,12 +633,12 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chart Skeleton Loader (Donut Chart)
|
|
||||||
static Widget chartSkeletonLoader() {
|
static Widget chartSkeletonLoader() {
|
||||||
return MyCard.bordered(
|
return MyCard.bordered(
|
||||||
paddingAll: 16,
|
paddingAll: 16,
|
||||||
@ -535,6 +647,7 @@ class SkeletonLoaders {
|
|||||||
elevation: 1.5,
|
elevation: 1.5,
|
||||||
position: MyShadowPosition.bottom,
|
position: MyShadowPosition.bottom,
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -550,8 +663,7 @@ class SkeletonLoaders {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// Donut Skeleton Placeholder
|
// Donut Skeleton Placeholder
|
||||||
Expanded(
|
Center(
|
||||||
child: Center(
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 180,
|
width: 180,
|
||||||
height: 180,
|
height: 180,
|
||||||
@ -561,7 +673,6 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
@ -582,22 +693,23 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date Skeleton Loader
|
|
||||||
static Widget dateSkeletonLoader() {
|
static Widget dateSkeletonLoader() {
|
||||||
return Container(
|
return ShimmerEffect(
|
||||||
|
child: Container(
|
||||||
height: 14,
|
height: 14,
|
||||||
width: 90,
|
width: 90,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.shade300,
|
color: Colors.grey.shade300,
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expense By Status Skeleton Loader
|
|
||||||
static Widget expenseByStatusSkeletonLoader() {
|
static Widget expenseByStatusSkeletonLoader() {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@ -613,6 +725,7 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -729,10 +842,10 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Document List Skeleton Loader
|
|
||||||
static Widget documentSkeletonLoader() {
|
static Widget documentSkeletonLoader() {
|
||||||
return Column(
|
return Column(
|
||||||
children: List.generate(5, (index) {
|
children: List.generate(5, (index) {
|
||||||
@ -742,6 +855,7 @@ class SkeletonLoaders {
|
|||||||
// Date placeholder
|
// Date placeholder
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 12,
|
height: 12,
|
||||||
width: 80,
|
width: 80,
|
||||||
@ -751,6 +865,7 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
// Document Card Skeleton
|
// Document Card Skeleton
|
||||||
Container(
|
Container(
|
||||||
@ -767,6 +882,7 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -820,13 +936,13 @@ class SkeletonLoaders {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Document Details Card Skeleton Loader
|
|
||||||
static Widget documentDetailsSkeletonLoader() {
|
static Widget documentDetailsSkeletonLoader() {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@ -848,6 +964,7 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -929,12 +1046,14 @@ class SkeletonLoaders {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// Versions section skeleton
|
// Versions section skeleton
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12),
|
margin: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: List.generate(3, (index) {
|
children: List.generate(3, (index) {
|
||||||
@ -983,12 +1102,12 @@ class SkeletonLoaders {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Employee List - Card Style
|
|
||||||
static Widget employeeListSkeletonLoader() {
|
static Widget employeeListSkeletonLoader() {
|
||||||
return Column(
|
return Column(
|
||||||
children: List.generate(4, (index) {
|
children: List.generate(4, (index) {
|
||||||
@ -997,6 +1116,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 10,
|
paddingAll: 10,
|
||||||
margin: MySpacing.bottom(12),
|
margin: MySpacing.bottom(12),
|
||||||
shadow: MyShadow(elevation: 3),
|
shadow: MyShadow(elevation: 3),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -1022,7 +1142,9 @@ class SkeletonLoaders {
|
|||||||
color: Colors.grey.shade300),
|
color: Colors.grey.shade300),
|
||||||
MySpacing.width(8),
|
MySpacing.width(8),
|
||||||
Container(
|
Container(
|
||||||
height: 12, width: 60, color: Colors.grey.shade300),
|
height: 12,
|
||||||
|
width: 60,
|
||||||
|
color: Colors.grey.shade300),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
MySpacing.height(8),
|
MySpacing.height(8),
|
||||||
@ -1054,16 +1176,17 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Employee List - Compact Collapsed Style
|
|
||||||
static Widget employeeListCollapsedSkeletonLoader() {
|
static Widget employeeListCollapsedSkeletonLoader() {
|
||||||
return MyCard.bordered(
|
return MyCard.bordered(
|
||||||
borderRadiusAll: 4,
|
borderRadiusAll: 4,
|
||||||
paddingAll: 8,
|
paddingAll: 8,
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: List.generate(4, (index) {
|
children: List.generate(4, (index) {
|
||||||
return Column(
|
return Column(
|
||||||
@ -1127,16 +1250,17 @@ class SkeletonLoaders {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daily Progress Report Header Loader
|
|
||||||
static Widget dailyProgressReportSkeletonLoader() {
|
static Widget dailyProgressReportSkeletonLoader() {
|
||||||
return MyCard.bordered(
|
return MyCard.bordered(
|
||||||
borderRadiusAll: 4,
|
borderRadiusAll: 4,
|
||||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||||
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
||||||
paddingAll: 8,
|
paddingAll: 8,
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: List.generate(3, (index) {
|
children: List.generate(3, (index) {
|
||||||
return Column(
|
return Column(
|
||||||
@ -1158,11 +1282,10 @@ class SkeletonLoaders {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daily Progress Planning (Collapsed View)
|
|
||||||
|
|
||||||
static Widget dailyProgressPlanningSkeletonCollapsedOnly() {
|
static Widget dailyProgressPlanningSkeletonCollapsedOnly() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -1172,6 +1295,7 @@ class SkeletonLoaders {
|
|||||||
paddingAll: 16,
|
paddingAll: 16,
|
||||||
margin: MySpacing.bottom(12),
|
margin: MySpacing.bottom(12),
|
||||||
shadow: MyShadow(elevation: 3),
|
shadow: MyShadow(elevation: 3),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// Icon placeholder
|
// Icon placeholder
|
||||||
@ -1200,6 +1324,7 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1208,11 +1333,12 @@ class SkeletonLoaders {
|
|||||||
static Widget expenseListSkeletonLoader() {
|
static Widget expenseListSkeletonLoader() {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 80),
|
||||||
itemCount: 6, // Show 6 skeleton items
|
itemCount: 6,
|
||||||
separatorBuilder: (_, __) =>
|
separatorBuilder: (_, __) =>
|
||||||
Divider(color: Colors.grey.shade300, height: 20),
|
Divider(color: Colors.grey.shade300, height: 20),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Column(
|
return ShimmerEffect(
|
||||||
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Title and Amount
|
// Title and Amount
|
||||||
@ -1261,6 +1387,7 @@ class SkeletonLoaders {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1275,6 +1402,7 @@ class SkeletonLoaders {
|
|||||||
elevation: 1.5,
|
elevation: 1.5,
|
||||||
position: MyShadowPosition.bottom,
|
position: MyShadowPosition.bottom,
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -1294,7 +1422,8 @@ class SkeletonLoaders {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(height: 12, width: 120, color: Colors.grey.shade300),
|
Container(
|
||||||
|
height: 12, width: 120, color: Colors.grey.shade300),
|
||||||
MySpacing.height(6),
|
MySpacing.height(6),
|
||||||
Container(height: 10, width: 80, color: Colors.grey.shade300),
|
Container(height: 10, width: 80, color: Colors.grey.shade300),
|
||||||
MySpacing.height(8),
|
MySpacing.height(8),
|
||||||
@ -1339,9 +1468,11 @@ class SkeletonLoaders {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Arrow
|
// Arrow
|
||||||
Icon(Icons.arrow_forward_ios, size: 14, color: Colors.grey.shade300),
|
Icon(Icons.arrow_forward_ios,
|
||||||
|
size: 14, color: Colors.grey.shade300),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1354,6 +1485,7 @@ class SkeletonLoaders {
|
|||||||
elevation: 1.5,
|
elevation: 1.5,
|
||||||
position: MyShadowPosition.bottom,
|
position: MyShadowPosition.bottom,
|
||||||
),
|
),
|
||||||
|
child: ShimmerEffect(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -1396,6 +1528,88 @@ class SkeletonLoaders {
|
|||||||
Container(height: 10, width: 120, color: Colors.grey.shade300),
|
Container(height: 10, width: 120, color: Colors.grey.shade300),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A custom reusable Shimmer Effect widget.
|
||||||
|
/// This creates a gradient wave animation over any child widget.
|
||||||
|
class ShimmerEffect extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final Color baseColor;
|
||||||
|
final Color highlightColor;
|
||||||
|
|
||||||
|
ShimmerEffect({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
|
Color? baseColor,
|
||||||
|
Color? highlightColor,
|
||||||
|
}) : baseColor = baseColor ?? Colors.grey.shade300,
|
||||||
|
highlightColor = highlightColor ?? Colors.grey.shade100,
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ShimmerEffect> createState() => _ShimmerEffectState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShimmerEffectState extends State<ShimmerEffect>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(seconds: 2), // Adjust speed here
|
||||||
|
)..repeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
child: widget.child,
|
||||||
|
builder: (context, child) {
|
||||||
|
return ShaderMask(
|
||||||
|
blendMode: BlendMode.srcATop,
|
||||||
|
shaderCallback: (bounds) {
|
||||||
|
final gradient = LinearGradient(
|
||||||
|
colors: [
|
||||||
|
widget.baseColor,
|
||||||
|
widget.highlightColor,
|
||||||
|
widget.baseColor,
|
||||||
|
],
|
||||||
|
stops: const [0.1, 0.5, 0.9],
|
||||||
|
begin: const Alignment(-1.0, -0.3),
|
||||||
|
end: const Alignment(1.0, 0.3),
|
||||||
|
transform: _SlidingGradientTransform(_controller.value),
|
||||||
|
);
|
||||||
|
return gradient.createShader(bounds);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SlidingGradientTransform extends GradientTransform {
|
||||||
|
final double percent;
|
||||||
|
|
||||||
|
const _SlidingGradientTransform(this.percent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
|
||||||
|
// This moves the gradient across the width of the widget
|
||||||
|
return Matrix4.translationValues(
|
||||||
|
(bounds.width * 2) * percent - bounds.width, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -31,8 +31,7 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
Get.put(DashboardController(), permanent: true);
|
Get.put(DashboardController(), permanent: true);
|
||||||
final AttendanceController attendanceController =
|
final AttendanceController attendanceController =
|
||||||
Get.put(AttendanceController());
|
Get.put(AttendanceController());
|
||||||
final DynamicMenuController menuController =
|
final DynamicMenuController menuController = Get.put(DynamicMenuController());
|
||||||
Get.put(DynamicMenuController());
|
|
||||||
final ProjectController projectController = Get.find<ProjectController>();
|
final ProjectController projectController = Get.find<ProjectController>();
|
||||||
|
|
||||||
bool hasMpin = true;
|
bool hasMpin = true;
|
||||||
@ -97,6 +96,11 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
children: [
|
children: [
|
||||||
_sectionTitle('Quick Action'),
|
_sectionTitle('Quick Action'),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
|
if (dashboardController.isLoadingEmployees.value) {
|
||||||
|
// Show loading skeleton
|
||||||
|
return SkeletonLoaders.attendanceQuickCardSkeleton();
|
||||||
|
}
|
||||||
|
|
||||||
final employees = dashboardController.employees;
|
final employees = dashboardController.employees;
|
||||||
final employee = employees.isNotEmpty ? employees.first : null;
|
final employee = employees.isNotEmpty ? employees.first : null;
|
||||||
|
|
||||||
@ -121,6 +125,7 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actual employee quick action card
|
||||||
final bool isCheckedIn = employee.checkIn != null;
|
final bool isCheckedIn = employee.checkIn != null;
|
||||||
final bool isCheckedOut = employee.checkOut != null;
|
final bool isCheckedOut = employee.checkOut != null;
|
||||||
|
|
||||||
@ -233,8 +238,6 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
final List<String> cardOrder = [
|
final List<String> cardOrder = [
|
||||||
MenuItems.attendance,
|
MenuItems.attendance,
|
||||||
MenuItems.employees,
|
MenuItems.employees,
|
||||||
MenuItems.dailyTaskPlanning,
|
|
||||||
MenuItems.dailyProgressReport,
|
|
||||||
MenuItems.directory,
|
MenuItems.directory,
|
||||||
MenuItems.finance,
|
MenuItems.finance,
|
||||||
MenuItems.documents,
|
MenuItems.documents,
|
||||||
@ -247,10 +250,6 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
_DashboardCardMeta(LucideIcons.scan_face, contentTheme.success),
|
_DashboardCardMeta(LucideIcons.scan_face, contentTheme.success),
|
||||||
MenuItems.employees:
|
MenuItems.employees:
|
||||||
_DashboardCardMeta(LucideIcons.users, contentTheme.warning),
|
_DashboardCardMeta(LucideIcons.users, contentTheme.warning),
|
||||||
MenuItems.dailyTaskPlanning:
|
|
||||||
_DashboardCardMeta(LucideIcons.logs, contentTheme.info),
|
|
||||||
MenuItems.dailyProgressReport:
|
|
||||||
_DashboardCardMeta(LucideIcons.list_todo, contentTheme.info),
|
|
||||||
MenuItems.directory:
|
MenuItems.directory:
|
||||||
_DashboardCardMeta(LucideIcons.folder, contentTheme.info),
|
_DashboardCardMeta(LucideIcons.folder, contentTheme.info),
|
||||||
MenuItems.finance:
|
MenuItems.finance:
|
||||||
@ -314,10 +313,10 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 2),
|
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 2),
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 4,
|
crossAxisCount: 3,
|
||||||
crossAxisSpacing: 8,
|
crossAxisSpacing: 15,
|
||||||
mainAxisSpacing: 8,
|
mainAxisSpacing: 8,
|
||||||
childAspectRatio: 1.15,
|
childAspectRatio: 1.8,
|
||||||
),
|
),
|
||||||
itemCount: filtered.length,
|
itemCount: filtered.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@ -367,9 +366,8 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
Icon(
|
Icon(
|
||||||
cardMeta.icon,
|
cardMeta.icon,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: isEnabled
|
color:
|
||||||
? cardMeta.color
|
isEnabled ? cardMeta.color : Colors.grey.shade300,
|
||||||
: Colors.grey.shade300,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Padding(
|
Padding(
|
||||||
@ -379,9 +377,8 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: isEnabled
|
fontWeight:
|
||||||
? FontWeight.w600
|
isEnabled ? FontWeight.w600 : FontWeight.w400,
|
||||||
: FontWeight.w400,
|
|
||||||
color: isEnabled
|
color: isEnabled
|
||||||
? Colors.black87
|
? Colors.black87
|
||||||
: Colors.grey.shade400,
|
: Colors.grey.shade400,
|
||||||
@ -424,11 +421,9 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
children: [
|
children: [
|
||||||
_sectionTitle('Project'),
|
_sectionTitle('Project'),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () =>
|
onTap: () => projectController.isProjectSelectionExpanded.toggle(),
|
||||||
projectController.isProjectSelectionExpanded.toggle(),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
||||||
const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user