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

@@ -13,6 +13,7 @@ import '../widgets/attire_info_card.dart';
import '../widgets/attire_item_card.dart';
import '../widgets/attire_section_header.dart';
import '../widgets/attire_section_tab.dart';
import '../widgets/attire_skeleton/attire_skeleton.dart';
class AttirePage extends StatefulWidget {
const AttirePage({super.key});
@@ -49,7 +50,7 @@ class _AttirePageState extends State<AttirePage> {
},
builder: (BuildContext context, AttireState state) {
if (state.status == AttireStatus.loading && state.options.isEmpty) {
return const Center(child: CircularProgressIndicator());
return const AttireSkeleton();
}
final List<AttireItem> requiredItems = state.options

View File

@@ -0,0 +1,37 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for a single attire item card.
class AttireItemSkeleton extends StatelessWidget {
/// Creates an [AttireItemSkeleton].
const AttireItemSkeleton({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>[
UiShimmerBox(width: 56, height: 56),
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,63 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'attire_item_skeleton.dart';
/// Full-page shimmer skeleton shown while attire items are loading.
class AttireSkeleton extends StatelessWidget {
/// Creates an [AttireSkeleton].
const AttireSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: SingleChildScrollView(
padding: const EdgeInsets.all(UiConstants.space5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
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: 160, height: 14),
SizedBox(height: UiConstants.space2),
UiShimmerLine(height: 12),
SizedBox(height: UiConstants.space1),
UiShimmerLine(width: 200, height: 12),
],
),
),
const SizedBox(height: UiConstants.space6),
// Section toggle chips placeholder
const Row(
children: <Widget>[
UiShimmerBox(width: 80, height: 32),
SizedBox(width: UiConstants.space3),
UiShimmerBox(width: 100, height: 32),
],
),
const SizedBox(height: UiConstants.space6),
// Section header placeholder
const UiShimmerSectionHeader(),
const SizedBox(height: UiConstants.space3),
// Attire item cards
UiShimmerList(
itemCount: 4,
spacing: UiConstants.space3,
itemBuilder: (int index) => const AttireItemSkeleton(),
),
],
),
),
);
}
}

View File

@@ -7,6 +7,7 @@ import 'package:krow_core/core.dart';
import 'package:staff_profile_info/src/presentation/blocs/personal_info_bloc.dart';
import 'package:staff_profile_info/src/presentation/blocs/personal_info_state.dart';
import 'package:staff_profile_info/src/presentation/widgets/personal_info_page/personal_info_content.dart';
import 'package:staff_profile_info/src/presentation/widgets/personal_info_skeleton/personal_info_skeleton.dart';
/// The Personal Info page for staff onboarding.
@@ -56,7 +57,7 @@ class PersonalInfoPage extends StatelessWidget {
builder: (BuildContext context, PersonalInfoState state) {
if (state.status == PersonalInfoStatus.loading ||
state.status == PersonalInfoStatus.initial) {
return const Center(child: CircularProgressIndicator());
return const PersonalInfoSkeleton();
}
if (state.staff == null) {

View File

@@ -0,0 +1,20 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for a single form field (label + input).
class FormFieldSkeleton extends StatelessWidget {
/// Creates a [FormFieldSkeleton].
const FormFieldSkeleton({super.key});
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(width: 80, height: 12),
SizedBox(height: UiConstants.space2),
UiShimmerBox(width: double.infinity, height: 48),
],
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'form_field_skeleton.dart';
/// Full-page shimmer skeleton shown while personal info is loading.
class PersonalInfoSkeleton extends StatelessWidget {
/// Creates a [PersonalInfoSkeleton].
const PersonalInfoSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: SingleChildScrollView(
padding: const EdgeInsets.all(UiConstants.space5),
child: Column(
children: <Widget>[
// Avatar placeholder
const Center(child: UiShimmerCircle(size: 80)),
const SizedBox(height: UiConstants.space6),
// Form fields
UiShimmerList(
itemCount: 5,
spacing: UiConstants.space5,
itemBuilder: (int index) => const FormFieldSkeleton(),
),
],
),
),
);
}
}