feat: add shimmer skeletons for various sections in the staff profile and onboarding features

- Implemented ProfilePageSkeleton for loading state in staff profile.
- Added ReliabilityScoreSkeleton and ReliabilityStatsSkeleton for reliability metrics.
- Created CertificatesSkeleton and related components for loading certificates.
- Developed DocumentsSkeleton and associated document card skeletons.
- Introduced TaxFormsSkeleton for loading tax forms.
- Added BankAccountSkeleton and its components for bank account loading state.
- Created TimeCardSkeleton for displaying time card loading state.
- Implemented AttireSkeleton for loading attire items.
- Added PersonalInfoSkeleton for loading personal information.
- Developed FaqsSkeleton for loading FAQ sections.
- Created PrivacySecuritySkeleton for loading privacy settings.
This commit is contained in:
Achintha Isuru
2026-03-10 15:20:24 -04:00
parent ccf1a75a4d
commit bd98a112a0
60 changed files with 1718 additions and 31 deletions

View File

@@ -11,6 +11,7 @@ import '../blocs/certificates/certificates_state.dart';
import '../widgets/add_certificate_card.dart';
import '../widgets/certificate_card.dart';
import '../widgets/certificates_header.dart';
import '../widgets/certificates_skeleton/certificates_skeleton.dart';
/// Page for viewing and managing staff certificates.
///
@@ -28,9 +29,7 @@ class CertificatesPage extends StatelessWidget {
builder: (BuildContext context, CertificatesState state) {
if (state.status == CertificatesStatus.loading ||
state.status == CertificatesStatus.initial) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
return const Scaffold(body: CertificatesSkeleton());
}
if (state.status == CertificatesStatus.failure) {

View File

@@ -0,0 +1,38 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for a single certificate card.
class CertificateCardSkeleton extends StatelessWidget {
/// Creates a [CertificateCardSkeleton].
const CertificateCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
margin: const EdgeInsets.only(bottom: UiConstants.space3),
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border),
),
child: const Row(
children: <Widget>[
UiShimmerCircle(size: 40),
SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 140, height: 14),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 100, height: 12),
],
),
),
UiShimmerBox(width: 60, height: 28),
],
),
);
}
}

View File

@@ -0,0 +1,37 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for the certificates progress header.
class CertificatesHeaderSkeleton extends StatelessWidget {
/// Creates a [CertificatesHeaderSkeleton].
const CertificatesHeaderSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(UiConstants.space5),
decoration: const BoxDecoration(color: UiColors.primary),
child: SafeArea(
bottom: false,
child: Column(
children: <Widget>[
const SizedBox(height: UiConstants.space4),
const UiShimmerCircle(size: 64),
const SizedBox(height: UiConstants.space3),
UiShimmerLine(
width: 120,
height: 14,
),
const SizedBox(height: UiConstants.space2),
UiShimmerLine(
width: 80,
height: 12,
),
const SizedBox(height: UiConstants.space6),
],
),
),
);
}
}

View File

@@ -0,0 +1,38 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'certificate_card_skeleton.dart';
import 'certificates_header_skeleton.dart';
/// Full-page shimmer skeleton shown while certificates are loading.
class CertificatesSkeleton extends StatelessWidget {
/// Creates a [CertificatesSkeleton].
const CertificatesSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
const CertificatesHeaderSkeleton(),
Transform.translate(
offset: const Offset(0, -UiConstants.space12),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
),
child: UiShimmerList(
itemCount: 4,
spacing: UiConstants.space3,
itemBuilder: (int index) =>
const CertificateCardSkeleton(),
),
),
),
],
),
),
);
}
}

View File

@@ -10,6 +10,7 @@ import '../blocs/documents/documents_cubit.dart';
import '../blocs/documents/documents_state.dart';
import '../widgets/document_card.dart';
import '../widgets/documents_progress_card.dart';
import '../widgets/documents_skeleton/documents_skeleton.dart';
class DocumentsPage extends StatelessWidget {
const DocumentsPage({super.key});
@@ -28,11 +29,7 @@ class DocumentsPage extends StatelessWidget {
child: BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (BuildContext context, DocumentsState state) {
if (state.status == DocumentsStatus.loading) {
return const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(UiColors.primary),
),
);
return const DocumentsSkeleton();
}
if (state.status == DocumentsStatus.failure) {
return Center(

View File

@@ -0,0 +1,37 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for a single document card row.
class DocumentCardSkeleton extends StatelessWidget {
/// Creates a [DocumentCardSkeleton].
const DocumentCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
border: Border.all(color: UiColors.border),
borderRadius: UiConstants.radiusLg,
color: UiColors.cardViewBackground,
),
child: const Row(
children: <Widget>[
UiShimmerCircle(size: 40),
SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 160, height: 14),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 100, height: 12),
],
),
),
UiShimmerBox(width: 24, height: 24),
],
),
);
}
}

