import 'package:flutter/material.dart'; import '../constants/app_constants.dart'; // 自定义按钮组件 class CustomButton extends StatelessWidget { final String text; final VoidCallback? onPressed; final IconData? icon; final Color? backgroundColor; final Color? textColor; final double? borderRadius; final bool isLoading; final double? width; final double? height; const CustomButton({ super.key, required this.text, this.onPressed, this.icon, this.backgroundColor, this.textColor, this.borderRadius, this.isLoading = false, this.width, this.height, }); @override Widget build(BuildContext context) { return SizedBox( width: width, height: height ?? 48, child: ElevatedButton( onPressed: isLoading ? null : onPressed, style: ElevatedButton.styleFrom( backgroundColor: backgroundColor ?? AppConstants.primaryColor, foregroundColor: textColor ?? Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius ?? 8), ), elevation: 2, ), child: isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: 18), const SizedBox(width: 8), ], Text(text), ], ), ), ); } } // 自定义卡片组件 class CustomCard extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final double? borderRadius; final Color? backgroundColor; final VoidCallback? onTap; final BoxBorder? border; const CustomCard({ super.key, required this.child, this.padding, this.margin, this.borderRadius, this.backgroundColor, this.onTap, this.border, }); @override Widget build(BuildContext context) { return Container( margin: margin ?? const EdgeInsets.all(8), child: Material( color: backgroundColor ?? AppConstants.surfaceColor, borderRadius: BorderRadius.circular(borderRadius ?? 12), elevation: 2, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(borderRadius ?? 12), child: Container( padding: padding ?? const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius ?? 12), border: border, ), child: child, ), ), ), ); } } // 自定义输入框组件 class CustomTextField extends StatelessWidget { final String? labelText; final String? hintText; final TextEditingController? controller; final IconData? prefixIcon; final IconData? suffixIcon; final VoidCallback? onSuffixIconTap; final bool obscureText; final String? Function(String?)? validator; final void Function(String)? onChanged; final TextInputType? keyboardType; final int? maxLines; final bool enabled; const CustomTextField({ super.key, this.labelText, this.hintText, this.controller, this.prefixIcon, this.suffixIcon, this.onSuffixIconTap, this.obscureText = false, this.validator, this.onChanged, this.keyboardType, this.maxLines = 1, this.enabled = true, }); @override Widget build(BuildContext context) { return TextFormField( controller: controller, obscureText: obscureText, validator: validator, onChanged: onChanged, keyboardType: keyboardType, maxLines: maxLines, enabled: enabled, decoration: InputDecoration( labelText: labelText, hintText: hintText, prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null, suffixIcon: suffixIcon != null ? IconButton( icon: Icon(suffixIcon), onPressed: onSuffixIconTap, ) : null, ), ); } } // 加载指示器组件 class LoadingIndicator extends StatelessWidget { final String? message; final Color? color; const LoadingIndicator({ super.key, this.message, this.color, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( color ?? AppConstants.primaryColor, ), ), if (message != null) ...[ const SizedBox(height: 16), Text( message!, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ], ), ); } } // 空状态组件 class EmptyState extends StatelessWidget { final String title; final String? subtitle; final IconData? icon; final Widget? action; const EmptyState({ super.key, required this.title, this.subtitle, this.icon, this.action, }); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon ?? Icons.inbox_outlined, size: 64, color: Colors.grey[400], ), const SizedBox(height: 16), Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black87, ), textAlign: TextAlign.center, ), if (subtitle != null) ...[ const SizedBox(height: 8), Text( subtitle!, style: const TextStyle( fontSize: 14, color: Colors.grey, ), textAlign: TextAlign.center, ), ], if (action != null) ...[ const SizedBox(height: 24), action!, ], ], ), ), ); } } // 错误状态组件 class ErrorState extends StatelessWidget { final String title; final String? subtitle; final VoidCallback? onRetry; const ErrorState({ super.key, required this.title, this.subtitle, this.onRetry, }); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: AppConstants.errorColor, ), const SizedBox(height: 16), Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: Colors.black87, ), textAlign: TextAlign.center, ), if (subtitle != null) ...[ const SizedBox(height: 8), Text( subtitle!, style: const TextStyle( fontSize: 14, color: Colors.grey, ), textAlign: TextAlign.center, ), ], if (onRetry != null) ...[ const SizedBox(height: 24), CustomButton( text: AppConstants.retryText, onPressed: onRetry, icon: AppConstants.refreshIcon, ), ], ], ), ), ); } }