import 'package:flutter/material.dart'; import '../core/app_colors.dart'; /// 渐变边框按钮(白色底+彩色渐变边框) class GradientBorderButton extends StatelessWidget { final String text; final IconData? icon; final VoidCallback? onTap; final double height; final bool isSuccess; const GradientBorderButton({ super.key, required this.text, this.icon, this.onTap, this.height = 52, this.isSuccess = false, }); @override Widget build(BuildContext context) { return Container( height: height, decoration: BoxDecoration( gradient: isSuccess ? AppColors.calmHealthGradient : AppColors.primaryGradient, borderRadius: BorderRadius.circular(14), boxShadow: AppColors.buttonShadow, ), child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(14), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) ...[ Icon(icon, size: 22, color: AppColors.textOnGradient), const SizedBox(width: 8), ], Text( text, style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w600, color: AppColors.textOnGradient, ), ), ], ), ), ), ); } } /// 卡片内的小操作按钮 class CardActionButton extends StatelessWidget { final String label; final IconData icon; final VoidCallback? onTap; final Color? iconColor; const CardActionButton({ super.key, required this.label, required this.icon, this.onTap, this.iconColor, }); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( gradient: AppColors.surfaceGradient, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.borderLight), boxShadow: AppColors.cardShadowLight, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18, color: iconColor ?? AppColors.primary), const SizedBox(width: 6), Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.textPrimary, ), ), ], ), ), ); } } /// 滑动删除组件:左滑露出红色删除背景,达30%阈值锁定,点击确认删除 /// [margin] 控制卡片之间的间距,red背景与child完全对齐 class SwipeDeleteTile extends StatefulWidget { final Widget child; final VoidCallback onDelete; final VoidCallback? onTap; final EdgeInsetsGeometry margin; const SwipeDeleteTile({ super.key, required this.child, required this.onDelete, this.onTap, this.margin = const EdgeInsets.symmetric(horizontal: 16, vertical: 4), }); @override State createState() => _SwipeDeleteTileState(); } class _SwipeDeleteTileState extends State with SingleTickerProviderStateMixin { double _dx = 0; static const _maxSlide = 80.0; static const _threshold = 24.0; // 30% of 80 void _onDragUpdate(DragUpdateDetails d) { setState(() => _dx = (_dx + d.delta.dx).clamp(-_maxSlide, 0.0)); } void _onDragEnd(DragEndDetails d) { if (_dx < -_threshold) { setState(() => _dx = -_maxSlide); } else { setState(() => _dx = 0); } } @override Widget build(BuildContext context) { final isSwiped = _dx < 0; return Padding( padding: widget.margin, child: Stack( children: [ Positioned.fill( child: Container( decoration: BoxDecoration( color: AppColors.error, borderRadius: BorderRadius.circular(16), ), child: const Align( alignment: Alignment.centerRight, child: Padding( padding: EdgeInsets.only(right: 20), child: Icon( Icons.delete_outline, color: AppColors.textOnGradient, size: 28, ), ), ), ), ), GestureDetector( behavior: isSwiped ? HitTestBehavior.opaque : HitTestBehavior.deferToChild, onTap: isSwiped ? () { widget.onDelete(); setState(() => _dx = 0); } : widget.onTap, onHorizontalDragUpdate: _onDragUpdate, onHorizontalDragEnd: _onDragEnd, child: Transform.translate( offset: Offset(_dx, 0), child: isSwiped ? AbsorbPointer(absorbing: true, child: widget.child) : widget.child, ), ), ], ), ); } } /// 功能区小图标容器 class IconBox extends StatelessWidget { final IconData icon; final double size; final Color? backgroundColor; final Color? iconColor; const IconBox({ super.key, required this.icon, this.size = 44, this.backgroundColor, this.iconColor, }); @override Widget build(BuildContext context) { return Container( width: size, height: size, decoration: BoxDecoration( gradient: backgroundColor == null ? AppColors.calmHealthGradient : null, color: backgroundColor, borderRadius: BorderRadius.circular(size / 3), boxShadow: AppColors.cardShadowLight, ), child: Icon(icon, size: size * 0.55, color: iconColor ?? Colors.white), ); } }