Files
zhinian_manage/lib/widgets/skeleton.dart

311 lines
8.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
import '../theme.dart';
class SkeletonBox extends StatelessWidget {
final double? width;
final double height;
final double radius;
const SkeletonBox({
super.key,
this.width,
required this.height,
this.radius = 6,
});
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(radius),
),
);
}
}
class SkeletonShimmer extends StatelessWidget {
final Widget child;
const SkeletonShimmer({super.key, required this.child});
@override
Widget build(BuildContext context) {
final base = AppColors.isDark
? const Color(0xFF334155)
: const Color(0xFFE2E8F0);
final highlight = AppColors.isDark
? const Color(0xFF475569)
: const Color(0xFFF1F5F9);
return Shimmer.fromColors(
baseColor: base,
highlightColor: highlight,
period: const Duration(milliseconds: 1400),
child: child,
);
}
}
class EventCardSkeleton extends StatelessWidget {
const EventCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: SkeletonShimmer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SkeletonBox(width: 44, height: 44, radius: 12),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
Expanded(child: SkeletonBox(height: 16, radius: 6)),
SizedBox(width: 8),
SkeletonBox(width: 48, height: 18, radius: 10),
],
),
const SizedBox(height: 10),
const SkeletonBox(height: 12, radius: 4),
const SizedBox(height: 6),
const SkeletonBox(width: 180, height: 12, radius: 4),
],
),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: const [
SkeletonBox(width: 160, height: 12, radius: 4),
],
),
),
],
),
),
);
}
}
class WorkOrderCardSkeleton extends StatelessWidget {
const WorkOrderCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: SkeletonShimmer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
Expanded(child: SkeletonBox(height: 16, radius: 6)),
SizedBox(width: 8),
SkeletonBox(width: 56, height: 18, radius: 10),
],
),
const SizedBox(height: 12),
const SkeletonBox(height: 12, radius: 4),
const SizedBox(height: 6),
const SkeletonBox(width: 220, height: 12, radius: 4),
const SizedBox(height: 14),
Row(
children: const [
SkeletonBox(width: 80, height: 12, radius: 4),
SizedBox(width: 12),
SkeletonBox(width: 100, height: 12, radius: 4),
Spacer(),
SkeletonBox(width: 36, height: 16, radius: 4),
],
),
],
),
),
);
}
}
class OrderCardSkeleton extends StatelessWidget {
const OrderCardSkeleton({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: SkeletonShimmer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
SkeletonBox(width: 140, height: 14, radius: 4),
Spacer(),
SkeletonBox(width: 56, height: 18, radius: 10),
],
),
const SizedBox(height: 14),
const SkeletonBox(height: 15, width: 200, radius: 4),
const SizedBox(height: 10),
Row(
children: const [
SkeletonBox(width: 90, height: 12, radius: 4),
SizedBox(width: 16),
SkeletonBox(width: 50, height: 12, radius: 4),
],
),
const SizedBox(height: 14),
Row(
children: const [
SkeletonBox(width: 100, height: 12, radius: 4),
Spacer(),
SkeletonBox(width: 70, height: 16, radius: 4),
],
),
],
),
),
);
}
}
class DetailPageSkeleton extends StatelessWidget {
const DetailPageSkeleton({super.key});
@override
Widget build(BuildContext context) {
return SkeletonShimmer(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
physics: const NeverScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
SkeletonBox(width: 100, height: 14, radius: 4),
SizedBox(height: 12),
SkeletonBox(height: 22, radius: 6),
SizedBox(height: 10),
SkeletonBox(width: 220, height: 14, radius: 4),
],
),
),
const SizedBox(height: 20),
_block(height: 130),
const SizedBox(height: 20),
_block(height: 130),
const SizedBox(height: 20),
_block(height: 100),
],
),
),
);
}
Widget _block({required double height}) {
return Container(
width: double.infinity,
height: height,
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
SkeletonBox(width: 80, height: 14, radius: 4),
SizedBox(height: 14),
SkeletonBox(height: 14, radius: 4),
SizedBox(height: 8),
SkeletonBox(width: 200, height: 14, radius: 4),
],
),
),
);
}
}
class SkeletonList extends StatelessWidget {
final WidgetBuilder itemBuilder;
final int count;
final EdgeInsetsGeometry padding;
const SkeletonList({
super.key,
required this.itemBuilder,
this.count = 5,
this.padding = const EdgeInsets.all(16),
});
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: padding,
itemCount: count,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, _) => itemBuilder(context),
);
}
}