feat: add shimmer loading skeletons for emergency contact section

This commit is contained in:
Achintha Isuru
2026-03-10 17:02:16 -04:00
parent 5a46edba9d
commit e60413f45c
5 changed files with 155 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ import '../widgets/emergency_contact_add_button.dart';
import '../widgets/emergency_contact_form_item.dart'; import '../widgets/emergency_contact_form_item.dart';
import '../widgets/emergency_contact_info_banner.dart'; import '../widgets/emergency_contact_info_banner.dart';
import '../widgets/emergency_contact_save_button.dart'; import '../widgets/emergency_contact_save_button.dart';
import '../widgets/emergency_contact_skeleton/emergency_contact_skeleton.dart';
/// The Staff Emergency Contact screen. /// The Staff Emergency Contact screen.
/// ///
@@ -43,7 +44,7 @@ class EmergencyContactScreen extends StatelessWidget {
}, },
builder: (context, state) { builder: (context, state) {
if (state.status == EmergencyContactStatus.loading) { if (state.status == EmergencyContactStatus.loading) {
return const Center(child: CircularProgressIndicator()); return const EmergencyContactSkeleton();
} }
return Column( return Column(
children: [ children: [

View File

@@ -0,0 +1,39 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'contact_field_skeleton.dart';
/// Shimmer placeholder for a single emergency contact card.
class ContactCardSkeleton extends StatelessWidget {
/// Creates a [ContactCardSkeleton].
const ContactCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: UiConstants.space4),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.bgPopup,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Header ("Contact 1")
UiShimmerLine(width: 90, height: 16),
SizedBox(height: UiConstants.space4),
// Full Name field
ContactFieldSkeleton(),
SizedBox(height: UiConstants.space4),
// Phone Number field
ContactFieldSkeleton(),
SizedBox(height: UiConstants.space4),
// Relationship field
ContactFieldSkeleton(),
],
),
);
}
}

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 ContactFieldSkeleton extends StatelessWidget {
/// Creates a [ContactFieldSkeleton].
const ContactFieldSkeleton({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,57 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'contact_card_skeleton.dart';
import 'info_banner_skeleton.dart';
/// Full-page shimmer skeleton shown while emergency contacts are loading.
class EmergencyContactSkeleton extends StatelessWidget {
/// Creates an [EmergencyContactSkeleton].
const EmergencyContactSkeleton({super.key});
@override
Widget build(BuildContext context) {
return UiShimmer(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(UiConstants.space6),
child: Column(
children: <Widget>[
// Info banner
const InfoBannerSkeleton(),
const SizedBox(height: UiConstants.space6),
// Contact card
const ContactCardSkeleton(),
const SizedBox(height: UiConstants.space4),
// Add contact button placeholder
UiShimmerBox(
width: 180,
height: 40,
borderRadius: UiConstants.radiusFull,
),
const SizedBox(height: UiConstants.space16),
],
),
),
),
// Save button placeholder
Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: const BoxDecoration(
border: Border(top: BorderSide(color: UiColors.border)),
),
child: SafeArea(
child: UiShimmerBox(
width: double.infinity,
height: 48,
borderRadius: UiConstants.radiusLg,
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,37 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// Shimmer placeholder for the emergency contact info banner.
class InfoBannerSkeleton extends StatelessWidget {
/// Creates an [InfoBannerSkeleton].
const InfoBannerSkeleton({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(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerCircle(size: 24),
SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UiShimmerLine(height: 12),
SizedBox(height: UiConstants.space2),
UiShimmerLine(width: 200, height: 12),
],
),
),
],
),
);
}
}