Files
photo-live/apps/mobile/lib/ui/components/state_badge.dart
T
2026-09-21 14:55:29 +08:00

113 lines
3.4 KiB
Dart

/// 状态徽章(UI-01 §3.1):图标 + 文案 + 颜色三重表达,不只靠颜色。
///
/// 跨页面复用的语义变体(上传状态、公开/关闭等);文案由调用方传入
/// (本地化),图标按变体内置、可显式覆盖。两档尺寸:compact 高 24
/// (卡片角标行)、默认高 32(列表/详情行)。
library;
import 'package:flutter/material.dart';
import '../tokens.dart';
/// 徽章语义变体。
enum BadgeVariant {
/// 成功/完成事实(绿)。
success,
/// 等待中(琥珀)。
warning,
/// 需要处理的错误(红)。
error,
/// 中性/关闭/未启用(灰)。
neutral,
/// 信息提示(主色蓝)。
info,
}
/// 状态徽章:`图标 + 文案`,浅底色块(语义色 12% 透明度)+ 语义色文字。
class StateBadge extends StatelessWidget {
const StateBadge({
required this.variant,
required this.label,
this.icon,
this.compact = false,
super.key,
});
/// 语义变体(决定图标与颜色)。
final BadgeVariant variant;
/// 展示文案(调用方本地化)。
final String label;
/// 显式覆盖图标(默认按变体内置)。
final IconData? icon;
/// 紧凑档:高 24、字号 12;默认高 32、字号 13。
final bool compact;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final (defaultIcon, color) = _resolve(variant, theme.brightness);
return Container(
height: compact ? 24.0 : 32.0,
padding: EdgeInsets.symmetric(horizontal: compact ? 8 : 10),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(kRadiusBadge),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon ?? defaultIcon, size: compact ? 14 : 16, color: color),
// 图标-文字光学间距(非布局节奏,不进令牌)。
SizedBox(width: compact ? kSpace4 : 6),
Flexible(
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style:
(compact
? theme.textTheme.labelSmall
: theme.textTheme.bodySmall)
?.copyWith(color: color, fontWeight: FontWeight.w600),
),
),
],
),
);
}
/// 变体 → (图标, 语义色);深色主题取提亮变体保证对比度。
static (IconData, Color) _resolve(
BadgeVariant variant,
Brightness brightness,
) => switch (variant) {
BadgeVariant.success =>
brightness == Brightness.light
? (Icons.check_circle, kColorSuccess)
: (Icons.check_circle, kColorSuccessDark),
BadgeVariant.warning =>
brightness == Brightness.light
? (Icons.hourglass_top, kColorWaiting)
: (Icons.hourglass_top, kColorWaitingDark),
BadgeVariant.error =>
brightness == Brightness.light
? (Icons.error_outline, kColorError)
: (Icons.error_outline, kColorErrorDark),
BadgeVariant.neutral =>
brightness == Brightness.light
? (Icons.remove_circle_outline, kColorTextSecondary)
: (Icons.remove_circle_outline, kColorTextSecondaryDark),
BadgeVariant.info =>
brightness == Brightness.light
? (Icons.info_outline, kColorInfo)
: (Icons.info_outline, kColorInfoDark),
};
}