added skeleton for the service and infra list
This commit is contained in:
parent
b907e76c12
commit
3603b12f9c
@ -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),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user