added skeleton for the service and infra list

This commit is contained in:
Vaibhav Surve 2025-12-08 15:21:40 +05:30
parent b907e76c12
commit 3603b12f9c
3 changed files with 144 additions and 3 deletions

View File

@ -35,6 +35,143 @@ class SkeletonLoaders {
);
}
static Widget serviceProjectListSkeletonLoader() {
// --- Start: Configuration to match live UI ---
// Live UI uses ListView.separated with:
// - padding: MySpacing.only(left: 8, right: 8, top: 4, bottom: 120)
// - separatorBuilder: MySpacing.height(12)
// - _buildProjectCard uses Card(margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 4))
// To combine:
// Horizontal padding: 8 (ListView) + 6 (Card margin) = 14 on each side.
// Top/Bottom separation: 4 (ListView padding) + 4 (Card margin) = 8
// Separator space: 4 (Card margin) + 12 (Separator) + 4 (Card margin) = 20 total space between cards.
// New ListView.separated padding to compensate for inner Card margins
const EdgeInsets listPadding =
const EdgeInsets.fromLTRB(14, 8, 14, 120 + 4); // 8(L/R) + 6(Card L/R Margin) = 14
// New separator to match the 12 + 4 * 2 = 20 gap.
const Widget cardSeparator = const SizedBox(height: 12);
const EdgeInsets cardMargin = EdgeInsets.zero; // Margin is now controlled by the ListView.separated padding
// Internal Card padding matches the live card
const EdgeInsets cardInnerPadding =
const EdgeInsets.symmetric(horizontal: 18, vertical: 14);
// --- End: Configuration to match live UI ---
return ListView.separated(
padding: listPadding, // Use calculated padding
physics:
const NeverScrollableScrollPhysics(),
itemCount: 4,
separatorBuilder: (_, __) => cardSeparator, // Use calculated separator
itemBuilder: (context, index) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
margin: cardMargin, // Set margin to zero, handled by ListView padding
shadowColor: Colors.indigo.withOpacity(0.10),
color: Colors.white,
child: ShimmerEffect(
child: Padding(
padding: cardInnerPadding, // Use live card's inner padding
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. Title and Status Row
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Project Name Placeholder
Container(
height: 18, // Matches MyText.titleMedium height approx
width: 150,
color: Colors.grey.shade300,
),
// Status Chip Placeholder
Container(
height: 18, // Matches status chip height approx
width: 60,
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(6),
),
),
],
),
// MySpacing.height(10) in live UI is the key spacing here
// Note: The live UI has MySpacing.height(4) after the title
// and then MySpacing.height(10) before the first detail row,
// so the total space is 4 + 10 = 14.
MySpacing.height(14),
// 2. Detail Rows (Date, Client, Contact)
// Assigned Date Row
_buildDetailRowSkeleton(
width: 200, iconColor: Colors.teal.shade300),
MySpacing.height(8),
// Client Row
_buildDetailRowSkeleton(
width: 240, iconColor: Colors.indigo.shade300),
MySpacing.height(8),
// Contact Row
_buildDetailRowSkeleton(
width: 220, iconColor: Colors.green.shade300),
MySpacing.height(12), // MySpacing.height(12) before Wrap
// 3. Service Chips Wrap
Wrap(
spacing: 6,
runSpacing: 4,
children: List.generate(
3,
(chipIndex) => Container(
height: 20,
width:
70 + (chipIndex * 10).toDouble(), // Varied widths
decoration: BoxDecoration(
color: Colors
.grey.shade300,
borderRadius: BorderRadius.circular(6),
),
),
),
),
],
),
),
),
);
},
);
}
/// Helper to build a skeleton row for details
static Widget _buildDetailRowSkeleton({
required double width,
required Color iconColor,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon Placeholder (size 18 matches live UI)
Icon(Icons.circle, size: 18, color: iconColor),
MySpacing.width(8),
// Text Placeholder (height 13 approx for font size 13)
Container(
height: 14,
width: width,
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
),
],
);
}
static Widget attendanceQuickCardSkeleton() {
return Container(
padding: const EdgeInsets.all(16),

View File

@ -11,6 +11,7 @@ import 'package:on_field_work/model/infra_project/infra_project_list.dart';
import 'package:on_field_work/helpers/widgets/custom_app_bar.dart';
import 'package:on_field_work/view/infraProject/infra_project_details_screen.dart';
import 'package:on_field_work/helpers/widgets/my_custom_skeleton.dart';
class InfraProjectScreen extends StatefulWidget {
const InfraProjectScreen({super.key});
@ -245,7 +246,7 @@ class _InfraProjectScreenState extends State<InfraProjectScreen> with UIMixin {
Expanded(
child: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
return Center(child: SkeletonLoaders.serviceProjectListSkeletonLoader());
}
final projects = controller.filteredProjects;

View File

@ -9,6 +9,7 @@ import 'package:on_field_work/model/service_project/service_projects_list_model.
import 'package:on_field_work/helpers/utils/date_time_utils.dart';
import 'package:on_field_work/view/service_project/service_project_details_screen.dart';
import 'package:on_field_work/helpers/widgets/custom_app_bar.dart';
import 'package:on_field_work/helpers/widgets/my_custom_skeleton.dart';
class ServiceProjectScreen extends StatefulWidget {
const ServiceProjectScreen({super.key});
@ -200,7 +201,7 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
end: Alignment.bottomCenter,
colors: [
appBarColor,
appBarColor.withOpacity(0.0),
appBarColor.withOpacity(0.0),
],
),
),
@ -264,7 +265,9 @@ class _ServiceProjectScreenState extends State<ServiceProjectScreen>
Expanded(
child: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
return Center(
child: SkeletonLoaders
.serviceProjectListSkeletonLoader());
}
final projects = controller.filteredProjects;