View File

@@ -0,0 +1,30 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for the documents progress card.
class DocumentsProgressSkeleton extends StatelessWidget {
/// Creates a [DocumentsProgressSkeleton].
const DocumentsProgressSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
border: Border.all(color: UiColors.border),
borderRadius: UiConstants.radiusLg,
color: UiColors.cardViewBackground,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 140, height: 14),
SizedBox(height: UiConstants.space3),
UiShimmerBox(width: double.infinity, height: 8),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 80, height: 12),
],
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'document_card_skeleton.dart';
import 'documents_progress_skeleton.dart';
/// Full-page shimmer skeleton shown while documents are loading.
class DocumentsSkeleton extends StatelessWidget {
/// Creates a [DocumentsSkeleton].
const DocumentsSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: ListView(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
vertical: UiConstants.space6,
),
children: <Widget>[
const DocumentsProgressSkeleton(),
const SizedBox(height: UiConstants.space4),
UiShimmerList(
itemCount: 5,
spacing: UiConstants.space3,
itemBuilder: (int index) => const DocumentCardSkeleton(),
),
],
),
);
}
}

View File

@@ -8,6 +8,7 @@ import 'package:krow_domain/krow_domain.dart';
import '../blocs/tax_forms/tax_forms_cubit.dart';
import '../blocs/tax_forms/tax_forms_state.dart';
import '../widgets/tax_forms_page/index.dart';
import '../widgets/tax_forms_skeleton/tax_forms_skeleton.dart';
class TaxFormsPage extends StatelessWidget {
const TaxFormsPage({super.key});
@@ -31,7 +32,7 @@ class TaxFormsPage extends StatelessWidget {
child: BlocBuilder<TaxFormsCubit, TaxFormsState>(
builder: (BuildContext context, TaxFormsState state) {
if (state.status == TaxFormsStatus.loading) {
return const Center(child: CircularProgressIndicator());
return const TaxFormsSkeleton();
}
if (state.status == TaxFormsStatus.failure) {

View File

@@ -0,0 +1,37 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for a single tax form card.
class TaxFormCardSkeleton extends StatelessWidget {
/// Creates a [TaxFormCardSkeleton].
const TaxFormCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
border: Border.all(color: UiColors.border),
borderRadius: UiConstants.radiusLg,
color: UiColors.cardViewBackground,
),
child: const Row(
children: <Widget>[
UiShimmerCircle(size: 40),
SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 120, height: 14),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 80, height: 12),
],
),
),
UiShimmerBox(width: 60, height: 24),
],
),
);
}
}

View File

@@ -0,0 +1,55 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'tax_form_card_skeleton.dart';
/// Full-page shimmer skeleton shown while tax forms are loading.
class TaxFormsSkeleton extends StatelessWidget {
/// Creates a [TaxFormsSkeleton].
const TaxFormsSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
vertical: UiConstants.space6,
),
child: Column(
spacing: UiConstants.space4,
children: <Widget>[
// Info card placeholder
Container(
width: double.infinity,
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
border: Border.all(color: UiColors.border),
borderRadius: UiConstants.radiusLg,
color: UiColors.cardViewBackground,
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 180, height: 14),
SizedBox(height: UiConstants.space2),
UiShimmerLine(height: 12),
SizedBox(height: UiConstants.space1),
UiShimmerLine(width: 200, height: 12),
],
),
),
// Progress overview placeholder
const UiShimmerStatsCard(),
// Form card placeholders
UiShimmerList(
itemCount: 3,
spacing: UiConstants.space2,
itemBuilder: (int index) => const TaxFormCardSkeleton(),
),
],
),
),
);
}
}