import 'package:flutter/material.dart'; /// 闪烁文字占位符 - 数据加载时显示闪烁的占位文字 class ShimmerText extends StatefulWidget { final String placeholder; final TextStyle? style; final TextAlign? textAlign; const ShimmerText({ super.key, this.placeholder = '--', this.style, this.textAlign, }); @override State createState() => _ShimmerTextState(); } class _ShimmerTextState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, )..repeat(reverse: true); _animation = Tween(begin: 0.3, end: 1.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 Opacity( opacity: _animation.value, child: Text( widget.placeholder, style: widget.style, textAlign: widget.textAlign, ), ); }, ); } } /// 数据文字组件 - 加载中显示闪烁占位符,加载完成显示真实数据 class DataText extends StatelessWidget { final String? data; final bool isLoading; final String placeholder; final TextStyle? style; final TextAlign? textAlign; final String Function(String)? formatter; const DataText({ super.key, this.data, this.isLoading = false, this.placeholder = '--', this.style, this.textAlign, this.formatter, }); @override Widget build(BuildContext context) { if (isLoading || data == null) { return ShimmerText( placeholder: placeholder, style: style, textAlign: textAlign, ); } final displayText = formatter != null ? formatter!(data!) : data!; return Text( displayText, style: style, textAlign: textAlign, ); } } /// 金额文字组件 - 专门用于显示金额 class AmountText extends StatelessWidget { final String? amount; final bool isLoading; final String prefix; final String suffix; final TextStyle? style; final TextAlign? textAlign; const AmountText({ super.key, this.amount, this.isLoading = false, this.prefix = '', this.suffix = '', this.style, this.textAlign, }); @override Widget build(BuildContext context) { if (isLoading || amount == null) { return ShimmerText( placeholder: '$prefix--$suffix', style: style, textAlign: textAlign, ); } return Text( '$prefix$amount$suffix', style: style, textAlign: textAlign, ); } } // ============================================================================ // 向后兼容的组件 - 用于尚未迁移的页面 // ============================================================================ /// 闪烁加载效果容器(向后兼容) class ShimmerLoading extends StatefulWidget { final Widget child; const ShimmerLoading({super.key, required this.child}); @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: 1000), vsync: this, )..repeat(reverse: true); _animation = Tween(begin: 0.4, end: 1.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 Opacity( opacity: _animation.value, child: widget.child, ); }, ); } } /// 闪烁占位块(向后兼容) class ShimmerBox extends StatelessWidget { final double? width; final double height; final BorderRadius? borderRadius; const ShimmerBox({ super.key, this.width, this.height = 16, this.borderRadius, }); @override Widget build(BuildContext context) { return Container( width: width, height: height, decoration: BoxDecoration( color: const Color(0xFFE5E7EB), borderRadius: borderRadius ?? BorderRadius.circular(4), ), ); } } /// 页面骨架屏(向后兼容) class PageSkeleton extends StatelessWidget { const PageSkeleton({super.key}); @override Widget build(BuildContext context) { return ShimmerLoading( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 60), const ShimmerBox(width: 120, height: 20), const SizedBox(height: 16), const ShimmerBox(height: 100), const SizedBox(height: 24), const ShimmerBox(height: 80), const SizedBox(height: 24), const ShimmerBox(height: 120), const SizedBox(height: 24), const ShimmerBox(height: 80), ], ), ), ); } }