import 'package:flutter/material.dart'; /// 按钮样式类型 enum ButtonStyleType { primary, // 主样式:纯色背景,无边框 secondary, // 次样式:白色背景,深灰文字,深灰边框 del, // 红色背景,白文字 } /// 自定义默认按钮(支持不可点击/禁用状态和防连点功能) class CustomButton extends StatefulWidget { final String text; // 按钮文字 final Color backgroundColor; // 按钮背景色 final Color delColor; // 按钮背景色 final double borderRadius; // 圆角半径(默认5) final VoidCallback? onPressed; // 点击事件回调 final EdgeInsetsGeometry? padding; // 内边距 final EdgeInsetsGeometry? margin; // 外边距 final double? height; // 按钮高度 final TextStyle? textStyle; // 文字样式 /// 新增:是否可点击(true 可点,false 禁用) /// 注意:如果 onPressed 为 null,也会被视为不可点击 final bool enabled; /// 新增:禁用时的背景色(可选) final Color? disabledBackgroundColor; /// 新增:禁用时的文字颜色(可选) final Color? disabledTextColor; /// 新增:防连点间隔时间(毫秒) final int debounceInterval; /// 新增:按钮样式类型 final ButtonStyleType buttonStyle; /// 新增:边框颜色(仅在 secondary 样式下使用,如不指定则使用默认深灰色) final Color? borderColor; /// 新增:边框宽度(仅在 secondary 样式下使用) final double borderWidth; /// 新增:未读数(默认0),大于0时在右上角显示未读气泡 final int noRead; const CustomButton({ super.key, required this.text, this.backgroundColor = Colors.blue, this.delColor = Colors.red, this.borderRadius = 5.0, this.onPressed, this.padding, this.margin, this.height, this.textStyle, this.enabled = true, this.disabledBackgroundColor, this.disabledTextColor, this.debounceInterval = 1000, // 默认1秒防连点 this.buttonStyle = ButtonStyleType.primary, // 默认为主样式 this.borderColor, this.borderWidth = 1.0, this.noRead = 0, // 新增参数默认0 }); @override State createState() => _CustomButtonState(); } class _CustomButtonState extends State { // 记录最后一次点击时间 DateTime _lastClickTime = DateTime(0); @override Widget build(BuildContext context) { // 如果 enabled 为 false 或 onPressed 为 null,则视为不可点击 final bool isEnabled = widget.enabled && widget.onPressed != null; // 根据按钮样式计算背景色、文字颜色和边框 final Color bgColor; final Color textColor; final Color? borderColor; if (widget.buttonStyle == ButtonStyleType.secondary) { // 次样式:白色背景,深灰文字,深灰边框 bgColor = isEnabled ? Colors.white : (widget.disabledBackgroundColor ?? Colors.grey.shade200); textColor = isEnabled ? Colors.grey[800]! : (widget.disabledTextColor ?? Colors.grey.shade500); borderColor = isEnabled ? (widget.borderColor ?? Colors.grey.shade400) : (widget.disabledTextColor ?? Colors.grey.shade300); } else if (widget.buttonStyle == ButtonStyleType.primary) { // 主样式:原有逻辑 bgColor = isEnabled ? widget.backgroundColor : (widget.disabledBackgroundColor ?? Colors.grey.shade400); textColor = isEnabled ? Colors.white : (widget.disabledTextColor ?? Colors.white70); borderColor = null; // 主样式默认无边框 }else{ bgColor = isEnabled ? widget.delColor : (widget.disabledBackgroundColor ?? Colors.grey.shade400); textColor = isEnabled ? Colors.white : (widget.disabledTextColor ?? Colors.white70); borderColor = null; // 主样式默认无边框 } // 计算最终文字样式 TextStyle finalTextStyle; if (widget.textStyle != null) { finalTextStyle = widget.textStyle!.copyWith(color: textColor); } else { finalTextStyle = TextStyle( color: textColor, fontSize: 14, fontWeight: FontWeight.bold, ); } // 处理点击事件(添加防连点逻辑) void handleOnPressed() { final now = DateTime.now(); if (now.difference(_lastClickTime).inMilliseconds < widget.debounceInterval) { // 在防连点间隔内,不执行操作 return; } _lastClickTime = now; if (widget.onPressed != null) { widget.onPressed!(); } } // 构建边框 final BoxBorder? border = borderColor != null ? Border.all( color: borderColor, width: widget.borderWidth, ) : null; // 是否显示未读气泡 final bool showBadge = widget.noRead > 0; // 气泡显示文本:超过 99 显示 "99+" final String badgeText = widget.noRead > 99 ? '99+' : widget.noRead.toString(); // 根据文字长度调整气泡尺寸(保持圆形) double badgeSize = 18; if (widget.noRead > 99) { badgeSize = 28; } else if (widget.noRead > 9) { badgeSize = 22; } else { badgeSize = 18; } // 点击拦截器 + 视觉反馈(禁用时降低不透明度) return Opacity( opacity: isEnabled ? 1.0 : 0.65, child: AbsorbPointer( absorbing: !isEnabled, child: GestureDetector( onTap: isEnabled ? handleOnPressed : null, child: Stack( clipBehavior: Clip.none, children: [ // 按钮主体 Container( height: widget.height ?? 45, // 默认高度45 padding: widget.padding ?? const EdgeInsets.all(6), // 默认内边距 margin: widget.margin ?? const EdgeInsets.symmetric(horizontal: 5), // 默认外边距 decoration: BoxDecoration( borderRadius: BorderRadius.circular(widget.borderRadius), color: bgColor, border: border, ), child: Center( child: Text( widget.text, style: finalTextStyle, ), ), ), // 未读气泡(右上角),只在 noRead > 0 时显示 if (showBadge) Positioned( // 让气泡靠近右上角并稍微偏移到外面一点,看起来像贴在角上 top: -6, right: -3, child: Container( width: badgeSize, height: badgeSize, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.red, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black26, blurRadius: 2, offset: Offset(0, 1), ), ], ), // 使用 FittedBox 保证文本不会溢出 child: FittedBox( fit: BoxFit.scaleDown, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: Text( badgeText, textAlign: TextAlign.center, style: const TextStyle( color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ), ), ), ], ), ), ), ); } }