import 'package:flutter/material.dart'; /// 骨架屏加载组件 - 提供更好的加载体验 class ShimmerLoading extends StatefulWidget { final Widget child; final bool isLoading; const ShimmerLoading({ super.key, required this.child, this.isLoading = true, }); @override State createState() => _ShimmerLoadingState(); } class _ShimmerLoadingState 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: -2, end: 2).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOutSine), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (!widget.isLoading) return widget.child; return AnimatedBuilder( animation: _animation, builder: (context, child) { return ShaderMask( shaderCallback: (bounds) { return LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: const [ Color(0xFFEBEBF4), Color(0xFFF4F4F4), Color(0xFFEBEBF4), ], stops: [ _animation.value - 1, _animation.value, _animation.value + 1, ].map((e) => e.clamp(0.0, 1.0)).toList(), ).createShader(bounds); }, blendMode: BlendMode.srcATop, child: widget.child, ); }, ); } } /// 骨架屏占位框 class ShimmerBox extends StatelessWidget { final double? width; final double height; final double borderRadius; const ShimmerBox({ super.key, this.width, required this.height, this.borderRadius = 8, }); @override Widget build(BuildContext context) { return Container( width: width, height: height, decoration: BoxDecoration( color: const Color(0xFFE5E7EB), borderRadius: BorderRadius.circular(borderRadius), ), ); } } /// 资产卡片骨架屏 class AssetCardSkeleton extends StatelessWidget { const AssetCardSkeleton({super.key}); @override Widget build(BuildContext context) { return ShimmerLoading( child: Container( margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 30, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const ShimmerBox(width: 80, height: 16), const SizedBox(height: 12), const ShimmerBox(width: 180, height: 36), const SizedBox(height: 8), const ShimmerBox(width: 120, height: 14), const SizedBox(height: 12), const ShimmerBox(width: 100, height: 24, borderRadius: 12), ], ), ), ); } } /// 资产项目骨架屏 class AssetItemSkeleton extends StatelessWidget { const AssetItemSkeleton({super.key}); @override Widget build(BuildContext context) { return ShimmerLoading( child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 2, offset: const Offset(0, 1), ), ], ), child: Row( children: [ const ShimmerBox(width: 40, height: 40, borderRadius: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const ShimmerBox(width: 60, height: 16), const SizedBox(height: 8), const ShimmerBox(width: 100, height: 20), const SizedBox(height: 4), const ShimmerBox(width: 80, height: 12), ], ), ), const ShimmerBox(width: 14, height: 20), ], ), ), ); } } /// 收益统计骨架屏 class EarningsStatsSkeleton extends StatelessWidget { const EarningsStatsSkeleton({super.key}); @override Widget build(BuildContext context) { return ShimmerLoading( child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 2, offset: const Offset(0, 1), ), ], ), child: Column( children: [ Row( children: [ Container( width: 4, height: 20, decoration: BoxDecoration( color: const Color(0xFFE5E7EB), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), const ShimmerBox(width: 60, height: 16), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: Column( children: const [ ShimmerBox(width: 50, height: 12), SizedBox(height: 8), ShimmerBox(width: 70, height: 16), ], ), ), Container( width: 1, height: 40, color: const Color(0xFFE5E7EB), ), Expanded( child: Column( children: const [ ShimmerBox(width: 50, height: 12), SizedBox(height: 8), ShimmerBox(width: 70, height: 16), ], ), ), Container( width: 1, height: 40, color: const Color(0xFFE5E7EB), ), Expanded( child: Column( children: const [ ShimmerBox(width: 50, height: 12), SizedBox(height: 8), ShimmerBox(width: 70, height: 16), ], ), ), ], ), ], ), ), ); } } /// 页面骨架屏 class PageSkeleton extends StatelessWidget { const PageSkeleton({super.key}); @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: const [ AssetCardSkeleton(), SizedBox(height: 24), AssetItemSkeleton(), SizedBox(height: 16), AssetItemSkeleton(), SizedBox(height: 16), AssetItemSkeleton(), SizedBox(height: 24), EarningsStatsSkeleton(), ], ), ); } }