import 'package:flutter/material.dart'; import '../../app/theme/app_colors.dart'; import '../../app/theme/app_spacing.dart'; /// 骨架屏加载组件 /// /// 列表加载、详情加载的骨架占位 class SkeletonLoader extends StatefulWidget { final double width; final double height; final double borderRadius; const SkeletonLoader({ super.key, this.width = double.infinity, required this.height, this.borderRadius = 8, }); @override State createState() => _SkeletonLoaderState(); } class _SkeletonLoaderState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, )..repeat(); _animation = Tween(begin: -1.0, end: 2.0).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Container( width: widget.width, height: widget.height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(widget.borderRadius), gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, stops: [ (_animation.value - 0.3).clamp(0.0, 1.0), _animation.value.clamp(0.0, 1.0), (_animation.value + 0.3).clamp(0.0, 1.0), ], colors: const [ AppColors.gray100, AppColors.gray50, AppColors.gray100, ], ), ), ); }, ); } } /// 券卡片骨架屏 class CouponCardSkeleton extends StatelessWidget { const CouponCardSkeleton({super.key}); @override Widget build(BuildContext context) { return Container( height: AppSpacing.couponCardHeight, decoration: BoxDecoration( color: AppColors.surface, borderRadius: AppSpacing.borderRadiusMd, border: Border.all(color: AppColors.borderLight), ), child: Row( children: [ const SkeletonLoader(width: 110, height: double.infinity, borderRadius: 12), const SizedBox(width: 16), Expanded( child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SkeletonLoader(width: 60, height: 10, borderRadius: 4), const SizedBox(height: 6), SkeletonLoader(width: 140, height: 14, borderRadius: 4), ], ), SkeletonLoader(width: 100, height: 16, borderRadius: 4), SkeletonLoader(width: 80, height: 10, borderRadius: 4), ], ), ), ), const SizedBox(width: 12), ], ), ); } }