feat: add shimmer loading skeletons for various pages and components

- Implemented UiShimmer as a core shimmer wrapper for animated gradient effects.
- Created shimmer presets for list items, stats cards, section headers, and more.
- Developed specific skeletons for billing, invoices, coverage, hubs, reports, payments, shifts, and home pages.
- Enhanced user experience by providing visual placeholders during data loading.
This commit is contained in:
Achintha Isuru
2026-03-10 13:21:30 -04:00
parent 3f112f5eb7
commit 0f0714c55b
36 changed files with 1594 additions and 36 deletions

View File

@@ -12,6 +12,7 @@ import '../blocs/client_hubs_state.dart';
import '../widgets/hub_card.dart';
import '../widgets/hub_empty_state.dart';
import '../widgets/hub_info_card.dart';
import '../widgets/hubs_page_skeleton.dart';
/// The main page for the client hubs feature.
///
@@ -94,7 +95,7 @@ class ClientHubsPage extends StatelessWidget {
),
if (state.status == ClientHubsStatus.loading)
const Center(child: CircularProgressIndicator())
const HubsPageSkeleton()
else if (state.hubs.isEmpty)
HubEmptyState(
onAddPressed: () async {

View File

@@ -0,0 +1,56 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer loading skeleton for the hubs list page.
///
/// Shows placeholder hub cards matching the [HubCard] layout with a
/// leading icon box, title line, and address line.
class HubsPageSkeleton extends StatelessWidget {
/// Creates a [HubsPageSkeleton].
const HubsPageSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: Column(
children: List.generate(5, (int index) {
return Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space3),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: UiColors.border),
borderRadius: UiConstants.radiusLg,
),
padding: const EdgeInsets.all(UiConstants.space4),
child: Row(
children: [
// Leading icon placeholder
UiShimmerBox(
width: 52,
height: 52,
borderRadius: UiConstants.radiusLg,
),
const SizedBox(width: UiConstants.space4),
// Title and address lines
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
UiShimmerLine(width: 160, height: 16),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 200, height: 12),
],
),
),
const SizedBox(width: UiConstants.space3),
// Chevron placeholder
const UiShimmerBox(width: 16, height: 16),
],
),
),
);
}),
),
);
}
}