Files
bowlingManager/mobile/lib/theme/badges.dart
T
2025-11-10 14:14:08 +09:00

205 lines
6.2 KiB
Dart

import 'package:flutter/material.dart';
/// 배지(Chip) 스타일 매핑과 헬퍼
class BadgeStyles {
// 색상 팔레트
static const Color primary = Color(0xFF1565C0); // Blue 700
static const Color success = Color(0xFF2E7D32); // Green 800
static const Color warning = Color(0xFFEF6C00); // Orange 800
static const Color danger = Color(0xFFC62828); // Red 800
// 배경 톤 (100 계열 느낌)
static final Color primaryBg = Colors.blue.shade100;
static final Color successBg = Colors.green.shade100;
static final Color dangerBg = Colors.red.shade100;
static final Color neutralBg = Colors.grey.shade200;
// 회원 유형 컬러/아이콘 매핑
static Chip memberType(String type) {
late final Color bg;
late final IconData icon;
switch (type) {
case '정회원':
bg = primaryBg;
icon = Icons.person;
break;
case '준회원':
bg = Colors.indigo.shade100;
icon = Icons.person_outline;
break;
case '게스트':
bg = neutralBg;
icon = Icons.person_add_alt;
break;
default:
bg = neutralBg;
icon = Icons.person_outline;
}
return _chip(type, bg, icon);
}
// 게스트용 초소형 원형 배지 (이니셜 'G')
static Widget guestSmall({double size = 18, Color? bg, Color? fg}) {
final Color bgColor = bg ?? neutralBg;
final Color fgColor = fg ?? Colors.black87;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: bgColor,
shape: BoxShape.circle,
border: Border.all(color: Colors.grey.shade300),
),
alignment: Alignment.center,
child: Text(
'G',
style: TextStyle(
fontSize: size * 0.62,
fontWeight: FontWeight.w700,
color: fgColor,
height: 1.0,
),
),
);
}
// 참가 상태 컬러/아이콘 매핑
static Chip participantStatus(String status) {
late final Color bg;
late final IconData icon;
// status는 한국어 라벨 또는 API 값(pending/confirmed/canceled/attended)로 들어올 수 있음
final s = status.toLowerCase();
String label;
if (s == 'pending' || s == '참가예정' || s == '등록됨' || s == '대기') {
bg = neutralBg;
icon = Icons.how_to_reg;
label = '예정';
} else if (s == 'confirmed' || s == '참가확정' || s == '확인됨') {
bg = successBg;
icon = Icons.check_circle;
label = '확정';
} else if (s == 'canceled' || s == '취소' || s == '취소됨') {
bg = dangerBg;
icon = Icons.cancel;
label = '취소';
} else if (s == 'attended' || s == '참석' || s == '참석함') {
bg = primaryBg;
icon = Icons.event_available;
label = '참석';
} else {
bg = neutralBg;
icon = Icons.help_outline;
label = status;
}
return _chip(label, bg, icon);
}
// 회원 상태 컬러/아이콘 매핑 (라벨 기준: 활성/비활성/정지/삭제됨)
static Chip memberStatus(String label) {
late final Color bg;
late final IconData icon;
switch (label) {
case '활성':
bg = successBg;
icon = Icons.check_circle;
break;
case '비활성':
bg = neutralBg;
icon = Icons.pause_circle_filled;
break;
case '정지':
bg = dangerBg;
icon = Icons.block;
break;
case '삭제됨':
bg = Colors.grey.shade300;
icon = Icons.delete_forever;
break;
default:
bg = neutralBg;
icon = Icons.person;
}
return _chip(label, bg, icon);
}
// 결제 상태 컬러/아이콘 매핑
static Chip payment(bool isPaid) {
return _chip(
isPaid ? '납부' : '미납',
isPaid ? successBg : dangerBg,
isPaid ? Icons.check_circle : Icons.cancel,
);
}
// 이벤트 상태 컬러/아이콘 매핑
static Chip eventStatus(String status) {
late final Color bg;
late final IconData icon;
// DB 원시값(영문) 또는 한국어 라벨을 모두 지원하도록 정규화
final s = status.toLowerCase().trim();
String label = status; // 표시용 라벨 (한국어)
if (s == 'active' || s == 'enabled' || s == '활성') {
label = '활성';
bg = successBg;
icon = Icons.check_circle;
} else if (s == 'pending' ||
s == 'draft' ||
s == 'scheduled' ||
s == 'waiting' ||
s == '대기') {
label = '대기';
bg = Colors.amber.shade100;
icon = Icons.hourglass_top;
} else if (s == 'canceled' || s == 'cancelled' || s == '취소') {
label = '취소';
bg = dangerBg;
icon = Icons.cancel;
} else if (s == 'completed' ||
s == 'done' ||
s == 'finished' ||
s == '완료') {
label = '완료';
bg = Colors.grey.shade300;
icon = Icons.done_all;
} else {
// 알 수 없는 상태는 중립 처리
label = status;
bg = neutralBg;
icon = Icons.event;
}
return _chip(label, bg, icon);
}
static Chip _chip(String text, Color bg, IconData icon) {
// 배경 대비에 따라 텍스트/아이콘 색상을 자동 설정 (흰/검정 중 더 높은 대비 선택)
double lumBg = bg.computeLuminance();
// 대비비 계산 함수 (WCAG 근사)
double contrast(double lum1, double lum2) {
final double l1 = lum1 > lum2 ? lum1 : lum2;
final double l2 = lum1 > lum2 ? lum2 : lum1;
return (l1 + 0.05) / (l2 + 0.05);
}
final double contrastWithWhite = contrast(
lumBg,
Colors.white.computeLuminance(),
);
final double contrastWithBlack = contrast(
lumBg,
Colors.black.computeLuminance(),
);
final Color fg = contrastWithWhite >= contrastWithBlack
? Colors.white
: Colors.black;
return Chip(
avatar: Icon(icon, size: 14, color: fg),
label: Text(text, style: TextStyle(color: fg, fontSize: 11)),
backgroundColor: bg,
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 0),
labelPadding: const EdgeInsets.only(left: -2, right: 5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity(horizontal: -3, vertical: -3),
);
}
}